Bump to 1.3.1
[slixmpp.git] / sleekxmpp / roster / multi.py
blob5d070ec8aa8646827a7c2e578702c564af189dcb
1 """
2 SleekXMPP: The Sleek XMPP Library
3 Copyright (C) 2010 Nathanael C. Fritz
4 This file is part of SleekXMPP.
6 See the file LICENSE for copying permission.
7 """
9 from sleekxmpp.stanza import Presence
10 from sleekxmpp.xmlstream import JID
11 from sleekxmpp.roster import RosterNode
14 class Roster(object):
16 """
17 SleekXMPP's roster manager.
19 The roster is divided into "nodes", where each node is responsible
20 for a single JID. While the distinction is not strictly necessary
21 for client connections, it is a necessity for components that use
22 multiple JIDs.
24 Rosters may be stored and persisted in an external datastore. An
25 interface object to the datastore that loads and saves roster items may
26 be provided. See the documentation for the RosterItem class for the
27 methods that the datastore interface object must provide.
29 Attributes:
30 xmpp -- The main SleekXMPP instance.
31 db -- Optional interface object to an external datastore.
32 auto_authorize -- Default auto_authorize value for new roster nodes.
33 Defaults to True.
34 auto_subscribe -- Default auto_subscribe value for new roster nodes.
35 Defaults to True.
37 Methods:
38 add -- Create a new roster node for a JID.
39 send_presence -- Shortcut for sending a presence stanza.
40 """
42 def __init__(self, xmpp, db=None):
43 """
44 Create a new roster.
46 Arguments:
47 xmpp -- The main SleekXMPP instance.
48 db -- Optional interface object to a datastore.
49 """
50 self.xmpp = xmpp
51 self.db = db
52 self._auto_authorize = True
53 self._auto_subscribe = True
54 self._rosters = {}
56 if self.db:
57 for node in self.db.entries(None, {}):
58 self.add(node)
60 self.xmpp.add_filter('out', self._save_last_status)
62 def _save_last_status(self, stanza):
64 if isinstance(stanza, Presence):
65 sfrom = stanza['from'].full
66 sto = stanza['to'].full
68 if not sfrom:
69 sfrom = self.xmpp.boundjid
71 if stanza['type'] in stanza.showtypes or \
72 stanza['type'] in ('available', 'unavailable'):
73 if sto:
74 self[sfrom][sto].last_status = stanza
75 else:
76 self[sfrom].last_status = stanza
77 with self[sfrom]._last_status_lock:
78 for jid in self[sfrom]:
79 self[sfrom][jid].last_status = None
81 if not self.xmpp.sentpresence:
82 self.xmpp.event('sent_presence')
83 self.xmpp.sentpresence = True
85 return stanza
87 def __getitem__(self, key):
88 """
89 Return the roster node for a JID.
91 A new roster node will be created if one
92 does not already exist.
94 Arguments:
95 key -- Return the roster for this JID.
96 """
97 if key is None:
98 key = self.xmpp.boundjid
99 if not isinstance(key, JID):
100 key = JID(key)
101 key = key.bare
103 if key not in self._rosters:
104 self.add(key)
105 self._rosters[key].auto_authorize = self.auto_authorize
106 self._rosters[key].auto_subscribe = self.auto_subscribe
107 return self._rosters[key]
109 def keys(self):
110 """Return the JIDs managed by the roster."""
111 return self._rosters.keys()
113 def __iter__(self):
114 """Iterate over the roster nodes."""
115 return self._rosters.__iter__()
117 def add(self, node):
119 Add a new roster node for the given JID.
121 Arguments:
122 node -- The JID for the new roster node.
124 if not isinstance(node, JID):
125 node = JID(node)
127 node = node.bare
128 if node not in self._rosters:
129 self._rosters[node] = RosterNode(self.xmpp, node, self.db)
131 def set_backend(self, db=None, save=True):
133 Set the datastore interface object for the roster.
135 Arguments:
136 db -- The new datastore interface.
137 save -- If True, save the existing state to the new
138 backend datastore. Defaults to True.
140 self.db = db
141 existing_entries = set(self._rosters)
142 new_entries = set(self.db.entries(None, {}))
144 for node in existing_entries:
145 self._rosters[node].set_backend(db, save)
146 for node in new_entries - existing_entries:
147 self.add(node)
149 def reset(self):
151 Reset the state of the roster to forget any current
152 presence information. Useful after a disconnection occurs.
154 for node in self:
155 self[node].reset()
157 def send_presence(self, **kwargs):
159 Create, initialize, and send a Presence stanza.
161 If no recipient is specified, send the presence immediately.
162 Otherwise, forward the send request to the recipient's roster
163 entry for processing.
165 Arguments:
166 pshow -- The presence's show value.
167 pstatus -- The presence's status message.
168 ppriority -- This connections' priority.
169 pto -- The recipient of a directed presence.
170 pfrom -- The sender of a directed presence, which should
171 be the owner JID plus resource.
172 ptype -- The type of presence, such as 'subscribe'.
173 pnick -- Optional nickname of the presence's sender.
175 if self.xmpp.is_component and not kwargs.get('pfrom', ''):
176 kwargs['pfrom'] = self.jid
177 self.xmpp.send_presence(**kwargs)
179 @property
180 def auto_authorize(self):
182 Auto accept or deny subscription requests.
184 If True, auto accept subscription requests.
185 If False, auto deny subscription requests.
186 If None, don't automatically respond.
188 return self._auto_authorize
190 @auto_authorize.setter
191 def auto_authorize(self, value):
193 Auto accept or deny subscription requests.
195 If True, auto accept subscription requests.
196 If False, auto deny subscription requests.
197 If None, don't automatically respond.
199 self._auto_authorize = value
200 for node in self._rosters:
201 self._rosters[node].auto_authorize = value
203 @property
204 def auto_subscribe(self):
206 Auto send requests for mutual subscriptions.
208 If True, auto send mutual subscription requests.
210 return self._auto_subscribe
212 @auto_subscribe.setter
213 def auto_subscribe(self, value):
215 Auto send requests for mutual subscriptions.
217 If True, auto send mutual subscription requests.
219 self._auto_subscribe = value
220 for node in self._rosters:
221 self._rosters[node].auto_subscribe = value
223 def __repr__(self):
224 return repr(self._rosters)