multi.py 7.0 KB

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