import logging class MultiRoster(object): def __init__(self, xmpp, datastore=None): self.xmpp = xmpp self.datastore = datastore self._rosters = {} def __getitem__(self, key): if key not in self._rosters: self.add(key) return self._rosters[key] def keys(self): return self._rosters.keys() def __iter__(self): return self._rosters.__iter__() def add(self, node): if node not in self._rosters: self._rosters[node] = Roster(self.xmpp, node, self.datastore) class Roster(object): def __init__(self, xmpp, jid, datastore=None): self.xmpp = xmpp self.jid = jid self.datastore = datastore self.auto_authorize = True self.auto_subscribe = True self._jids = {} def __getitem__(self, key): if key not in self._jids: self.add(key, save=True) return self._jids[key] def keys(self): return self._jids.keys() def __iter__(self): return self._jids.__iter__() def add(self, jid, name='', groups=None, afrom=False, ato=False, pending_in=False, pending_out=False, whitelisted=False, save=False): state = {'name': name, 'groups': groups or [], 'from': afrom, 'to': ato, 'pending_in': pending_in, 'pending_out': pending_out, 'whitelisted': whitelisted, 'subscription': 'none'} self._jids[jid] = RosterItem(self.xmpp, jid, self.jid, state=state, datastore=self.datastore) if save: self._jids[jid].save() def subscribe(self, jid): self._jids[jid].subscribe() def unsubscribe(self, jid): self._jids[jid].unsubscribe() def remove(self, jid): self._jids[jid].remove() if not self.xmpp.is_component: self.update(jid, subscription='remove') def update(self, jid, name=None, subscription=None, groups=[]): self._jids[jid]['name'] = name self._jids[jid]['groups'] = group self._jids[jid].save() if not self.xmpp.is_component: iq = self.Iq() iq['type'] = 'set' iq['roster']['items'] = {jid: {'name': name, 'subscription': subscription, 'groups': groups}} response = iq.send() return response and response['type'] == 'result' def presence(self, jid, resource=None): if resource is None: return self._jids[jid].resources default_presence = {'status': '', 'priority': 0, 'show': ''} return self._jids[jid].resources.get(resource, default_presence) class RosterItem(object): def __init__(self, xmpp, jid, owner=None, state=None, datastore=None): self.xmpp = xmpp self.jid = jid self.owner = owner or self.xmpp.jid self.last_status = None self.resources = {} self.datastore = datastore self._state = state or { 'from': False, 'to': False, 'pending_in': False, 'pending_out': False, 'whitelisted': False, 'subscription': 'none', 'name': '', 'groups': []} self._datastore_state = {} self.load() def load(self): if self.datastore: item = self.datastore.load(self.owner, self.jid, self._datastore_state) if item: self['name'] = item['name'] self['groups'] = item['groups'] self['from'] = item['from'] self['to'] = item['to'] self['whitelisted'] = item['whitelisted'] self['pending_out'] = item['pending_out'] self['pending_in'] = item['pending_in'] self['subscription'] = self._subscription() return self._state return None def save(self): if self.datastore: self.datastore.save(self.owner, self.jid, self._state, self._datastore_state) def __getitem__(self, key): if key in self._state: if key == 'subscription': return self._subscription() return self._state[key] else: raise KeyError def __setitem__(self, key, value): print "%s: %s" % (key, value) if key in self._state: if key in ['name', 'subscription', 'groups']: self._state[key] = value else: value = str(value).lower() self._state[key] = value in ('true', '1', 'on', 'yes') else: raise KeyError def _subscription(self): if self['to'] and self['from']: return 'both' elif self['from']: return 'from' elif self['to']: return 'to' else: return 'none' def remove(self): "Remove the jids subscription, inform it if it is subscribed, and unwhitelist it" if self['to']: p = self.xmpp.Presence() p['to'] = self.jid p['type'] = ['unsubscribe'] if self.xmpp.is_component: p['from'] = self.owner p.send() self['to'] = False self['whitelisted'] = False self.save() def subscribe(self): p = self.xmpp.Presence() p['to'] = self.jid p['type'] = 'subscribe' if self.xmpp.is_component: p['from'] = self.owner self['pending_out'] = True self.save() p.send() def authorize(self): self['from'] = True self['pending_in'] = False self.save() self._subscribed() self.send_last_presence() def unauthorize(self): self['from'] = False self['pending_in'] = False self.save() self._unsubscribed() p = self.xmpp.Presence() p['to'] = self.jid p['type'] = 'unavailable' if self.xmpp.is_component: p['from'] = self.owner p.send() def _subscribed(self): p = self.xmpp.Presence() p['to'] = self.jid p['type'] = 'subscribed' if self.xmpp.is_component: p['from'] = self.owner p.send() def unsubscribe(self): p = self.xmpp.Presence() p['to'] = self.jid p['type'] = 'unsubscribe' if self.xmpp.is_component: p['from'] = self.owner self.save() p.send() def _unsubscribed(self): p = self.xmpp.Presence() p['to'] = self.jid p['type'] = 'unsubscribed' if self.xmpp.is_component: p['from'] = self.owner p.send() def send_presence(self, ptype='available', status=None): p = self.xmpp.Presence() p['to'] = self.jid p['type'] = ptype p['status'] = status if self.xmpp.is_component: p['from'] = self.owner self.last_status = p p.send() def send_last_presence(self): if self.last_status is None: self.send_presence() else: self.last_status.send() def handle_available(self, presence): resource = presence['from'].resource data = {'status': presence['status'], 'show': presence['show'], 'priority': presence['priority']} if not self.resources: self.xmpp.event('got_online', presence) if resource not in self.resources: self.resources[resource] = {} self.resources[resource].update(data) def handle_unavailable(self, presence): resource = presence['from'].resource if not self.resources: return if resource in self.resources: del self.resources[resource] if not self.resources: self.xmpp.event('got_offline', presence) def handle_subscribe(self, presence): """ +------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | yes | "None + Pending In" | | "None + Pending Out" | yes | "None + Pending Out/In" | | "None + Pending In" | no | no state change | | "None + Pending Out/In" | no | no state change | | "To" | yes | "To + Pending In" | | "To + Pending In" | no | no state change | | "From" | no * | no state change | | "From + Pending Out" | no * | no state change | | "Both" | no * | no state change | +------------------------------------------------------------------+ """ if not self['from'] and not self['pending_in']: self['pending_in'] = True self.xmpp.event('roster_subscription_request', presence) elif self['from']: self._subscribed() self.save() def handle_subscribed(self, presence): """ +------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | no | no state change | | "None + Pending Out" | yes | "To" | | "None + Pending In" | no | no state change | | "None + Pending Out/In" | yes | "To + Pending In" | | "To" | no | no state change | | "To + Pending In" | no | no state change | | "From" | no | no state change | | "From + Pending Out" | yes | "Both" | | "Both" | no | no state change | +------------------------------------------------------------------+ """ if not self['to'] and self['pending_out']: self['pending_out'] = False self['to'] = True self.xmpp.event('roster_subscription_authorized', presence) self.save() def handle_unsubscribe(self, presence): """ +------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | no | no state change | | "None + Pending Out" | no | no state change | | "None + Pending In" | yes * | "None" | | "None + Pending Out/In" | yes * | "None + Pending Out" | | "To" | no | no state change | | "To + Pending In" | yes * | "To" | | "From" | yes * | "None" | | "From + Pending Out" | yes * | "None + Pending Out | | "Both" | yes * | "To" | +------------------------------------------------------------------+ """ if not self['from'] and self['pending_in']: self['pending_in'] = False self._unsubscribed() elif self['from']: self['from'] = False self._unsubscribed() self.xmpp.event('roster_subscription_remove', presence) self.save() def handle_unsubscribed(self, presence): """ +------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | no | no state change | | "None + Pending Out" | yes | "None" | | "None + Pending In" | no | no state change | | "None + Pending Out/In" | yes | "None + Pending In" | | "To" | yes | "None" | | "To + Pending In" | yes | "None + Pending In" | | "From" | no | no state change | | "From + Pending Out" | yes | "From" | | "Both" | yes | "From" | +------------------------------------------------------------------ """ if not self['to'] and self['pending_out']: self['pending_out'] = False elif self['to'] and not self['pending_out']: self['to'] = False self.xmpp.event('roster_subscription_removed', presence) self.save() def handle_probe(self, presence): if self['to']: self.send_last_presence() if self['pending_out']: self.subscribe() if not self['to']: self._unsubscribed()