summaryrefslogtreecommitdiff
path: root/sleekxmpp/roster
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2014-07-17 14:19:04 +0200
committerFlorent Le Coz <louiz@louiz.org>2014-07-17 14:19:04 +0200
commit5ab77c745270d7d5c016c1dc7ef2a82533a4b16e (patch)
tree259377cc666f8b9c7954fc4e7b8f7a912bcfe101 /sleekxmpp/roster
parente5582694c07236e6830c20361840360a1dde37f3 (diff)
downloadslixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.gz
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.bz2
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.tar.xz
slixmpp-5ab77c745270d7d5c016c1dc7ef2a82533a4b16e.zip
Rename to slixmpp
Diffstat (limited to 'sleekxmpp/roster')
-rw-r--r--sleekxmpp/roster/__init__.py11
-rw-r--r--sleekxmpp/roster/item.py497
-rw-r--r--sleekxmpp/roster/multi.py224
-rw-r--r--sleekxmpp/roster/single.py337
4 files changed, 0 insertions, 1069 deletions
diff --git a/sleekxmpp/roster/__init__.py b/sleekxmpp/roster/__init__.py
deleted file mode 100644
index 18b380c9..00000000
--- a/sleekxmpp/roster/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.roster.item import RosterItem
-from sleekxmpp.roster.single import RosterNode
-from sleekxmpp.roster.multi import Roster
diff --git a/sleekxmpp/roster/item.py b/sleekxmpp/roster/item.py
deleted file mode 100644
index ae194e0a..00000000
--- a/sleekxmpp/roster/item.py
+++ /dev/null
@@ -1,497 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-
-class RosterItem(object):
-
- """
- A RosterItem is a single entry in a roster node, and tracks
- the subscription state and user annotations of a single JID.
-
- Roster items may use an external datastore to persist roster data
- across sessions. Client applications will not need to use this
- functionality, but is intended for components that do not have their
- roster persisted automatically by the XMPP server.
-
- Roster items provide many methods for handling incoming presence
- stanzas that ensure that response stanzas are sent according to
- RFC 3921.
-
- The external datastore is accessed through a provided interface
- object which is stored in self.db. The interface object MUST
- provide two methods: load and save, both of which are responsible
- for working with a single roster item. A private dictionary,
- self._db_state, is used to store any metadata needed by the
- interface, such as the row ID of a roster item, etc.
-
- Interface for self.db.load:
- load(owner_jid, jid, db_state):
- owner_jid -- The JID that owns the roster.
- jid -- The JID of the roster item.
- db_state -- A dictionary containing any data saved
- by the interface object after a save()
- call. Will typically have the equivalent
- of a 'row_id' value.
-
- Interface for self.db.save:
- save(owner_jid, jid, item_state, db_state):
- owner_jid -- The JID that owns the roster.
- jid -- The JID of the roster item.
- item_state -- A dictionary containing the fields:
- 'from', 'to', 'pending_in', 'pending_out',
- 'whitelisted', 'subscription', 'name',
- and 'groups'.
- db_state -- A dictionary provided for persisting
- datastore specific information. Typically,
- a value equivalent to 'row_id' will be
- stored here.
-
- State Fields:
- from -- Indicates if a subscription of type 'from'
- has been authorized.
- to -- Indicates if a subscription of type 'to' has
- been authorized.
- pending_in -- Indicates if a subscription request has been
- received from this JID and it has not been
- authorized yet.
- pending_out -- Indicates if a subscription request has been sent
- to this JID and it has not been accepted yet.
- subscription -- Returns one of: 'to', 'from', 'both', or 'none'
- based on the states of from, to, pending_in,
- and pending_out. Assignment to this value does
- not affect the states of the other values.
- whitelisted -- Indicates if a subscription request from this
- JID should be automatically accepted.
- name -- A user supplied alias for the JID.
- groups -- A list of group names for the JID.
-
- Attributes:
- xmpp -- The main SleekXMPP instance.
- owner -- The JID that owns the roster.
- jid -- The JID for the roster item.
- db -- Optional datastore interface object.
- last_status -- The last presence sent to this JID.
- resources -- A dictionary of online resources for this JID.
- Will contain the fields 'show', 'status',
- and 'priority'.
-
- Methods:
- load -- Retrieve the roster item from an
- external datastore, if one was provided.
- save -- Save the roster item to an external
- datastore, if one was provided.
- remove -- Remove a subscription to the JID and revoke
- its whitelisted status.
- subscribe -- Subscribe to the JID.
- authorize -- Accept a subscription from the JID.
- unauthorize -- Deny a subscription from the JID.
- unsubscribe -- Unsubscribe from the JID.
- send_presence -- Send a directed presence to the JID.
- send_last_presence -- Resend the last sent presence.
- handle_available -- Update the JID's resource information.
- handle_unavailable -- Update the JID's resource information.
- handle_subscribe -- Handle a subscription request.
- handle_subscribed -- Handle a notice that a subscription request
- was authorized by the JID.
- handle_unsubscribe -- Handle an unsubscribe request.
- handle_unsubscribed -- Handle a notice that a subscription was
- removed by the JID.
- handle_probe -- Handle a presence probe query.
- """
-
- def __init__(self, xmpp, jid, owner=None,
- state=None, db=None, roster=None):
- """
- Create a new roster item.
-
- Arguments:
- xmpp -- The main SleekXMPP instance.
- jid -- The item's JID.
- owner -- The roster owner's JID. Defaults
- so self.xmpp.boundjid.bare.
- state -- A dictionary of initial state values.
- db -- An optional interface to an external datastore.
- roster -- The roster object containing this entry.
- """
- self.xmpp = xmpp
- self.jid = jid
- self.owner = owner or self.xmpp.boundjid.bare
- self.last_status = None
- self.resources = {}
- self.roster = roster
- self.db = db
- self._state = state or {
- 'from': False,
- 'to': False,
- 'pending_in': False,
- 'pending_out': False,
- 'whitelisted': False,
- 'subscription': 'none',
- 'name': '',
- 'groups': []}
-
- self._db_state = {}
- self.load()
-
- def set_backend(self, db=None, save=True):
- """
- Set the datastore interface object for the roster item.
-
- Arguments:
- db -- The new datastore interface.
- save -- If True, save the existing state to the new
- backend datastore. Defaults to True.
- """
- self.db = db
- if save:
- self.save()
- self.load()
-
- def load(self):
- """
- Load the item's state information from an external datastore,
- if one has been provided.
- """
- if self.db:
- item = self.db.load(self.owner, self.jid,
- self._db_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, remove=False):
- """
- Save the item's state information to an external datastore,
- if one has been provided.
-
- Arguments:
- remove -- If True, expunge the item from the datastore.
- """
- self['subscription'] = self._subscription()
- if remove:
- self._state['removed'] = True
- if self.db:
- self.db.save(self.owner, self.jid,
- self._state, self._db_state)
-
- # Finally, remove the in-memory copy if needed.
- if remove:
- del self.xmpp.roster[self.owner][self.jid]
-
- def __getitem__(self, key):
- """Return a state field's value."""
- if key in self._state:
- if key == 'subscription':
- return self._subscription()
- return self._state[key]
- else:
- raise KeyError
-
- def __setitem__(self, key, value):
- """
- Set the value of a state field.
-
- For boolean states, the values True, 'true', '1', 'on',
- and 'yes' are accepted as True; all others are False.
-
- Arguments:
- key -- The state field to modify.
- value -- The new value of the state field.
- """
- 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):
- """Return the proper subscription type based on current state."""
- 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 a JID's whitelisted status and unsubscribe if a
- subscription exists.
- """
- 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):
- """Send a subscription request to the JID."""
- 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):
- """Authorize a received subscription request from the JID."""
- self['from'] = True
- self['pending_in'] = False
- self.save()
- self._subscribed()
- self.send_last_presence()
-
- def unauthorize(self):
- """Deny a received subscription request from the JID."""
- 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):
- """Handle acknowledging a subscription."""
- 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):
- """Unsubscribe from the JID."""
- 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):
- """Handle acknowledging an unsubscribe request."""
- 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, **kwargs):
- """
- Create, initialize, and send a Presence stanza.
-
- If no recipient is specified, send the presence immediately.
- Otherwise, forward the send request to the recipient's roster
- entry for processing.
-
- Arguments:
- pshow -- The presence's show value.
- pstatus -- The presence's status message.
- ppriority -- This connections' priority.
- pto -- The recipient of a directed presence.
- pfrom -- The sender of a directed presence, which should
- be the owner JID plus resource.
- ptype -- The type of presence, such as 'subscribe'.
- pnick -- Optional nickname of the presence's sender.
- """
- if self.xmpp.is_component and not kwargs.get('pfrom', ''):
- kwargs['pfrom'] = self.owner
- if not kwargs.get('pto', ''):
- kwargs['pto'] = self.jid
- self.xmpp.send_presence(**kwargs)
-
- def send_last_presence(self):
- if self.last_status is None:
- pres = self.roster.last_status
- if pres is None:
- self.send_presence()
- else:
- pres['to'] = self.jid
- if self.xmpp.is_component:
- pres['from'] = self.owner
- else:
- del pres['from']
- pres.send()
- else:
- self.last_status.send()
-
- def handle_available(self, presence):
- resource = presence['from'].resource
- data = {'status': presence['status'],
- 'show': presence['show'],
- 'priority': presence['priority']}
- got_online = not self.resources
- if resource not in self.resources:
- self.resources[resource] = {}
- old_status = self.resources[resource].get('status', '')
- old_show = self.resources[resource].get('show', None)
- self.resources[resource].update(data)
- if got_online:
- self.xmpp.event('got_online', presence)
- if old_show != presence['show'] or old_status != presence['status']:
- self.xmpp.event('changed_status', presence)
-
- def handle_unavailable(self, presence):
- resource = presence['from'].resource
- if not self.resources:
- return
- if resource in self.resources:
- del self.resources[resource]
- self.xmpp.event('changed_status', presence)
- 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 self.xmpp.is_component:
- 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()
- else:
- #server shouldn't send an invalid subscription request
- self.xmpp.event('roster_subscription_request', presence)
-
- 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 self.xmpp.is_component:
- if not self['to'] and self['pending_out']:
- self['pending_out'] = False
- self['to'] = True
- self.xmpp.event('roster_subscription_authorized', presence)
- self.save()
- else:
- self.xmpp.event('roster_subscription_authorized', presence)
-
- 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 self.xmpp.is_component:
- 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()
- else:
- self.xmpp.event('roster_subscription_remove', presence)
-
- 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 self.xmpp.is_component:
- 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()
- else:
- self.xmpp.event('roster_subscription_removed', presence)
-
- def handle_probe(self, presence):
- if self['from']:
- self.send_last_presence()
- if self['pending_out']:
- self.subscribe()
- if not self['from']:
- self._unsubscribed()
-
- def reset(self):
- """
- Forgot current resource presence information as part of
- a roster reset request.
- """
- self.resources = {}
-
- def __repr__(self):
- return repr(self._state)
diff --git a/sleekxmpp/roster/multi.py b/sleekxmpp/roster/multi.py
deleted file mode 100644
index 5d070ec8..00000000
--- a/sleekxmpp/roster/multi.py
+++ /dev/null
@@ -1,224 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.stanza import Presence
-from sleekxmpp.xmlstream import JID
-from sleekxmpp.roster import RosterNode
-
-
-class Roster(object):
-
- """
- SleekXMPP's roster manager.
-
- The roster is divided into "nodes", where each node is responsible
- for a single JID. While the distinction is not strictly necessary
- for client connections, it is a necessity for components that use
- multiple JIDs.
-
- Rosters may be stored and persisted in an external datastore. An
- interface object to the datastore that loads and saves roster items may
- be provided. See the documentation for the RosterItem class for the
- methods that the datastore interface object must provide.
-
- Attributes:
- xmpp -- The main SleekXMPP instance.
- db -- Optional interface object to an external datastore.
- auto_authorize -- Default auto_authorize value for new roster nodes.
- Defaults to True.
- auto_subscribe -- Default auto_subscribe value for new roster nodes.
- Defaults to True.
-
- Methods:
- add -- Create a new roster node for a JID.
- send_presence -- Shortcut for sending a presence stanza.
- """
-
- def __init__(self, xmpp, db=None):
- """
- Create a new roster.
-
- Arguments:
- xmpp -- The main SleekXMPP instance.
- db -- Optional interface object to a datastore.
- """
- self.xmpp = xmpp
- self.db = db
- self._auto_authorize = True
- self._auto_subscribe = True
- self._rosters = {}
-
- if self.db:
- for node in self.db.entries(None, {}):
- self.add(node)
-
- self.xmpp.add_filter('out', self._save_last_status)
-
- def _save_last_status(self, stanza):
-
- if isinstance(stanza, Presence):
- sfrom = stanza['from'].full
- sto = stanza['to'].full
-
- if not sfrom:
- sfrom = self.xmpp.boundjid
-
- if stanza['type'] in stanza.showtypes or \
- stanza['type'] in ('available', 'unavailable'):
- if sto:
- self[sfrom][sto].last_status = stanza
- else:
- self[sfrom].last_status = stanza
- with self[sfrom]._last_status_lock:
- for jid in self[sfrom]:
- self[sfrom][jid].last_status = None
-
- if not self.xmpp.sentpresence:
- self.xmpp.event('sent_presence')
- self.xmpp.sentpresence = True
-
- return stanza
-
- def __getitem__(self, key):
- """
- Return the roster node for a JID.
-
- A new roster node will be created if one
- does not already exist.
-
- Arguments:
- key -- Return the roster for this JID.
- """
- if key is None:
- key = self.xmpp.boundjid
- if not isinstance(key, JID):
- key = JID(key)
- key = key.bare
-
- if key not in self._rosters:
- self.add(key)
- self._rosters[key].auto_authorize = self.auto_authorize
- self._rosters[key].auto_subscribe = self.auto_subscribe
- return self._rosters[key]
-
- def keys(self):
- """Return the JIDs managed by the roster."""
- return self._rosters.keys()
-
- def __iter__(self):
- """Iterate over the roster nodes."""
- return self._rosters.__iter__()
-
- def add(self, node):
- """
- Add a new roster node for the given JID.
-
- Arguments:
- node -- The JID for the new roster node.
- """
- if not isinstance(node, JID):
- node = JID(node)
-
- node = node.bare
- if node not in self._rosters:
- self._rosters[node] = RosterNode(self.xmpp, node, self.db)
-
- def set_backend(self, db=None, save=True):
- """
- Set the datastore interface object for the roster.
-
- Arguments:
- db -- The new datastore interface.
- save -- If True, save the existing state to the new
- backend datastore. Defaults to True.
- """
- self.db = db
- existing_entries = set(self._rosters)
- new_entries = set(self.db.entries(None, {}))
-
- for node in existing_entries:
- self._rosters[node].set_backend(db, save)
- for node in new_entries - existing_entries:
- self.add(node)
-
- def reset(self):
- """
- Reset the state of the roster to forget any current
- presence information. Useful after a disconnection occurs.
- """
- for node in self:
- self[node].reset()
-
- def send_presence(self, **kwargs):
- """
- Create, initialize, and send a Presence stanza.
-
- If no recipient is specified, send the presence immediately.
- Otherwise, forward the send request to the recipient's roster
- entry for processing.
-
- Arguments:
- pshow -- The presence's show value.
- pstatus -- The presence's status message.
- ppriority -- This connections' priority.
- pto -- The recipient of a directed presence.
- pfrom -- The sender of a directed presence, which should
- be the owner JID plus resource.
- ptype -- The type of presence, such as 'subscribe'.
- pnick -- Optional nickname of the presence's sender.
- """
- if self.xmpp.is_component and not kwargs.get('pfrom', ''):
- kwargs['pfrom'] = self.jid
- self.xmpp.send_presence(**kwargs)
-
- @property
- def auto_authorize(self):
- """
- Auto accept or deny subscription requests.
-
- If True, auto accept subscription requests.
- If False, auto deny subscription requests.
- If None, don't automatically respond.
- """
- return self._auto_authorize
-
- @auto_authorize.setter
- def auto_authorize(self, value):
- """
- Auto accept or deny subscription requests.
-
- If True, auto accept subscription requests.
- If False, auto deny subscription requests.
- If None, don't automatically respond.
- """
- self._auto_authorize = value
- for node in self._rosters:
- self._rosters[node].auto_authorize = value
-
- @property
- def auto_subscribe(self):
- """
- Auto send requests for mutual subscriptions.
-
- If True, auto send mutual subscription requests.
- """
- return self._auto_subscribe
-
- @auto_subscribe.setter
- def auto_subscribe(self, value):
- """
- Auto send requests for mutual subscriptions.
-
- If True, auto send mutual subscription requests.
- """
- self._auto_subscribe = value
- for node in self._rosters:
- self._rosters[node].auto_subscribe = value
-
- def __repr__(self):
- return repr(self._rosters)
diff --git a/sleekxmpp/roster/single.py b/sleekxmpp/roster/single.py
deleted file mode 100644
index f080ae8a..00000000
--- a/sleekxmpp/roster/single.py
+++ /dev/null
@@ -1,337 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-import threading
-
-from sleekxmpp.xmlstream import JID
-from sleekxmpp.roster import RosterItem
-
-
-class RosterNode(object):
-
- """
- A roster node is a roster for a single JID.
-
- Attributes:
- xmpp -- The main SleekXMPP instance.
- jid -- The JID that owns the roster node.
- db -- Optional interface to an external datastore.
- auto_authorize -- Determines how authorizations are handled:
- True -- Accept all subscriptions.
- False -- Reject all subscriptions.
- None -- Subscriptions must be
- manually authorized.
- Defaults to True.
- auto_subscribe -- Determines if bi-directional subscriptions
- are created after automatically authrorizing
- a subscription request.
- Defaults to True
- last_status -- The last sent presence status that was broadcast
- to all contact JIDs.
-
- Methods:
- add -- Add a JID to the roster.
- update -- Update a JID's subscription information.
- subscribe -- Subscribe to a JID.
- unsubscribe -- Unsubscribe from a JID.
- remove -- Remove a JID from the roster.
- presence -- Return presence information for a JID's resources.
- send_presence -- Shortcut for sending a presence stanza.
- """
-
- def __init__(self, xmpp, jid, db=None):
- """
- Create a roster node for a JID.
-
- Arguments:
- xmpp -- The main SleekXMPP instance.
- jid -- The JID that owns the roster.
- db -- Optional interface to an external datastore.
- """
- self.xmpp = xmpp
- self.jid = jid
- self.db = db
- self.auto_authorize = True
- self.auto_subscribe = True
- self.last_status = None
- self._version = ''
- self._jids = {}
- self._last_status_lock = threading.Lock()
-
- if self.db:
- if hasattr(self.db, 'version'):
- self._version = self.db.version(self.jid)
- for jid in self.db.entries(self.jid):
- self.add(jid)
-
- @property
- def version(self):
- """Retrieve the roster's version ID."""
- if self.db and hasattr(self.db, 'version'):
- self._version = self.db.version(self.jid)
- return self._version
-
- @version.setter
- def version(self, version):
- """Set the roster's version ID."""
- self._version = version
- if self.db and hasattr(self.db, 'set_version'):
- self.db.set_version(self.jid, version)
-
- def __getitem__(self, key):
- """
- Return the roster item for a subscribed JID.
-
- A new item entry will be created if one does not already exist.
- """
- if key is None:
- key = JID('')
- if not isinstance(key, JID):
- key = JID(key)
- key = key.bare
- if key not in self._jids:
- self.add(key, save=True)
- return self._jids[key]
-
- def __delitem__(self, key):
- """
- Remove a roster item from the local storage.
-
- To remove an item from the server, use the remove() method.
- """
- if key is None:
- key = JID('')
- if not isinstance(key, JID):
- key = JID(key)
- key = key.bare
- if key in self._jids:
- del self._jids[key]
-
- def __len__(self):
- """Return the number of JIDs referenced by the roster."""
- return len(self._jids)
-
- def keys(self):
- """Return a list of all subscribed JIDs."""
- return self._jids.keys()
-
- def has_jid(self, jid):
- """Returns whether the roster has a JID."""
- return jid in self._jids
-
- def groups(self):
- """Return a dictionary mapping group names to JIDs."""
- result = {}
- for jid in self._jids:
- groups = self._jids[jid]['groups']
- if not groups:
- if '' not in result:
- result[''] = []
- result[''].append(jid)
- for group in groups:
- if group not in result:
- result[group] = []
- result[group].append(jid)
- return result
-
- def __iter__(self):
- """Iterate over the roster items."""
- return self._jids.__iter__()
-
- def set_backend(self, db=None, save=True):
- """
- Set the datastore interface object for the roster node.
-
- Arguments:
- db -- The new datastore interface.
- save -- If True, save the existing state to the new
- backend datastore. Defaults to True.
- """
- self.db = db
- existing_entries = set(self._jids)
- new_entries = set(self.db.entries(self.jid, {}))
-
- for jid in existing_entries:
- self._jids[jid].set_backend(db, save)
- for jid in new_entries - existing_entries:
- self.add(jid)
-
- def add(self, jid, name='', groups=None, afrom=False, ato=False,
- pending_in=False, pending_out=False, whitelisted=False,
- save=False):
- """
- Add a new roster item entry.
-
- Arguments:
- jid -- The JID for the roster item.
- name -- An alias for the JID.
- groups -- A list of group names.
- afrom -- Indicates if the JID has a subscription state
- of 'from'. Defaults to False.
- ato -- Indicates if the JID has a subscription state
- of 'to'. Defaults to False.
- pending_in -- Indicates if the JID has sent a subscription
- request to this connection's JID.
- Defaults to False.
- pending_out -- Indicates if a subscription request has been sent
- to this JID.
- Defaults to False.
- whitelisted -- Indicates if a subscription request from this JID
- should be automatically authorized.
- Defaults to False.
- save -- Indicates if the item should be persisted
- immediately to an external datastore,
- if one is used.
- Defaults to False.
- """
- if isinstance(jid, JID):
- key = jid.bare
- else:
- key = jid
-
- state = {'name': name,
- 'groups': groups or [],
- 'from': afrom,
- 'to': ato,
- 'pending_in': pending_in,
- 'pending_out': pending_out,
- 'whitelisted': whitelisted,
- 'subscription': 'none'}
- self._jids[key] = RosterItem(self.xmpp, jid, self.jid,
- state=state, db=self.db,
- roster=self)
- if save:
- self._jids[key].save()
-
- def subscribe(self, jid):
- """
- Subscribe to the given JID.
-
- Arguments:
- jid -- The JID to subscribe to.
- """
- self[jid].subscribe()
-
- def unsubscribe(self, jid):
- """
- Unsubscribe from the given JID.
-
- Arguments:
- jid -- The JID to unsubscribe from.
- """
- self[jid].unsubscribe()
-
- def remove(self, jid):
- """
- Remove a JID from the roster.
-
- Arguments:
- jid -- The JID to remove.
- """
- self[jid].remove()
- if not self.xmpp.is_component:
- return self.update(jid, subscription='remove')
-
- def update(self, jid, name=None, subscription=None, groups=[],
- block=True, timeout=None, callback=None):
- """
- Update a JID's subscription information.
-
- Arguments:
- jid -- The JID to update.
- name -- Optional alias for the JID.
- subscription -- The subscription state. May be one of: 'to',
- 'from', 'both', 'none', or 'remove'.
- groups -- A list of group names.
- block -- Specify if the roster request will block
- until a response is received, or a timeout
- occurs. Defaults to True.
- timeout -- The length of time (in seconds) to wait
- for a response before continuing if blocking
- is used. Defaults to self.response_timeout.
- callback -- Optional reference to a stream handler function.
- Will be executed when the roster is received.
- Implies block=False.
- """
- self[jid]['name'] = name
- self[jid]['groups'] = groups
- self[jid].save()
-
- if not self.xmpp.is_component:
- iq = self.xmpp.Iq()
- iq['type'] = 'set'
- iq['roster']['items'] = {jid: {'name': name,
- 'subscription': subscription,
- 'groups': groups}}
-
- return iq.send(block, timeout, callback)
-
- def presence(self, jid, resource=None):
- """
- Retrieve the presence information of a JID.
-
- May return either all online resources' status, or
- a single resource's status.
-
- Arguments:
- jid -- The JID to lookup.
- resource -- Optional resource for returning
- only the status of a single connection.
- """
- if resource is None:
- return self[jid].resources
-
- default_presence = {'status': '',
- 'priority': 0,
- 'show': ''}
- return self[jid].resources.get(resource,
- default_presence)
-
- def reset(self):
- """
- Reset the state of the roster to forget any current
- presence information. Useful after a disconnection occurs.
- """
- for jid in self:
- self[jid].reset()
-
- def send_presence(self, **kwargs):
- """
- Create, initialize, and send a Presence stanza.
-
- If no recipient is specified, send the presence immediately.
- Otherwise, forward the send request to the recipient's roster
- entry for processing.
-
- Arguments:
- pshow -- The presence's show value.
- pstatus -- The presence's status message.
- ppriority -- This connections' priority.
- pto -- The recipient of a directed presence.
- pfrom -- The sender of a directed presence, which should
- be the owner JID plus resource.
- ptype -- The type of presence, such as 'subscribe'.
- pnick -- Optional nickname of the presence's sender.
- """
- if self.xmpp.is_component and not kwargs.get('pfrom', ''):
- kwargs['pfrom'] = self.jid
- self.xmpp.send_presence(**kwargs)
-
- def send_last_presence(self):
- if self.last_status is None:
- self.send_presence()
- else:
- pres = self.last_status
- if self.xmpp.is_component:
- pres['from'] = self.jid
- else:
- del pres['from']
- pres.send()
-
- def __repr__(self):
- return repr(self._jids)