Compare commits
	
		
			32 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 45991e47ee | ||
|   | b8f40eb843 | ||
|   | b73a859031 | ||
|   | 9dbf246f0b | ||
|   | 4fb77ac878 | ||
|   | d0c506f930 | ||
|   | 7351fe1a02 | ||
|   | 38c2f51f83 | ||
|   | 1bf34caa5b | ||
|   | 5769935720 | ||
|   | 0214db7545 | ||
|   | ffc6f031d9 | ||
|   | 9e248bb852 | ||
|   | 973890e2c9 | ||
|   | 9c08e56ed0 | ||
|   | b888610525 | ||
|   | 6d68706326 | ||
|   | 5bdcd9ef9d | ||
|   | 2eff35cc7a | ||
|   | ac330b5c6c | ||
|   | 46ffa8e9fe | ||
|   | 03847497cc | ||
|   | 185d7cf28e | ||
|   | 8aa3d0c047 | ||
|   | 9e3d506651 | ||
|   | 2f3ff37a24 | ||
|   | 1f09d60a52 | ||
|   | d528884723 | ||
|   | d9aff3d36f | ||
|   | 04cc48775d | ||
|   | 27ebb6e8f6 | ||
|   | 8f55704928 | 
| @@ -26,13 +26,7 @@ from sleekxmpp.xmlstream.matcher import * | ||||
| from sleekxmpp.xmlstream.handler import * | ||||
|  | ||||
|  | ||||
| # Flag indicating if DNS SRV records are available for use. | ||||
| SRV_SUPPORT = True | ||||
| try: | ||||
|     import dns.resolver | ||||
| except: | ||||
|     SRV_SUPPORT = False | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
| # In order to make sure that Unicode is handled properly | ||||
| # in Python 2.x, reset the default encoding. | ||||
| @@ -192,9 +186,9 @@ class BaseXMPP(XMLStream): | ||||
|                 xep = "(XEP-%s) " % self.plugin[plugin].xep | ||||
|  | ||||
|             desc = (xep, self.plugin[plugin].description) | ||||
|             logging.debug("Loaded Plugin %s%s" % desc) | ||||
|             log.debug("Loaded Plugin %s%s" % desc) | ||||
|         except: | ||||
|             logging.exception("Unable to load plugin: %s", plugin) | ||||
|             log.exception("Unable to load plugin: %s", plugin) | ||||
|  | ||||
|     def register_plugins(self): | ||||
|         """ | ||||
| @@ -228,7 +222,7 @@ class BaseXMPP(XMLStream): | ||||
|         if key in self.plugin: | ||||
|             return self.plugin[key] | ||||
|         else: | ||||
|             logging.warning("""Plugin "%s" is not loaded.""" % key) | ||||
|             log.warning("""Plugin "%s" is not loaded.""" % key) | ||||
|             return False | ||||
|  | ||||
|     def get(self, key, default): | ||||
| @@ -446,12 +440,12 @@ class BaseXMPP(XMLStream): | ||||
|         """ | ||||
|         Attribute accessor for bare jid | ||||
|         """ | ||||
|         logging.warning("jid property deprecated. Use boundjid.bare") | ||||
|         log.warning("jid property deprecated. Use boundjid.bare") | ||||
|         return self.boundjid.bare | ||||
|  | ||||
|     @jid.setter | ||||
|     def jid(self, value): | ||||
|         logging.warning("jid property deprecated. Use boundjid.bare") | ||||
|         log.warning("jid property deprecated. Use boundjid.bare") | ||||
|         self.boundjid.bare = value | ||||
|  | ||||
|     @property | ||||
| @@ -459,12 +453,12 @@ class BaseXMPP(XMLStream): | ||||
|         """ | ||||
|         Attribute accessor for full jid | ||||
|         """ | ||||
|         logging.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         log.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         return self.boundjid.full | ||||
|  | ||||
|     @fulljid.setter | ||||
|     def fulljid(self, value): | ||||
|         logging.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         log.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         self.boundjid.full = value | ||||
|  | ||||
|     @property | ||||
| @@ -472,12 +466,12 @@ class BaseXMPP(XMLStream): | ||||
|         """ | ||||
|         Attribute accessor for jid resource | ||||
|         """ | ||||
|         logging.warning("resource property deprecated. Use boundjid.resource") | ||||
|         log.warning("resource property deprecated. Use boundjid.resource") | ||||
|         return self.boundjid.resource | ||||
|  | ||||
|     @resource.setter | ||||
|     def resource(self, value): | ||||
|         logging.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         log.warning("fulljid property deprecated. Use boundjid.full") | ||||
|         self.boundjid.resource = value | ||||
|  | ||||
|     @property | ||||
| @@ -485,12 +479,12 @@ class BaseXMPP(XMLStream): | ||||
|         """ | ||||
|         Attribute accessor for jid usernode | ||||
|         """ | ||||
|         logging.warning("username property deprecated. Use boundjid.user") | ||||
|         log.warning("username property deprecated. Use boundjid.user") | ||||
|         return self.boundjid.user | ||||
|  | ||||
|     @username.setter | ||||
|     def username(self, value): | ||||
|         logging.warning("username property deprecated. Use boundjid.user") | ||||
|         log.warning("username property deprecated. Use boundjid.user") | ||||
|         self.boundjid.user = value | ||||
|  | ||||
|     @property | ||||
| @@ -498,17 +492,17 @@ class BaseXMPP(XMLStream): | ||||
|         """ | ||||
|         Attribute accessor for jid host | ||||
|         """ | ||||
|         logging.warning("server property deprecated. Use boundjid.host") | ||||
|         log.warning("server property deprecated. Use boundjid.host") | ||||
|         return self.boundjid.server | ||||
|  | ||||
|     @server.setter | ||||
|     def server(self, value): | ||||
|         logging.warning("server property deprecated. Use boundjid.host") | ||||
|         log.warning("server property deprecated. Use boundjid.host") | ||||
|         self.boundjid.server = value | ||||
|  | ||||
|     def set_jid(self, jid): | ||||
|         """Rip a JID apart and claim it as our own.""" | ||||
|         logging.debug("setting jid to %s" % jid) | ||||
|         log.debug("setting jid to %s" % jid) | ||||
|         self.boundjid.full = jid | ||||
|  | ||||
|     def getjidresource(self, fulljid): | ||||
| @@ -553,6 +547,7 @@ class BaseXMPP(XMLStream): | ||||
|         priority = presence['priority'] | ||||
|  | ||||
|         was_offline = False | ||||
|         got_online = False | ||||
|         old_roster = self.roster.get(jid, {}).get(resource, {}) | ||||
|  | ||||
|         # Create a new roster entry if needed. | ||||
| @@ -569,7 +564,7 @@ class BaseXMPP(XMLStream): | ||||
|         # Determine if the user has just come online. | ||||
|         if not resource in connections: | ||||
|             if show == 'available' or show in presence.showtypes: | ||||
|                 self.event("got_online", presence) | ||||
|                 got_online = True | ||||
|             was_offline = True | ||||
|             connections[resource] = {} | ||||
|  | ||||
| @@ -587,7 +582,7 @@ class BaseXMPP(XMLStream): | ||||
|         # disconnects. Determine if this was the last connection | ||||
|         # for the JID. | ||||
|         if show == 'unavailable': | ||||
|             logging.debug("%s %s got offline" % (jid, resource)) | ||||
|             log.debug("%s %s got offline" % (jid, resource)) | ||||
|             del connections[resource] | ||||
|  | ||||
|             if not connections and not self.roster[jid]['in_roster']: | ||||
| @@ -601,7 +596,9 @@ class BaseXMPP(XMLStream): | ||||
|  | ||||
|         # Presence state has changed. | ||||
|         self.event("changed_status", presence) | ||||
|         logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, | ||||
|         if got_online: | ||||
|             self.event("got_online", presence) | ||||
|         log.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, | ||||
|                                                    show, status)) | ||||
|  | ||||
|     def _handle_subscribe(self, presence): | ||||
| @@ -618,8 +615,8 @@ class BaseXMPP(XMLStream): | ||||
|         None       *          Disable automatic handling and use | ||||
|                               a custom handler. | ||||
|         """ | ||||
|         presence = self.Presence() | ||||
|         presence['to'] = presence['from'].bare | ||||
|         presence.reply() | ||||
|         presence['to'] = presence['to'].bare | ||||
|  | ||||
|         # We are using trinary logic, so conditions have to be | ||||
|         # more explicit than usual. | ||||
|   | ||||
| @@ -32,6 +32,9 @@ except: | ||||
|     SRV_SUPPORT = False | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class ClientXMPP(BaseXMPP): | ||||
|  | ||||
|     """ | ||||
| @@ -133,7 +136,7 @@ class ClientXMPP(BaseXMPP): | ||||
|  | ||||
|     def _session_timeout_check(self): | ||||
|         if not self.session_started_event.isSet(): | ||||
|             logging.debug("Session start has taken more than 15 seconds") | ||||
|             log.debug("Session start has taken more than 15 seconds") | ||||
|             self.disconnect(reconnect=self.auto_reconnect) | ||||
|  | ||||
|     def connect(self, address=tuple()): | ||||
| @@ -150,19 +153,19 @@ class ClientXMPP(BaseXMPP): | ||||
|         self.session_started_event.clear() | ||||
|         if not address or len(address) < 2: | ||||
|             if not self.srv_support: | ||||
|                 logging.debug("Did not supply (address, port) to connect" + \ | ||||
|                 log.debug("Did not supply (address, port) to connect" + \ | ||||
|                               " to and no SRV support is installed" + \ | ||||
|                               " (http://www.dnspython.org)." + \ | ||||
|                               " Continuing to attempt connection, using" + \ | ||||
|                               " server hostname from JID.") | ||||
|             else: | ||||
|                 logging.debug("Since no address is supplied," + \ | ||||
|                 log.debug("Since no address is supplied," + \ | ||||
|                               "attempting SRV lookup.") | ||||
|                 try: | ||||
|                     xmpp_srv = "_xmpp-client._tcp.%s" % self.server | ||||
|                     answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV) | ||||
|                 except dns.resolver.NXDOMAIN: | ||||
|                     logging.debug("No appropriate SRV record found." + \ | ||||
|                 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): | ||||
|                     log.debug("No appropriate SRV record found." + \ | ||||
|                                   " Using JID server name.") | ||||
|                 else: | ||||
|                     # Pick a random server, weighted by priority. | ||||
| @@ -219,8 +222,8 @@ class ClientXMPP(BaseXMPP): | ||||
|         iq['roster']['items'] = {jid: {'name': name, | ||||
|                                        'subscription': subscription, | ||||
|                                        'groups': groups}} | ||||
|         resp = iq.send() | ||||
|         return resp['type'] == 'result' | ||||
|         response = iq.send() | ||||
|         return response['type'] == 'result' | ||||
|  | ||||
|     def del_roster_item(self, jid): | ||||
|         """ | ||||
| @@ -235,8 +238,8 @@ class ClientXMPP(BaseXMPP): | ||||
|     def get_roster(self): | ||||
|         """Request the roster from the server.""" | ||||
|         iq = self.Iq()._set_stanza_values({'type': 'get'}).enable('roster') | ||||
|         iq.send() | ||||
|         self._handle_roster(iq, request=True) | ||||
|         response = iq.send() | ||||
|         self._handle_roster(response, request=True) | ||||
|  | ||||
|     def _handle_stream_features(self, features): | ||||
|         """ | ||||
| @@ -276,7 +279,7 @@ class ClientXMPP(BaseXMPP): | ||||
|             self.send_xml(xml) | ||||
|             return True | ||||
|         else: | ||||
|             logging.warning("The module tlslite is required to log in" +\ | ||||
|             log.warning("The module tlslite is required to log in" +\ | ||||
|                             " to some servers, and has not been found.") | ||||
|             return False | ||||
|  | ||||
| @@ -286,7 +289,7 @@ class ClientXMPP(BaseXMPP): | ||||
|  | ||||
|         Restarts the stream. | ||||
|         """ | ||||
|         logging.debug("Starting TLS") | ||||
|         log.debug("Starting TLS") | ||||
|         if self.start_tls(): | ||||
|             raise RestartStream() | ||||
|  | ||||
| @@ -300,7 +303,7 @@ class ClientXMPP(BaseXMPP): | ||||
|         if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features: | ||||
|             return False | ||||
|  | ||||
|         logging.debug("Starting SASL Auth") | ||||
|         log.debug("Starting SASL Auth") | ||||
|         sasl_ns = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|         self.add_handler("<success xmlns='%s' />" % sasl_ns, | ||||
|                          self._handle_auth_success, | ||||
| @@ -334,7 +337,7 @@ class ClientXMPP(BaseXMPP): | ||||
|                     sasl_ns, | ||||
|                     'ANONYMOUS')) | ||||
|             else: | ||||
|                 logging.error("No appropriate login method.") | ||||
|                 log.error("No appropriate login method.") | ||||
|                 self.disconnect() | ||||
|         return True | ||||
|  | ||||
| @@ -356,7 +359,7 @@ class ClientXMPP(BaseXMPP): | ||||
|         Arguments: | ||||
|             xml -- The SASL authentication failure element. | ||||
|         """ | ||||
|         logging.info("Authentication failed.") | ||||
|         log.info("Authentication failed.") | ||||
|         self.event("failed_auth", direct=True) | ||||
|         self.disconnect() | ||||
|  | ||||
| @@ -367,7 +370,7 @@ class ClientXMPP(BaseXMPP): | ||||
|         Arguments: | ||||
|             xml -- The bind feature element. | ||||
|         """ | ||||
|         logging.debug("Requesting resource: %s" % self.boundjid.resource) | ||||
|         log.debug("Requesting resource: %s" % self.boundjid.resource) | ||||
|         xml.clear() | ||||
|         iq = self.Iq(stype='set') | ||||
|         if self.boundjid.resource: | ||||
| @@ -381,10 +384,10 @@ class ClientXMPP(BaseXMPP): | ||||
|         self.set_jid(response.xml.find('{%s}bind/{%s}jid' % (bind_ns, | ||||
|                                                              bind_ns)).text) | ||||
|         self.bound = True | ||||
|         logging.info("Node set to: %s" % self.boundjid.fulljid) | ||||
|         log.info("Node set to: %s" % self.boundjid.fulljid) | ||||
|         session_ns = 'urn:ietf:params:xml:ns:xmpp-session' | ||||
|         if "{%s}session" % session_ns not in self.features or self.bindfail: | ||||
|             logging.debug("Established Session") | ||||
|             log.debug("Established Session") | ||||
|             self.sessionstarted = True | ||||
|             self.session_started_event.set() | ||||
|             self.event("session_start") | ||||
| @@ -399,7 +402,7 @@ class ClientXMPP(BaseXMPP): | ||||
|         if self.authenticated and self.bound: | ||||
|             iq = self.makeIqSet(xml) | ||||
|             response = iq.send() | ||||
|             logging.debug("Established Session") | ||||
|             log.debug("Established Session") | ||||
|             self.sessionstarted = True | ||||
|             self.session_started_event.set() | ||||
|             self.event("session_start") | ||||
|   | ||||
| @@ -15,13 +15,16 @@ import hashlib | ||||
|  | ||||
| from sleekxmpp import plugins | ||||
| from sleekxmpp import stanza | ||||
| from sleekxmpp.basexmpp import BaseXMPP, SRV_SUPPORT | ||||
| from sleekxmpp.basexmpp import BaseXMPP | ||||
| from sleekxmpp.xmlstream import XMLStream, RestartStream | ||||
| from sleekxmpp.xmlstream import StanzaBase, ET | ||||
| from sleekxmpp.xmlstream.matcher import * | ||||
| from sleekxmpp.xmlstream.handler import * | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class ComponentXMPP(BaseXMPP): | ||||
|  | ||||
|     """ | ||||
| @@ -67,6 +70,8 @@ class ComponentXMPP(BaseXMPP): | ||||
|         self.server_port = port | ||||
|         self.set_jid(jid) | ||||
|         self.secret = secret | ||||
|         self.plugin_config = plugin_config | ||||
|         self.plugin_whitelist = plugin_whitelist | ||||
|         self.is_component = True | ||||
|  | ||||
|         self.register_handler( | ||||
| @@ -80,7 +85,7 @@ class ComponentXMPP(BaseXMPP): | ||||
|  | ||||
|         Overrides XMLStream.connect. | ||||
|         """ | ||||
|         logging.debug("Connecting to %s:%s" % (self.server_host, | ||||
|         log.debug("Connecting to %s:%s" % (self.server_host, | ||||
|                                                self.server_port)) | ||||
|         return XMLStream.connect(self, self.server_host, | ||||
|                                        self.server_port) | ||||
|   | ||||
| @@ -38,6 +38,9 @@ class XMPPError(Exception): | ||||
|                               element. Same as the additional arguments to | ||||
|                               the ET.Element constructor. | ||||
|         """ | ||||
|         if extension_args is None: | ||||
|             extension_args = {} | ||||
|  | ||||
|         self.condition = condition | ||||
|         self.text = text | ||||
|         self.etype = etype | ||||
|   | ||||
| @@ -5,5 +5,6 @@ | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
| __all__ = ['xep_0004', 'xep_0030', 'xep_0033', 'xep_0045', 'xep_0050', | ||||
| 'xep_0078', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060'] | ||||
| __all__ = ['xep_0004', 'xep_0012', 'xep_0030', 'xep_0033', 'xep_0045', | ||||
|            'xep_0050', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify', | ||||
|            'xep_0060', 'xep_0202'] | ||||
|   | ||||
| @@ -14,6 +14,9 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID | ||||
| from .. stanza.iq import Iq | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class GmailQuery(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'query' | ||||
| @@ -34,12 +37,12 @@ class MailBox(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mailbox' | ||||
|     plugin_attrib = 'mailbox' | ||||
|     interfaces = set(('result-time', 'total-matched', 'total-estimate',  | ||||
|     interfaces = set(('result-time', 'total-matched', 'total-estimate', | ||||
|                       'url', 'threads', 'matched', 'estimate')) | ||||
|  | ||||
|     def getThreads(self): | ||||
|         threads = [] | ||||
|         for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,  | ||||
|         for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace, | ||||
|                                                       MailThread.name)): | ||||
|             threads.append(MailThread(xml=threadXML, parent=None)) | ||||
|         return threads | ||||
| @@ -55,10 +58,10 @@ class MailThread(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mail-thread-info' | ||||
|     plugin_attrib = 'thread' | ||||
|     interfaces = set(('tid', 'participation', 'messages', 'date',  | ||||
|     interfaces = set(('tid', 'participation', 'messages', 'date', | ||||
|                       'senders', 'url', 'labels', 'subject', 'snippet')) | ||||
|     sub_interfaces = set(('labels', 'subject', 'snippet')) | ||||
|          | ||||
|  | ||||
|     def getSenders(self): | ||||
|         senders = [] | ||||
|         sendersXML = self.xml.find('{%s}senders' % self.namespace) | ||||
| @@ -91,13 +94,13 @@ class gmail_notify(base.base_plugin): | ||||
|     """ | ||||
|     Google Talk: Gmail Notifications | ||||
|     """ | ||||
|      | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.description = 'Google Talk: Gmail Notifications' | ||||
|  | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Gmail Result', | ||||
|                      MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,  | ||||
|                      MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns, | ||||
|                                                    MailBox.namespace, | ||||
|                                                    MailBox.name)), | ||||
|                      self.handle_gmail)) | ||||
| @@ -108,7 +111,7 @@ class gmail_notify(base.base_plugin): | ||||
|                                                    NewMail.namespace, | ||||
|                                                    NewMail.name)), | ||||
|                      self.handle_new_mail)) | ||||
|          | ||||
|  | ||||
|         registerStanzaPlugin(Iq, GmailQuery) | ||||
|         registerStanzaPlugin(Iq, MailBox) | ||||
|         registerStanzaPlugin(Iq, NewMail) | ||||
| @@ -118,12 +121,12 @@ class gmail_notify(base.base_plugin): | ||||
|     def handle_gmail(self, iq): | ||||
|         mailbox = iq['mailbox'] | ||||
|         approx = ' approximately' if mailbox['estimated'] else '' | ||||
|         logging.info('Gmail: Received%s %s emails' % (approx, mailbox['total-matched'])) | ||||
|         log.info('Gmail: Received%s %s emails' % (approx, mailbox['total-matched'])) | ||||
|         self.last_result_time = mailbox['result-time'] | ||||
|         self.xmpp.event('gmail_messages', iq) | ||||
|  | ||||
|     def handle_new_mail(self, iq): | ||||
|         logging.info("Gmail: New emails received!") | ||||
|         log.info("Gmail: New emails received!") | ||||
|         self.xmpp.event('gmail_notify') | ||||
|         self.checkEmail() | ||||
|  | ||||
| @@ -135,9 +138,9 @@ class gmail_notify(base.base_plugin): | ||||
|  | ||||
|     def search(self, query=None, newer=None): | ||||
|         if query is None: | ||||
|             logging.info("Gmail: Checking for new emails") | ||||
|             log.info("Gmail: Checking for new emails") | ||||
|         else: | ||||
|             logging.info('Gmail: Searching for emails matching: "%s"' % query) | ||||
|             log.info('Gmail: Searching for emails matching: "%s"' % query) | ||||
|         iq = self.xmpp.Iq() | ||||
|         iq['type'] = 'get' | ||||
|         iq['to'] = self.xmpp.jid | ||||
|   | ||||
| @@ -3,15 +3,19 @@ import logging | ||||
| from xml.etree import cElementTree as ET | ||||
| import types | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class jobs(base.base_plugin): | ||||
| 	def plugin_init(self): | ||||
| 		self.xep = 'pubsubjob' | ||||
| 		self.description = "Job distribution over Pubsub" | ||||
| 	 | ||||
|  | ||||
| 	def post_init(self): | ||||
| 		pass | ||||
| 		#TODO add event | ||||
| 	 | ||||
|  | ||||
| 	def createJobNode(self, host, jid, node, config=None): | ||||
| 		pass | ||||
|  | ||||
| @@ -40,7 +44,7 @@ class jobs(base.base_plugin): | ||||
| 		iq['psstate']['payload'] = state | ||||
| 		result = iq.send() | ||||
| 		if result is None or type(result) == types.BooleanType or result['type'] != 'result': | ||||
| 			logging.error("Unable to change %s:%s to %s" % (node, jobid, state)) | ||||
| 			log.error("Unable to change %s:%s to %s" % (node, jobid, state)) | ||||
| 			return False | ||||
| 		return True | ||||
|  | ||||
|   | ||||
| @@ -2,42 +2,46 @@ | ||||
|     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 . import base | ||||
| import logging | ||||
| import log | ||||
| from xml.etree import cElementTree as ET | ||||
| import copy | ||||
| import logging | ||||
| #TODO support item groups and results | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class old_0004(base.base_plugin): | ||||
| 	 | ||||
|  | ||||
| 	def plugin_init(self): | ||||
| 		self.xep = '0004' | ||||
| 		self.description = '*Deprecated Data Forms' | ||||
| 		self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform, name='Old Message Form') | ||||
| 	 | ||||
|  | ||||
| 	def post_init(self): | ||||
| 		base.base_plugin.post_init(self) | ||||
| 		self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') | ||||
| 		logging.warning("This implementation of XEP-0004 is deprecated.") | ||||
| 	 | ||||
| 		log.warning("This implementation of XEP-0004 is deprecated.") | ||||
|  | ||||
| 	def handler_message_xform(self, xml): | ||||
| 		object = self.handle_form(xml) | ||||
| 		self.xmpp.event("message_form", object) | ||||
| 	 | ||||
|  | ||||
| 	def handler_presence_xform(self, xml): | ||||
| 		object = self.handle_form(xml) | ||||
| 		self.xmpp.event("presence_form", object) | ||||
| 	 | ||||
|  | ||||
| 	def handle_form(self, xml): | ||||
| 		xmlform = xml.find('{jabber:x:data}x') | ||||
| 		object = self.buildForm(xmlform) | ||||
| 		self.xmpp.event("message_xform", object) | ||||
| 		return object | ||||
| 	 | ||||
|  | ||||
| 	def buildForm(self, xml): | ||||
| 		form = Form(ftype=xml.attrib['type']) | ||||
| 		form.fromXML(xml) | ||||
| @@ -51,12 +55,12 @@ class FieldContainer(object): | ||||
| 		self.fields = [] | ||||
| 		self.field = {} | ||||
| 		self.stanza = stanza | ||||
| 	 | ||||
|  | ||||
| 	def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None): | ||||
| 		self.field[var] = FormField(var, ftype, label, desc, required, value) | ||||
| 		self.fields.append(self.field[var]) | ||||
| 		return self.field[var] | ||||
| 	 | ||||
|  | ||||
| 	def buildField(self, xml): | ||||
| 		self.field[xml.get('var', '__unnamed__')] = FormField(xml.get('var', '__unnamed__'), xml.get('type', 'text-single')) | ||||
| 		self.fields.append(self.field[xml.get('var', '__unnamed__')]) | ||||
| @@ -66,13 +70,13 @@ class FieldContainer(object): | ||||
| 		self.stanza = xml.tag | ||||
| 		for field in xml.findall('{jabber:x:data}field'): | ||||
| 			self.buildField(field) | ||||
| 	 | ||||
|  | ||||
| 	def getXML(self, ftype): | ||||
| 		container = ET.Element(self.stanza) | ||||
| 		for field in self.fields: | ||||
| 			container.append(field.getXML(ftype)) | ||||
| 		return container | ||||
| 	 | ||||
|  | ||||
| class Form(FieldContainer): | ||||
| 	types = ('form', 'submit', 'cancel', 'result') | ||||
| 	def __init__(self, xmpp=None, ftype='form', title='', instructions=''): | ||||
| @@ -85,7 +89,7 @@ class Form(FieldContainer): | ||||
| 		self.instructions = instructions | ||||
| 		self.reported = [] | ||||
| 		self.items = [] | ||||
| 	 | ||||
|  | ||||
| 	def merge(self, form2): | ||||
| 		form1 = Form(ftype=self.type) | ||||
| 		form1.fromXML(self.getXML(self.type)) | ||||
| @@ -98,18 +102,18 @@ class Form(FieldContainer): | ||||
| 				if (option, label) not in form1.field[field.var].options: | ||||
| 					form1.fields[field.var].addOption(option, label) | ||||
| 		return form1 | ||||
| 	 | ||||
|  | ||||
| 	def copy(self): | ||||
| 		newform = Form(ftype=self.type) | ||||
| 		newform.fromXML(self.getXML(self.type)) | ||||
| 		return newform | ||||
| 	 | ||||
|  | ||||
| 	def update(self, form): | ||||
| 		values = form.getValues() | ||||
| 		for var in values: | ||||
| 			if var in self.fields: | ||||
| 				self.fields[var].setValue(self.fields[var]) | ||||
| 	 | ||||
|  | ||||
| 	def getValues(self): | ||||
| 		result = {} | ||||
| 		for field in self.fields: | ||||
| @@ -118,7 +122,7 @@ class Form(FieldContainer): | ||||
| 				value = value[0] | ||||
| 			result[field.var] = value | ||||
| 		return result | ||||
| 	 | ||||
|  | ||||
| 	def setValues(self, values={}): | ||||
| 		for field in values: | ||||
| 			if field in self.field: | ||||
| @@ -127,10 +131,10 @@ class Form(FieldContainer): | ||||
| 						self.field[field].setValue(value) | ||||
| 				else: | ||||
| 					self.field[field].setValue(values[field]) | ||||
| 	 | ||||
|  | ||||
| 	def fromXML(self, xml): | ||||
| 		self.buildForm(xml) | ||||
| 	 | ||||
|  | ||||
| 	def addItem(self): | ||||
| 		newitem = FieldContainer('item') | ||||
| 		self.items.append(newitem) | ||||
| @@ -148,21 +152,21 @@ class Form(FieldContainer): | ||||
| 	def buildReported(self, xml): | ||||
| 		reported = self.addReported() | ||||
| 		reported.buildContainer(xml) | ||||
| 	 | ||||
|  | ||||
| 	def setTitle(self, title): | ||||
| 		self.title = title | ||||
| 	 | ||||
|  | ||||
| 	def setInstructions(self, instructions): | ||||
| 		self.instructions = instructions | ||||
| 	 | ||||
|  | ||||
| 	def setType(self, ftype): | ||||
| 		self.type = ftype | ||||
| 	 | ||||
|  | ||||
| 	def getXMLMessage(self, to): | ||||
| 		msg = self.xmpp.makeMessage(to) | ||||
| 		msg.append(self.getXML()) | ||||
| 		return msg | ||||
| 	 | ||||
|  | ||||
| 	def buildForm(self, xml): | ||||
| 		self.type = xml.get('type', 'form') | ||||
| 		if xml.find('{jabber:x:data}title') is not None: | ||||
| @@ -175,7 +179,7 @@ class Form(FieldContainer): | ||||
| 			self.buildReported(reported) | ||||
| 		for item in xml.findall('{jabber:x:data}item'): | ||||
| 			self.buildItem(item) | ||||
| 	 | ||||
|  | ||||
| 	#def getXML(self, tostring = False): | ||||
| 	def getXML(self, ftype=None): | ||||
| 		if ftype: | ||||
| @@ -199,7 +203,7 @@ class Form(FieldContainer): | ||||
| 		#if tostring: | ||||
| 		#	form = self.xmpp.tostring(form) | ||||
| 		return form | ||||
| 	 | ||||
|  | ||||
| 	def getXHTML(self): | ||||
| 		form = ET.Element('{http://www.w3.org/1999/xhtml}form') | ||||
| 		if self.title: | ||||
| @@ -217,8 +221,8 @@ class Form(FieldContainer): | ||||
| 		for field in self.items: | ||||
| 			form.append(field.getXHTML()) | ||||
| 		return form | ||||
| 		 | ||||
| 	 | ||||
|  | ||||
|  | ||||
| 	def makeSubmit(self): | ||||
| 		self.setType('submit') | ||||
|  | ||||
| @@ -246,13 +250,13 @@ class FormField(object): | ||||
| 			self.islinebreak = False | ||||
| 		if value: | ||||
| 			self.setValue(value) | ||||
| 	 | ||||
|  | ||||
| 	def addOption(self, value, label): | ||||
| 		if self.islist: | ||||
| 			self.options.append((value, label)) | ||||
| 		else: | ||||
| 			raise ValueError("Cannot add options to non-list type field.") | ||||
| 	 | ||||
|  | ||||
| 	def setTrue(self): | ||||
| 		if self.type == 'boolean': | ||||
| 			self.value = [True] | ||||
| @@ -263,10 +267,10 @@ class FormField(object): | ||||
|  | ||||
| 	def require(self): | ||||
| 		self.required = True | ||||
| 	 | ||||
|  | ||||
| 	def setDescription(self, desc): | ||||
| 		self.desc = desc | ||||
| 	 | ||||
|  | ||||
| 	def setValue(self, value): | ||||
| 		if self.type == 'boolean': | ||||
| 			if value in ('1', 1, True, 'true', 'True', 'yes'): | ||||
| @@ -291,10 +295,10 @@ class FormField(object): | ||||
| 				pass | ||||
| 		else: | ||||
| 			self.value = '' | ||||
| 	 | ||||
|  | ||||
| 	def setAnswer(self, value): | ||||
| 		self.setValue(value) | ||||
| 	 | ||||
|  | ||||
| 	def buildField(self, xml): | ||||
| 		self.type = xml.get('type', 'text-single') | ||||
| 		self.label = xml.get('label', '') | ||||
| @@ -306,7 +310,7 @@ class FormField(object): | ||||
| 			self.require() | ||||
| 		if xml.find('{jabber:x:data}desc') is not None: | ||||
| 			self.setDescription(xml.find('{jabber:x:data}desc').text) | ||||
| 	 | ||||
|  | ||||
| 	def getXML(self, ftype): | ||||
| 		field = ET.Element('{jabber:x:data}field') | ||||
| 		if ftype != 'result': | ||||
| @@ -342,7 +346,7 @@ class FormField(object): | ||||
| 				valuexml.text = value | ||||
| 			field.append(valuexml) | ||||
| 		return field | ||||
| 	 | ||||
|  | ||||
| 	def getXHTML(self): | ||||
| 		field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type}) | ||||
| 		if self.label: | ||||
| @@ -414,4 +418,4 @@ class FormField(object): | ||||
| 				pass | ||||
| 		label.append(formf) | ||||
| 		return field | ||||
| 		 | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,9 @@ from .. stanza.message import Message | ||||
| import types | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Form(ElementBase): | ||||
| 	namespace = 'jabber:x:data' | ||||
| 	name = 'x' | ||||
| @@ -33,7 +36,7 @@ class Form(ElementBase): | ||||
| 		if title is not None: | ||||
| 			self['title'] = title | ||||
| 		self.field = FieldAccessor(self) | ||||
| 	 | ||||
|  | ||||
| 	def setup(self, xml=None): | ||||
| 		if ElementBase.setup(self, xml): #if we had to generate xml | ||||
| 			self['type'] = 'form' | ||||
| @@ -55,11 +58,11 @@ class Form(ElementBase): | ||||
| 		return field | ||||
|  | ||||
| 	def getXML(self, type='submit'): | ||||
| 		logging.warning("Form.getXML() is deprecated API compatibility with plugins/old_0004.py") | ||||
| 		log.warning("Form.getXML() is deprecated API compatibility with plugins/old_0004.py") | ||||
| 		return self.xml | ||||
| 	 | ||||
|  | ||||
| 	def fromXML(self, xml): | ||||
| 		logging.warning("Form.fromXML() is deprecated API compatibility with plugins/old_0004.py") | ||||
| 		log.warning("Form.fromXML() is deprecated API compatibility with plugins/old_0004.py") | ||||
| 		n = Form(xml=xml) | ||||
| 		return n | ||||
|  | ||||
| @@ -113,10 +116,10 @@ class Form(ElementBase): | ||||
| 		reportedXML = self.xml.find('{%s}reported' % self.namespace) | ||||
| 		if reportedXML is not None: | ||||
| 			self.xml.remove(reportedXML) | ||||
| 	 | ||||
|  | ||||
| 	def getFields(self, use_dict=False): | ||||
| 		fields = {} if use_dict else [] | ||||
| 		fieldsXML = self.xml.findall('{%s}field' % FormField.namespace)     | ||||
| 		fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) | ||||
| 		for fieldXML in fieldsXML: | ||||
| 			field = FormField(xml=fieldXML) | ||||
| 			if use_dict: | ||||
| @@ -144,7 +147,7 @@ class Form(ElementBase): | ||||
|  | ||||
| 	def getReported(self): | ||||
| 		fields = {} | ||||
| 		fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,  | ||||
| 		fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace, | ||||
| 									 FormField.namespace)) | ||||
| 		for fieldXML in fieldsXML: | ||||
| 			field = FormField(xml=fieldXML) | ||||
| @@ -197,7 +200,7 @@ class Form(ElementBase): | ||||
| 		fields = self.getFields(use_dict=True) | ||||
| 		for field in values: | ||||
| 			fields[field]['value'] = values[field] | ||||
| 	 | ||||
|  | ||||
| 	def merge(self, other): | ||||
| 		new = copy.copy(self) | ||||
| 		if type(other) == types.DictType: | ||||
| @@ -212,7 +215,7 @@ class Form(ElementBase): | ||||
| class FieldAccessor(object): | ||||
| 	def __init__(self, form): | ||||
| 		self.form = form | ||||
| 	 | ||||
|  | ||||
| 	def __getitem__(self, key): | ||||
| 		return self.form.getFields(use_dict=True)[key] | ||||
|  | ||||
| @@ -366,21 +369,21 @@ class xep_0004(base.base_plugin): | ||||
|  | ||||
| 		self.xmpp.registerHandler( | ||||
| 			Callback('Data Form', | ||||
| 				 MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns,  | ||||
| 				 MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns, | ||||
| 								   Form.namespace)), | ||||
| 				 self.handle_form)) | ||||
|  | ||||
| 		registerStanzaPlugin(FormField, FieldOption) | ||||
| 		registerStanzaPlugin(Form, FormField) | ||||
| 		registerStanzaPlugin(Message, Form) | ||||
| 	 | ||||
|  | ||||
| 	def makeForm(self, ftype='form', title='', instructions=''): | ||||
| 		f = Form() | ||||
| 		f['type'] = ftype | ||||
| 		f['title'] = title | ||||
| 		f['instructions'] = instructions | ||||
| 		return f | ||||
| 	 | ||||
|  | ||||
| 	def post_init(self): | ||||
| 		base.base_plugin.post_init(self) | ||||
| 		self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') | ||||
|   | ||||
							
								
								
									
										118
									
								
								sleekxmpp/plugins/xep_0012.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								sleekxmpp/plugins/xep_0012.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| """ | ||||
|     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 datetime import datetime | ||||
| import logging | ||||
|  | ||||
| from . import base | ||||
| from .. stanza.iq import Iq | ||||
| from .. xmlstream.handler.callback import Callback | ||||
| from .. xmlstream.matcher.xpath import MatchXPath | ||||
| from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class LastActivity(ElementBase): | ||||
|     name = 'query' | ||||
|     namespace = 'jabber:iq:last' | ||||
|     plugin_attrib = 'last_activity' | ||||
|     interfaces = set(('seconds', 'status')) | ||||
|  | ||||
|     def get_seconds(self): | ||||
|         return int(self._get_attr('seconds')) | ||||
|  | ||||
|     def set_seconds(self, value): | ||||
|         self._set_attr('seconds', str(value)) | ||||
|  | ||||
|     def get_status(self): | ||||
|         return self.xml.text | ||||
|  | ||||
|     def set_status(self, value): | ||||
|         self.xml.text = str(value) | ||||
|  | ||||
|     def del_status(self): | ||||
|         self.xml.text = '' | ||||
|  | ||||
| class xep_0012(base.base_plugin): | ||||
|     """ | ||||
|     XEP-0012 Last Activity | ||||
|     """ | ||||
|     def plugin_init(self): | ||||
|         self.description = "Last Activity" | ||||
|         self.xep = "0012" | ||||
|  | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Last Activity', | ||||
|                  MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns, | ||||
|                                   LastActivity.namespace)), | ||||
|                  self.handle_last_activity_query)) | ||||
|         register_stanza_plugin(Iq, LastActivity) | ||||
|  | ||||
|         self.xmpp.add_event_handler('last_activity_request', self.handle_last_activity) | ||||
|  | ||||
|  | ||||
|     def post_init(self): | ||||
|         base.base_plugin.post_init(self) | ||||
|         if self.xmpp.is_component: | ||||
|             # We are a component, so we track the uptime | ||||
|             self.xmpp.add_event_handler("session_start", self._reset_uptime) | ||||
|             self._start_datetime = datetime.now() | ||||
|         self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:last') | ||||
|  | ||||
|     def _reset_uptime(self, event): | ||||
|         self._start_datetime = datetime.now() | ||||
|  | ||||
|     def handle_last_activity_query(self, iq): | ||||
|         if iq['type'] == 'get': | ||||
|             log.debug("Last activity requested by %s" % iq['from']) | ||||
|             self.xmpp.event('last_activity_request', iq) | ||||
|         elif iq['type'] == 'result': | ||||
|             log.debug("Last activity result from %s" % iq['from']) | ||||
|             self.xmpp.event('last_activity', iq) | ||||
|  | ||||
|     def handle_last_activity(self, iq): | ||||
|         jid = iq['from'] | ||||
|  | ||||
|         if self.xmpp.is_component: | ||||
|             # Send the uptime | ||||
|             result = LastActivity() | ||||
|             td = (datetime.now() - self._start_datetime) | ||||
|             result['seconds'] = td.seconds + td.days * 24 * 3600 | ||||
|             reply = iq.reply().setPayload(result.xml).send() | ||||
|         else: | ||||
|             barejid = JID(jid).bare | ||||
|             if barejid in self.xmpp.roster and ( self.xmpp.roster[barejid]['subscription'] in ('from', 'both') or | ||||
|                                                  barejid == self.xmpp.boundjid.bare ): | ||||
|                 # We don't know how to calculate it | ||||
|                 iq.reply().error().setPayload(iq['last_activity'].xml) | ||||
|                 iq['error']['code'] = '503' | ||||
|                 iq['error']['type'] = 'cancel' | ||||
|                 iq['error']['condition'] = 'service-unavailable' | ||||
|                 iq.send() | ||||
|             else: | ||||
|                 iq.reply().error().setPayload(iq['last_activity'].xml) | ||||
|                 iq['error']['code'] = '403' | ||||
|                 iq['error']['type'] = 'auth' | ||||
|                 iq['error']['condition'] = 'forbidden' | ||||
|                 iq.send() | ||||
|  | ||||
|     def get_last_activity(self, jid): | ||||
|         """Query the LastActivity of jid and return it in seconds""" | ||||
|         iq = self.xmpp.makeIqGet() | ||||
|         query = LastActivity() | ||||
|         iq.append(query.xml) | ||||
|         iq.attrib['to'] = jid | ||||
|         iq.attrib['from'] = self.xmpp.boundjid.full | ||||
|         id = iq.get('id') | ||||
|         result = iq.send() | ||||
|         if result and result is not None and result.get('type', 'error') != 'error': | ||||
|             return result['last_activity']['seconds'] | ||||
|         else: | ||||
|             return False | ||||
| @@ -13,315 +13,317 @@ from .. xmlstream.matcher.xpath import MatchXPath | ||||
| from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID | ||||
| from .. stanza.iq import Iq | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class DiscoInfo(ElementBase): | ||||
| 	namespace = 'http://jabber.org/protocol/disco#info' | ||||
| 	name = 'query' | ||||
| 	plugin_attrib = 'disco_info' | ||||
| 	interfaces = set(('node', 'features', 'identities')) | ||||
|     namespace = 'http://jabber.org/protocol/disco#info' | ||||
|     name = 'query' | ||||
|     plugin_attrib = 'disco_info' | ||||
|     interfaces = set(('node', 'features', 'identities')) | ||||
|  | ||||
| 	def getFeatures(self): | ||||
| 		features = [] | ||||
| 		featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
| 		for feature in featuresXML: | ||||
| 			features.append(feature.attrib['var']) | ||||
| 		return features | ||||
|     def getFeatures(self): | ||||
|         features = [] | ||||
|         featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
|         for feature in featuresXML: | ||||
|             features.append(feature.attrib['var']) | ||||
|         return features | ||||
|  | ||||
| 	def setFeatures(self, features): | ||||
| 		self.delFeatures() | ||||
| 		for name in features: | ||||
| 			self.addFeature(name) | ||||
|     def setFeatures(self, features): | ||||
|         self.delFeatures() | ||||
|         for name in features: | ||||
|             self.addFeature(name) | ||||
|  | ||||
| 	def delFeatures(self): | ||||
| 		featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
| 		for feature in featuresXML: | ||||
| 			self.xml.remove(feature) | ||||
|     def delFeatures(self): | ||||
|         featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
|         for feature in featuresXML: | ||||
|             self.xml.remove(feature) | ||||
|  | ||||
| 	def addFeature(self, feature): | ||||
| 		featureXML = ET.Element('{%s}feature' % self.namespace,  | ||||
| 					{'var': feature}) | ||||
| 		self.xml.append(featureXML) | ||||
|     def addFeature(self, feature): | ||||
|         featureXML = ET.Element('{%s}feature' % self.namespace, | ||||
|                     {'var': feature}) | ||||
|         self.xml.append(featureXML) | ||||
|  | ||||
| 	def delFeature(self, feature): | ||||
| 		featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
| 		for featureXML in featuresXML: | ||||
| 			if featureXML.attrib['var'] == feature: | ||||
| 				self.xml.remove(featureXML) | ||||
|     def delFeature(self, feature): | ||||
|         featuresXML = self.xml.findall('{%s}feature' % self.namespace) | ||||
|         for featureXML in featuresXML: | ||||
|             if featureXML.attrib['var'] == feature: | ||||
|                 self.xml.remove(featureXML) | ||||
|  | ||||
| 	def getIdentities(self): | ||||
| 		ids = [] | ||||
| 		idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
| 		for idXML in idsXML: | ||||
| 			idData = (idXML.attrib['category'], | ||||
| 				  idXML.attrib['type'], | ||||
| 				  idXML.attrib.get('name', '')) | ||||
| 			ids.append(idData) | ||||
| 		return ids | ||||
|     def getIdentities(self): | ||||
|         ids = [] | ||||
|         idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
|         for idXML in idsXML: | ||||
|             idData = (idXML.attrib['category'], | ||||
|                   idXML.attrib['type'], | ||||
|                   idXML.attrib.get('name', '')) | ||||
|             ids.append(idData) | ||||
|         return ids | ||||
|  | ||||
| 	def setIdentities(self, ids): | ||||
| 		self.delIdentities() | ||||
| 		for idData in ids: | ||||
| 			self.addIdentity(*idData) | ||||
|     def setIdentities(self, ids): | ||||
|         self.delIdentities() | ||||
|         for idData in ids: | ||||
|             self.addIdentity(*idData) | ||||
|  | ||||
| 	def delIdentities(self): | ||||
| 		idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
| 		for idXML in idsXML: | ||||
| 			self.xml.remove(idXML) | ||||
|     def delIdentities(self): | ||||
|         idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
|         for idXML in idsXML: | ||||
|             self.xml.remove(idXML) | ||||
|  | ||||
| 	def addIdentity(self, category, id_type, name=''): | ||||
| 		idXML = ET.Element('{%s}identity' % self.namespace,  | ||||
| 				   {'category': category, | ||||
| 				    'type': id_type, | ||||
| 				    'name': name}) | ||||
| 		self.xml.append(idXML) | ||||
|     def addIdentity(self, category, id_type, name=''): | ||||
|         idXML = ET.Element('{%s}identity' % self.namespace, | ||||
|                    {'category': category, | ||||
|                     'type': id_type, | ||||
|                     'name': name}) | ||||
|         self.xml.append(idXML) | ||||
|  | ||||
| 	def delIdentity(self, category, id_type, name=''): | ||||
| 		idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
| 		for idXML in idsXML: | ||||
| 			idData = (idXML.attrib['category'],  | ||||
| 				  idXML.attrib['type']) | ||||
| 			delId = (category, id_type) | ||||
| 			if idData == delId: | ||||
| 				self.xml.remove(idXML) | ||||
|     def delIdentity(self, category, id_type, name=''): | ||||
|         idsXML = self.xml.findall('{%s}identity' % self.namespace) | ||||
|         for idXML in idsXML: | ||||
|             idData = (idXML.attrib['category'], | ||||
|                   idXML.attrib['type']) | ||||
|             delId = (category, id_type) | ||||
|             if idData == delId: | ||||
|                 self.xml.remove(idXML) | ||||
|  | ||||
|  | ||||
| class DiscoItems(ElementBase): | ||||
| 	namespace = 'http://jabber.org/protocol/disco#items' | ||||
| 	name = 'query' | ||||
| 	plugin_attrib = 'disco_items' | ||||
| 	interfaces = set(('node', 'items')) | ||||
|     namespace = 'http://jabber.org/protocol/disco#items' | ||||
|     name = 'query' | ||||
|     plugin_attrib = 'disco_items' | ||||
|     interfaces = set(('node', 'items')) | ||||
|  | ||||
| 	def getItems(self): | ||||
| 		items = [] | ||||
| 		itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
| 		for item in itemsXML: | ||||
| 			itemData = (item.attrib['jid'], | ||||
| 				    item.attrib.get('node'), | ||||
| 				    item.attrib.get('name')) | ||||
| 			items.append(itemData) | ||||
| 		return items | ||||
|     def getItems(self): | ||||
|         items = [] | ||||
|         itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
|         for item in itemsXML: | ||||
|             itemData = (item.attrib['jid'], | ||||
|                     item.attrib.get('node'), | ||||
|                     item.attrib.get('name')) | ||||
|             items.append(itemData) | ||||
|         return items | ||||
|  | ||||
| 	def setItems(self, items): | ||||
| 		self.delItems() | ||||
| 		for item in items: | ||||
| 			self.addItem(*item) | ||||
|     def setItems(self, items): | ||||
|         self.delItems() | ||||
|         for item in items: | ||||
|             self.addItem(*item) | ||||
|  | ||||
| 	def delItems(self): | ||||
| 		itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
| 		for item in itemsXML: | ||||
| 			self.xml.remove(item) | ||||
|     def delItems(self): | ||||
|         itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
|         for item in itemsXML: | ||||
|             self.xml.remove(item) | ||||
|  | ||||
| 	def addItem(self, jid, node='', name=''): | ||||
| 		itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid}) | ||||
| 		if name: | ||||
| 			itemXML.attrib['name'] = name | ||||
| 		if node: | ||||
| 			itemXML.attrib['node'] = node | ||||
| 		self.xml.append(itemXML) | ||||
|     def addItem(self, jid, node='', name=''): | ||||
|         itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid}) | ||||
|         if name: | ||||
|             itemXML.attrib['name'] = name | ||||
|         if node: | ||||
|             itemXML.attrib['node'] = node | ||||
|         self.xml.append(itemXML) | ||||
|  | ||||
|     def delItem(self, jid, node=''): | ||||
|         itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
|         for itemXML in itemsXML: | ||||
|             itemData = (itemXML.attrib['jid'], | ||||
|                     itemXML.attrib.get('node', '')) | ||||
|             itemDel = (jid, node) | ||||
|             if itemData == itemDel: | ||||
|                 self.xml.remove(itemXML) | ||||
|  | ||||
| 	def delItem(self, jid, node=''): | ||||
| 		itemsXML = self.xml.findall('{%s}item' % self.namespace) | ||||
| 		for itemXML in itemsXML: | ||||
| 			itemData = (itemXML.attrib['jid'], | ||||
| 				    itemXML.attrib.get('node', '')) | ||||
| 			itemDel = (jid, node) | ||||
| 			if itemData == itemDel: | ||||
| 				self.xml.remove(itemXML) | ||||
| 	 | ||||
|  | ||||
| class DiscoNode(object): | ||||
| 	""" | ||||
| 	Collection object for grouping info and item information | ||||
| 	into nodes. | ||||
| 	""" | ||||
| 	def __init__(self, name): | ||||
| 		self.name = name | ||||
| 		self.info = DiscoInfo() | ||||
| 		self.items = DiscoItems() | ||||
|     """ | ||||
|     Collection object for grouping info and item information | ||||
|     into nodes. | ||||
|     """ | ||||
|     def __init__(self, name): | ||||
|         self.name = name | ||||
|         self.info = DiscoInfo() | ||||
|         self.items = DiscoItems() | ||||
|  | ||||
| 		self.info['node'] = name | ||||
| 		self.items['node'] = name | ||||
|         self.info['node'] = name | ||||
|         self.items['node'] = name | ||||
|  | ||||
| 		# This is a bit like poor man's inheritance, but | ||||
| 		# to simplify adding information to the node we  | ||||
| 		# map node functions to either the info or items | ||||
| 		# stanza objects. | ||||
| 		# | ||||
| 		# We don't want to make DiscoNode inherit from  | ||||
| 		# DiscoInfo and DiscoItems because DiscoNode is | ||||
| 		# not an actual stanza, and doing so would create | ||||
| 		# confusion and potential bugs. | ||||
|         # This is a bit like poor man's inheritance, but | ||||
|         # to simplify adding information to the node we | ||||
|         # map node functions to either the info or items | ||||
|         # stanza objects. | ||||
|         # | ||||
|         # We don't want to make DiscoNode inherit from | ||||
|         # DiscoInfo and DiscoItems because DiscoNode is | ||||
|         # not an actual stanza, and doing so would create | ||||
|         # confusion and potential bugs. | ||||
|  | ||||
| 		self._map(self.items, 'items', ['get', 'set', 'del']) | ||||
| 		self._map(self.items, 'item', ['add', 'del']) | ||||
| 		self._map(self.info, 'identities', ['get', 'set', 'del']) | ||||
| 		self._map(self.info, 'identity', ['add', 'del']) | ||||
| 		self._map(self.info, 'features', ['get', 'set', 'del']) | ||||
| 		self._map(self.info, 'feature', ['add', 'del']) | ||||
|         self._map(self.items, 'items', ['get', 'set', 'del']) | ||||
|         self._map(self.items, 'item', ['add', 'del']) | ||||
|         self._map(self.info, 'identities', ['get', 'set', 'del']) | ||||
|         self._map(self.info, 'identity', ['add', 'del']) | ||||
|         self._map(self.info, 'features', ['get', 'set', 'del']) | ||||
|         self._map(self.info, 'feature', ['add', 'del']) | ||||
|  | ||||
| 	def isEmpty(self): | ||||
| 		""" | ||||
| 		Test if the node contains any information. Useful for | ||||
| 		determining if a node can be deleted. | ||||
| 		""" | ||||
| 		ids = self.getIdentities() | ||||
| 		features = self.getFeatures() | ||||
| 		items = self.getItems() | ||||
|     def isEmpty(self): | ||||
|         """ | ||||
|         Test if the node contains any information. Useful for | ||||
|         determining if a node can be deleted. | ||||
|         """ | ||||
|         ids = self.getIdentities() | ||||
|         features = self.getFeatures() | ||||
|         items = self.getItems() | ||||
|  | ||||
| 		if not ids and not features and not items: | ||||
| 			return True | ||||
| 		return False | ||||
|         if not ids and not features and not items: | ||||
|             return True | ||||
|         return False | ||||
|  | ||||
| 	def _map(self, obj, interface, access): | ||||
| 		""" | ||||
| 		Map functions of the form obj.accessInterface | ||||
| 		to self.accessInterface for each given access type. | ||||
| 		""" | ||||
| 		interface = interface.title() | ||||
| 		for access_type in access: | ||||
| 			method = access_type + interface | ||||
| 			if hasattr(obj, method): | ||||
| 				setattr(self, method, getattr(obj, method)) | ||||
|     def _map(self, obj, interface, access): | ||||
|         """ | ||||
|         Map functions of the form obj.accessInterface | ||||
|         to self.accessInterface for each given access type. | ||||
|         """ | ||||
|         interface = interface.title() | ||||
|         for access_type in access: | ||||
|             method = access_type + interface | ||||
|             if hasattr(obj, method): | ||||
|                 setattr(self, method, getattr(obj, method)) | ||||
|  | ||||
|  | ||||
| class xep_0030(base.base_plugin): | ||||
| 	""" | ||||
| 	XEP-0030 Service Discovery | ||||
| 	""" | ||||
| 	 | ||||
| 	def plugin_init(self): | ||||
| 		self.xep = '0030' | ||||
| 		self.description = 'Service Discovery' | ||||
|     """ | ||||
|     XEP-0030 Service Discovery | ||||
|     """ | ||||
|  | ||||
| 		self.xmpp.registerHandler( | ||||
| 			Callback('Disco Items', | ||||
| 				 MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,  | ||||
| 								  DiscoItems.namespace)), | ||||
| 				 self.handle_item_query)) | ||||
|     def plugin_init(self): | ||||
|         self.xep = '0030' | ||||
|         self.description = 'Service Discovery' | ||||
|  | ||||
| 		self.xmpp.registerHandler( | ||||
| 			Callback('Disco Info', | ||||
| 				 MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,  | ||||
| 								  DiscoInfo.namespace)), | ||||
| 				 self.handle_info_query)) | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Disco Items', | ||||
|                  MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns, | ||||
|                                   DiscoItems.namespace)), | ||||
|                  self.handle_item_query)) | ||||
|  | ||||
| 		registerStanzaPlugin(Iq, DiscoInfo) | ||||
| 		registerStanzaPlugin(Iq, DiscoItems) | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Disco Info', | ||||
|                  MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns, | ||||
|                                   DiscoInfo.namespace)), | ||||
|                  self.handle_info_query)) | ||||
|  | ||||
| 		self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items) | ||||
| 		self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info) | ||||
|         registerStanzaPlugin(Iq, DiscoInfo) | ||||
|         registerStanzaPlugin(Iq, DiscoItems) | ||||
|  | ||||
| 		self.nodes = {'main': DiscoNode('main')} | ||||
|         self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items) | ||||
|         self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info) | ||||
|  | ||||
| 	def add_node(self, node): | ||||
| 		if node not in self.nodes: | ||||
| 			self.nodes[node] = DiscoNode(node) | ||||
|         self.nodes = {'main': DiscoNode('main')} | ||||
|  | ||||
| 	def del_node(self, node): | ||||
| 		if node in self.nodes: | ||||
| 			del self.nodes[node] | ||||
|     def add_node(self, node): | ||||
|         if node not in self.nodes: | ||||
|             self.nodes[node] = DiscoNode(node) | ||||
|  | ||||
| 	def handle_item_query(self, iq): | ||||
| 		if iq['type'] == 'get': | ||||
| 			logging.debug("Items requested by %s" % iq['from']) | ||||
| 			self.xmpp.event('disco_items_request', iq) | ||||
| 		elif iq['type'] == 'result': | ||||
| 			logging.debug("Items result from %s" % iq['from']) | ||||
| 			self.xmpp.event('disco_items', iq) | ||||
|     def del_node(self, node): | ||||
|         if node in self.nodes: | ||||
|             del self.nodes[node] | ||||
|  | ||||
| 	def handle_info_query(self, iq): | ||||
| 		if iq['type'] == 'get': | ||||
| 			logging.debug("Info requested by %s" % iq['from']) | ||||
| 			self.xmpp.event('disco_info_request', iq) | ||||
| 		elif iq['type'] == 'result': | ||||
| 			logging.debug("Info result from %s" % iq['from']) | ||||
| 			self.xmpp.event('disco_info', iq) | ||||
|     def handle_item_query(self, iq): | ||||
|         if iq['type'] == 'get': | ||||
|             log.debug("Items requested by %s" % iq['from']) | ||||
|             self.xmpp.event('disco_items_request', iq) | ||||
|         elif iq['type'] == 'result': | ||||
|             log.debug("Items result from %s" % iq['from']) | ||||
|             self.xmpp.event('disco_items', iq) | ||||
|  | ||||
| 	def handle_disco_info(self, iq, forwarded=False): | ||||
| 		""" | ||||
| 		A default handler for disco#info requests. If another | ||||
| 		handler is registered, this one will defer and not run. | ||||
| 		""" | ||||
| 		handlers = self.xmpp.event_handlers['disco_info_request'] | ||||
| 		if not forwarded and len(handlers) > 1: | ||||
| 			return | ||||
|     def handle_info_query(self, iq): | ||||
|         if iq['type'] == 'get': | ||||
|             log.debug("Info requested by %s" % iq['from']) | ||||
|             self.xmpp.event('disco_info_request', iq) | ||||
|         elif iq['type'] == 'result': | ||||
|             log.debug("Info result from %s" % iq['from']) | ||||
|             self.xmpp.event('disco_info', iq) | ||||
|  | ||||
| 		node_name = iq['disco_info']['node'] | ||||
| 		if not node_name: | ||||
| 			node_name = 'main' | ||||
|     def handle_disco_info(self, iq, forwarded=False): | ||||
|         """ | ||||
|         A default handler for disco#info requests. If another | ||||
|         handler is registered, this one will defer and not run. | ||||
|         """ | ||||
|         if not forwarded and self.xmpp.event_handled('disco_info_request'): | ||||
|             return | ||||
|  | ||||
| 		logging.debug("Using default handler for disco#info on node '%s'." % node_name) | ||||
|         node_name = iq['disco_info']['node'] | ||||
|         if not node_name: | ||||
|             node_name = 'main' | ||||
|  | ||||
| 		if node_name in self.nodes: | ||||
| 			node = self.nodes[node_name] | ||||
| 			iq.reply().setPayload(node.info.xml).send() | ||||
| 		else: | ||||
| 			logging.debug("Node %s requested, but does not exist." % node_name) | ||||
| 			iq.reply().error().setPayload(iq['disco_info'].xml) | ||||
| 			iq['error']['code'] = '404' | ||||
| 			iq['error']['type'] = 'cancel' | ||||
| 			iq['error']['condition'] = 'item-not-found' | ||||
| 			iq.send() | ||||
| 			 | ||||
| 	def handle_disco_items(self, iq, forwarded=False): | ||||
| 		""" | ||||
| 		A default handler for disco#items requests. If another | ||||
| 		handler is registered, this one will defer and not run. | ||||
|         log.debug("Using default handler for disco#info on node '%s'." % node_name) | ||||
|  | ||||
| 		If this handler is called by your own custom handler with | ||||
| 		forwarded set to True, then it will run as normal. | ||||
| 		""" | ||||
| 		handlers = self.xmpp.event_handlers['disco_items_request'] | ||||
| 		if not forwarded and len(handlers) > 1: | ||||
| 			return | ||||
|         if node_name in self.nodes: | ||||
|             node = self.nodes[node_name] | ||||
|             iq.reply().setPayload(node.info.xml).send() | ||||
|         else: | ||||
|             log.debug("Node %s requested, but does not exist." % node_name) | ||||
|             iq.reply().error().setPayload(iq['disco_info'].xml) | ||||
|             iq['error']['code'] = '404' | ||||
|             iq['error']['type'] = 'cancel' | ||||
|             iq['error']['condition'] = 'item-not-found' | ||||
|             iq.send() | ||||
|  | ||||
| 		node_name = iq['disco_items']['node'] | ||||
| 		if not node_name: | ||||
| 			node_name = 'main' | ||||
|     def handle_disco_items(self, iq, forwarded=False): | ||||
|         """ | ||||
|         A default handler for disco#items requests. If another | ||||
|         handler is registered, this one will defer and not run. | ||||
|  | ||||
| 		logging.debug("Using default handler for disco#items on node '%s'." % node_name) | ||||
|         If this handler is called by your own custom handler with | ||||
|         forwarded set to True, then it will run as normal. | ||||
|         """ | ||||
|         if not forwarded and self.xmpp.event_handled('disco_items_request'): | ||||
|             return | ||||
|  | ||||
| 		if node_name in self.nodes: | ||||
| 			node = self.nodes[node_name] | ||||
| 			iq.reply().setPayload(node.items.xml).send() | ||||
| 		else:	 | ||||
| 			logging.debug("Node %s requested, but does not exist." % node_name) | ||||
| 			iq.reply().error().setPayload(iq['disco_items'].xml) | ||||
| 			iq['error']['code'] = '404' | ||||
| 			iq['error']['type'] = 'cancel' | ||||
| 			iq['error']['condition'] = 'item-not-found' | ||||
| 			iq.send() | ||||
|         node_name = iq['disco_items']['node'] | ||||
|         if not node_name: | ||||
|             node_name = 'main' | ||||
|  | ||||
| 	# Older interface methods for backwards compatibility | ||||
|         log.debug("Using default handler for disco#items on node '%s'." % node_name) | ||||
|  | ||||
| 	def getInfo(self, jid, node='', dfrom=None): | ||||
| 		iq = self.xmpp.Iq() | ||||
| 		iq['type'] = 'get' | ||||
| 		iq['to'] = jid | ||||
| 		iq['from'] = dfrom | ||||
| 		iq['disco_info']['node'] = node | ||||
| 		return iq.send() | ||||
|         if node_name in self.nodes: | ||||
|             node = self.nodes[node_name] | ||||
|             iq.reply().setPayload(node.items.xml).send() | ||||
|         else: | ||||
|             log.debug("Node %s requested, but does not exist." % node_name) | ||||
|             iq.reply().error().setPayload(iq['disco_items'].xml) | ||||
|             iq['error']['code'] = '404' | ||||
|             iq['error']['type'] = 'cancel' | ||||
|             iq['error']['condition'] = 'item-not-found' | ||||
|             iq.send() | ||||
|  | ||||
| 	def getItems(self, jid, node='', dfrom=None): | ||||
| 		iq = self.xmpp.Iq() | ||||
| 		iq['type'] = 'get' | ||||
| 		iq['to'] = jid | ||||
| 		iq['from'] = dfrom | ||||
| 		iq['disco_items']['node'] = node | ||||
| 		return iq.send() | ||||
| 	 | ||||
| 	def add_feature(self, feature, node='main'): | ||||
| 		self.add_node(node) | ||||
| 		self.nodes[node].addFeature(feature) | ||||
| 	 | ||||
| 	def add_identity(self, category='', itype='', name='', node='main'): | ||||
| 		self.add_node(node) | ||||
| 		self.nodes[node].addIdentity(category=category, | ||||
| 					     id_type=itype, | ||||
| 					     name=name) | ||||
| 	 | ||||
| 	def add_item(self, jid=None, name='', node='main', subnode=''): | ||||
| 		self.add_node(node) | ||||
| 		self.add_node(subnode) | ||||
| 		if jid is None: | ||||
| 			jid = self.xmpp.fulljid | ||||
| 		self.nodes[node].addItem(jid=jid, name=name, node=subnode) | ||||
|     # Older interface methods for backwards compatibility | ||||
|  | ||||
|     def getInfo(self, jid, node='', dfrom=None): | ||||
|         iq = self.xmpp.Iq() | ||||
|         iq['type'] = 'get' | ||||
|         iq['to'] = jid | ||||
|         iq['from'] = dfrom | ||||
|         iq['disco_info']['node'] = node | ||||
|         return iq.send() | ||||
|  | ||||
|     def getItems(self, jid, node='', dfrom=None): | ||||
|         iq = self.xmpp.Iq() | ||||
|         iq['type'] = 'get' | ||||
|         iq['to'] = jid | ||||
|         iq['from'] = dfrom | ||||
|         iq['disco_items']['node'] = node | ||||
|         return iq.send() | ||||
|  | ||||
|     def add_feature(self, feature, node='main'): | ||||
|         self.add_node(node) | ||||
|         self.nodes[node].addFeature(feature) | ||||
|  | ||||
|     def add_identity(self, category='', itype='', name='', node='main'): | ||||
|         self.add_node(node) | ||||
|         self.nodes[node].addIdentity(category=category, | ||||
|                          id_type=itype, | ||||
|                          name=name) | ||||
|  | ||||
|     def add_item(self, jid=None, name='', node='main', subnode=''): | ||||
|         self.add_node(node) | ||||
|         self.add_node(subnode) | ||||
|         if jid is None: | ||||
|             jid = self.xmpp.fulljid | ||||
|         self.nodes[node].addItem(jid=jid, name=name, node=subnode) | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     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 __future__ import with_statement | ||||
| @@ -15,6 +15,10 @@ from .. xmlstream.handler.callback import Callback | ||||
| from .. xmlstream.matcher.xpath import MatchXPath | ||||
| from .. xmlstream.matcher.xmlmask import MatchXMLMask | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class MUCPresence(ElementBase): | ||||
| 	name = 'x' | ||||
| 	namespace = 'http://jabber.org/protocol/muc#user' | ||||
| @@ -34,79 +38,79 @@ class MUCPresence(ElementBase): | ||||
| 		#TODO if no affilation, set it to the default and return default | ||||
| 		item = self.getXMLItem() | ||||
| 		return item.get('affiliation', '') | ||||
| 	 | ||||
|  | ||||
| 	def setAffiliation(self, value): | ||||
| 		item = self.getXMLItem() | ||||
| 		#TODO check for valid affiliation | ||||
| 		item.attrib['affiliation'] = value | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def delAffiliation(self): | ||||
| 		item = self.getXMLItem() | ||||
| 		#TODO set default affiliation | ||||
| 		if 'affiliation' in item.attrib: del item.attrib['affiliation'] | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def getJid(self): | ||||
| 		item = self.getXMLItem() | ||||
| 		return JID(item.get('jid', '')) | ||||
| 	 | ||||
|  | ||||
| 	def setJid(self, value): | ||||
| 		item = self.getXMLItem() | ||||
| 		if not isinstance(value, str): | ||||
| 			value = str(value) | ||||
| 		item.attrib['jid'] = value | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def delJid(self): | ||||
| 		item = self.getXMLItem() | ||||
| 		if 'jid' in item.attrib: del item.attrib['jid'] | ||||
| 		return self | ||||
| 		 | ||||
|  | ||||
| 	def getRole(self): | ||||
| 		item = self.getXMLItem() | ||||
| 		#TODO get default role, set default role if none | ||||
| 		return item.get('role', '') | ||||
| 	 | ||||
|  | ||||
| 	def setRole(self, value): | ||||
| 		item = self.getXMLItem() | ||||
| 		#TODO check for valid role | ||||
| 		item.attrib['role'] = value | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def delRole(self): | ||||
| 		item = self.getXMLItem() | ||||
| 		#TODO set default role | ||||
| 		if 'role' in item.attrib: del item.attrib['role'] | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def getNick(self): | ||||
| 		return self.parent()['from'].resource | ||||
| 	 | ||||
|  | ||||
| 	def getRoom(self): | ||||
| 		return self.parent()['from'].bare | ||||
| 	 | ||||
|  | ||||
| 	def setNick(self, value): | ||||
| 		logging.warning("Cannot set nick through mucpresence plugin.") | ||||
| 		log.warning("Cannot set nick through mucpresence plugin.") | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def setRoom(self, value): | ||||
| 		logging.warning("Cannot set room through mucpresence plugin.") | ||||
| 		log.warning("Cannot set room through mucpresence plugin.") | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def delNick(self): | ||||
| 		logging.warning("Cannot delete nick through mucpresence plugin.") | ||||
| 		log.warning("Cannot delete nick through mucpresence plugin.") | ||||
| 		return self | ||||
| 	 | ||||
|  | ||||
| 	def delRoom(self): | ||||
| 		logging.warning("Cannot delete room through mucpresence plugin.") | ||||
| 		log.warning("Cannot delete room through mucpresence plugin.") | ||||
| 		return self | ||||
|  | ||||
| class xep_0045(base.base_plugin): | ||||
| 	""" | ||||
| 	Impliments XEP-0045 Multi User Chat | ||||
| 	""" | ||||
| 	 | ||||
|  | ||||
| 	def plugin_init(self): | ||||
| 		self.rooms = {} | ||||
| 		self.ourNicks = {} | ||||
| @@ -116,7 +120,8 @@ class xep_0045(base.base_plugin): | ||||
| 		registerStanzaPlugin(Presence, MUCPresence) | ||||
| 		self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence)) | ||||
| 		self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message)) | ||||
| 	 | ||||
| 		self.xmpp.registerHandler(Callback('MUCSubject', MatchXMLMask("<message xmlns='%s' type='groupchat'><subject/></message>" % self.xmpp.default_ns), self.handle_groupchat_subject)) | ||||
|  | ||||
| 	def handle_groupchat_presence(self, pr): | ||||
| 		""" Handle a presence in a muc. | ||||
| 		""" | ||||
| @@ -135,27 +140,33 @@ class xep_0045(base.base_plugin): | ||||
| 			if entry['nick'] not in self.rooms[entry['room']]: | ||||
| 				got_online = True | ||||
| 			self.rooms[entry['room']][entry['nick']] = entry | ||||
| 		logging.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry)) | ||||
| 		log.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry)) | ||||
| 		self.xmpp.event("groupchat_presence", pr) | ||||
| 		self.xmpp.event("muc::%s::presence" % entry['room'], pr) | ||||
| 		if got_offline: | ||||
| 			self.xmpp.event("muc::%s::got_offline" % entry['room'], pr) | ||||
| 		if got_online: | ||||
| 			self.xmpp.event("muc::%s::got_online" % entry['room'], pr) | ||||
| 	 | ||||
|  | ||||
| 	def handle_groupchat_message(self, msg): | ||||
| 		""" Handle a message event in a muc. | ||||
| 		""" | ||||
| 		self.xmpp.event('groupchat_message', msg) | ||||
| 		self.xmpp.event("muc::%s::message" % msg['from'].bare, msg) | ||||
| 		        | ||||
|  | ||||
| 	def handle_groupchat_subject(self, msg): | ||||
| 		""" Handle a message coming from a muc indicating | ||||
| 		a change of subject (or announcing it when joining the room) | ||||
| 		""" | ||||
| 		self.xmpp.event('groupchat_subject', msg) | ||||
|  | ||||
| 	def jidInRoom(self, room, jid): | ||||
| 		for nick in self.rooms[room]: | ||||
| 			entry = self.rooms[room][nick] | ||||
| 			if entry is not None and entry['jid'].full == jid: | ||||
| 				return True | ||||
| 		return False | ||||
| 	 | ||||
|  | ||||
| 	def getNick(self, room, jid): | ||||
| 		for nick in self.rooms[room]: | ||||
| 			entry = self.rooms[room][nick] | ||||
| @@ -176,12 +187,12 @@ class xep_0045(base.base_plugin): | ||||
| 		if xform is None: return False | ||||
| 		form = self.xmpp.plugin['old_0004'].buildForm(xform) | ||||
| 		return form | ||||
| 	 | ||||
|  | ||||
| 	def configureRoom(self, room, form=None, ifrom=None): | ||||
| 		if form is None: | ||||
| 			form = self.getRoomForm(room, ifrom=ifrom) | ||||
| 			#form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit') | ||||
| 			#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig')	 | ||||
| 			#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig') | ||||
| 		iq = self.xmpp.makeIqSet() | ||||
| 		iq['to'] = room | ||||
| 		if ifrom is not None: | ||||
| @@ -194,7 +205,7 @@ class xep_0045(base.base_plugin): | ||||
| 		if result['type'] == 'error': | ||||
| 			return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None): | ||||
| 		""" Join the specified room, requesting 'maxhistory' lines of history. | ||||
| 		""" | ||||
| @@ -220,7 +231,7 @@ class xep_0045(base.base_plugin): | ||||
| 			self.xmpp.send(stanza, expect) | ||||
| 		self.rooms[room] = {} | ||||
| 		self.ourNicks[room] = nick | ||||
| 	 | ||||
|  | ||||
| 	def destroy(self, room, reason='', altroom = '', ifrom=None): | ||||
| 		iq = self.xmpp.makeIqSet() | ||||
| 		if ifrom is not None: | ||||
| @@ -246,9 +257,9 @@ class xep_0045(base.base_plugin): | ||||
| 			raise TypeError | ||||
| 		query = ET.Element('{http://jabber.org/protocol/muc#admin}query') | ||||
| 		if nick is not None: | ||||
| 			item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})	 | ||||
| 			item = ET.Element('item', {'affiliation':affiliation, 'nick':nick}) | ||||
| 		else: | ||||
| 			item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})	 | ||||
| 			item = ET.Element('item', {'affiliation':affiliation, 'jid':jid}) | ||||
| 		query.append(item) | ||||
| 		iq = self.xmpp.makeIqSet(query) | ||||
| 		iq['to'] = room | ||||
| @@ -256,7 +267,7 @@ class xep_0045(base.base_plugin): | ||||
| 		if result is False or result['type'] != 'result': | ||||
| 			raise ValueError | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def invite(self, room, jid, reason=''): | ||||
| 		""" Invite a jid to a room.""" | ||||
| 		msg = self.xmpp.makeMessage(room) | ||||
| @@ -279,7 +290,7 @@ class xep_0045(base.base_plugin): | ||||
| 		else: | ||||
| 			self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick)) | ||||
| 		del self.rooms[room] | ||||
| 	 | ||||
|  | ||||
| 	def getRoomConfig(self, room): | ||||
| 		iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner') | ||||
| 		iq['to'] = room | ||||
| @@ -291,14 +302,14 @@ class xep_0045(base.base_plugin): | ||||
| 		if form is None: | ||||
| 			raise ValueError | ||||
| 		return self.xmpp.plugin['xep_0004'].buildForm(form) | ||||
| 	 | ||||
|  | ||||
| 	def cancelConfig(self, room): | ||||
| 		query = ET.Element('{http://jabber.org/protocol/muc#owner}query') | ||||
| 		x = ET.Element('{jabber:x:data}x', type='cancel') | ||||
| 		query.append(x) | ||||
| 		iq = self.xmpp.makeIqSet(query) | ||||
| 		iq.send() | ||||
| 	 | ||||
|  | ||||
| 	def setRoomConfig(self, room, config): | ||||
| 		query = ET.Element('{http://jabber.org/protocol/muc#owner}query') | ||||
| 		x = config.getXML('submit') | ||||
| @@ -307,15 +318,15 @@ class xep_0045(base.base_plugin): | ||||
| 		iq['to'] = room | ||||
| 		iq['from'] = self.xmpp.jid | ||||
| 		iq.send() | ||||
| 		 | ||||
|  | ||||
| 	def getJoinedRooms(self): | ||||
| 		return self.rooms.keys() | ||||
| 		 | ||||
|  | ||||
| 	def getOurJidInRoom(self, roomJid): | ||||
| 		""" Return the jid we're using in a room. | ||||
| 		""" | ||||
| 		return "%s/%s" % (roomJid, self.ourNicks[roomJid]) | ||||
| 		 | ||||
|  | ||||
| 	def getJidProperty(self, room, nick, jidProperty): | ||||
| 		""" Get the property of a nick in a room, such as its 'jid' or 'affiliation' | ||||
| 			If not found, return None. | ||||
| @@ -324,7 +335,7 @@ class xep_0045(base.base_plugin): | ||||
| 			return self.rooms[room][nick][jidProperty] | ||||
| 		else: | ||||
| 			return None | ||||
| 	 | ||||
|  | ||||
| 	def getRoster(self, room): | ||||
| 		""" Get the list of nicks in a room. | ||||
| 		""" | ||||
|   | ||||
| @@ -6,6 +6,10 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET | ||||
| from . import stanza_pubsub | ||||
| from . xep_0004 import Form | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class xep_0060(base.base_plugin): | ||||
| 	""" | ||||
| 	XEP-0060 Publish Subscribe | ||||
| @@ -14,7 +18,7 @@ class xep_0060(base.base_plugin): | ||||
| 	def plugin_init(self): | ||||
| 		self.xep = '0060' | ||||
| 		self.description = 'Publish-Subscribe' | ||||
| 	 | ||||
|  | ||||
| 	def create_node(self, jid, node, config=None, collection=False, ntype=None): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') | ||||
| 		create = ET.Element('create') | ||||
| @@ -52,7 +56,7 @@ class xep_0060(base.base_plugin): | ||||
| 		result = iq.send() | ||||
| 		if result is False or result is None or result['type'] == 'error': return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def subscribe(self, jid, node, bare=True, subscribee=None): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') | ||||
| 		subscribe = ET.Element('subscribe') | ||||
| @@ -72,7 +76,7 @@ class xep_0060(base.base_plugin): | ||||
| 		result = iq.send() | ||||
| 		if result is False or result is None or result['type'] == 'error': return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def unsubscribe(self, jid, node, bare=True, subscribee=None): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') | ||||
| 		unsubscribe = ET.Element('unsubscribe') | ||||
| @@ -92,7 +96,7 @@ class xep_0060(base.base_plugin): | ||||
| 		result = iq.send() | ||||
| 		if result is False or result is None or result['type'] == 'error': return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def getNodeConfig(self, jid, node=None): # if no node, then grab default | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') | ||||
| 		if node is not None: | ||||
| @@ -110,17 +114,17 @@ class xep_0060(base.base_plugin): | ||||
| 		#self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse) | ||||
| 		result = iq.send() | ||||
| 		if result is None or result == False or result['type'] == 'error': | ||||
| 			logging.warning("got error instead of config") | ||||
| 			log.warning("got error instead of config") | ||||
| 			return False | ||||
| 		if node is not None: | ||||
| 			form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}configure/{jabber:x:data}x') | ||||
| 		else: | ||||
| 			form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}default/{jabber:x:data}x') | ||||
| 		if not form or form is None: | ||||
| 			logging.error("No form found.") | ||||
| 			log.error("No form found.") | ||||
| 			return False | ||||
| 		return Form(xml=form) | ||||
| 	 | ||||
|  | ||||
| 	def getNodeSubscriptions(self, jid, node): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') | ||||
| 		subscriptions = ET.Element('subscriptions') | ||||
| @@ -133,7 +137,7 @@ class xep_0060(base.base_plugin): | ||||
| 		id = iq['id'] | ||||
| 		result = iq.send() | ||||
| 		if result is None or result == False or result['type'] == 'error': | ||||
| 			logging.warning("got error instead of config") | ||||
| 			log.warning("got error instead of config") | ||||
| 			return False | ||||
| 		else: | ||||
| 			results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}subscriptions/{http://jabber.org/protocol/pubsub#owner}subscription') | ||||
| @@ -156,7 +160,7 @@ class xep_0060(base.base_plugin): | ||||
| 		id = iq['id'] | ||||
| 		result = iq.send() | ||||
| 		if result is None or result == False or result['type'] == 'error': | ||||
| 			logging.warning("got error instead of config") | ||||
| 			log.warning("got error instead of config") | ||||
| 			return False | ||||
| 		else: | ||||
| 			results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}affiliations/{http://jabber.org/protocol/pubsub#owner}affiliation') | ||||
| @@ -181,8 +185,8 @@ class xep_0060(base.base_plugin): | ||||
| 			return True | ||||
| 		else: | ||||
| 			return False | ||||
| 		 | ||||
| 	 | ||||
|  | ||||
|  | ||||
| 	def setNodeConfig(self, jid, node, config): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') | ||||
| 		configure = ET.Element('configure') | ||||
| @@ -195,10 +199,10 @@ class xep_0060(base.base_plugin): | ||||
| 		iq.attrib['from'] = self.xmpp.fulljid | ||||
| 		id = iq['id'] | ||||
| 		result = iq.send() | ||||
| 		if result is None or result['type'] == 'error':  | ||||
| 		if result is None or result['type'] == 'error': | ||||
| 			return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def setItem(self, jid, node, items=[]): | ||||
| 		pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') | ||||
| 		publish = ET.Element('publish') | ||||
| @@ -218,7 +222,7 @@ class xep_0060(base.base_plugin): | ||||
| 		result = iq.send() | ||||
| 		if result is None or result is False or result['type'] == 'error': return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def addItem(self, jid, node, items=[]): | ||||
| 		return self.setItem(jid, node, items) | ||||
|  | ||||
| @@ -237,7 +241,7 @@ class xep_0060(base.base_plugin): | ||||
| 		result = iq.send() | ||||
| 		if result is None or result is False or result['type'] == 'error': return False | ||||
| 		return True | ||||
| 	 | ||||
|  | ||||
| 	def getNodes(self, jid): | ||||
| 		response = self.xmpp.plugin['xep_0030'].getItems(jid) | ||||
| 		items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') | ||||
| @@ -246,7 +250,7 @@ class xep_0060(base.base_plugin): | ||||
| 			for item in items: | ||||
| 				nodes[item.get('node')] = item.get('name') | ||||
| 		return nodes | ||||
| 	 | ||||
|  | ||||
| 	def getItems(self, jid, node): | ||||
| 		response = self.xmpp.plugin['xep_0030'].getItems(jid, node) | ||||
| 		items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') | ||||
| @@ -264,7 +268,7 @@ class xep_0060(base.base_plugin): | ||||
| 		try: | ||||
| 			config.field['pubsub#collection'].setValue(parent) | ||||
| 		except KeyError: | ||||
| 			logging.warning("pubsub#collection doesn't exist in config, trying to add it") | ||||
| 			log.warning("pubsub#collection doesn't exist in config, trying to add it") | ||||
| 			config.addField('pubsub#collection', value=parent) | ||||
| 		if not self.setNodeConfig(jid, child, config): | ||||
| 			return False | ||||
| @@ -298,7 +302,7 @@ class xep_0060(base.base_plugin): | ||||
| 		try: | ||||
| 			config.field['pubsub#collection'].setValue(parent) | ||||
| 		except KeyError: | ||||
| 			logging.warning("pubsub#collection doesn't exist in config, trying to add it") | ||||
| 			log.warning("pubsub#collection doesn't exist in config, trying to add it") | ||||
| 			config.addField('pubsub#collection', value=parent) | ||||
| 		if not self.setNodeConfig(jid, child, config): | ||||
| 			return False | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     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 __future__ import with_statement | ||||
| @@ -12,6 +12,9 @@ import hashlib | ||||
| from . import base | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class xep_0078(base.base_plugin): | ||||
| 	""" | ||||
| 	XEP-0078 NON-SASL Authentication | ||||
| @@ -23,14 +26,14 @@ class xep_0078(base.base_plugin): | ||||
| 		#disabling until I fix conflict with PLAIN | ||||
| 		#self.xmpp.registerFeature("<auth xmlns='http://jabber.org/features/iq-auth'/>", self.auth) | ||||
| 		self.streamid = '' | ||||
| 	 | ||||
|  | ||||
| 	def check_stream(self, xml): | ||||
| 		self.streamid = xml.attrib['id'] | ||||
| 		if xml.get('version', '0') != '1.0': | ||||
| 			self.auth() | ||||
| 	 | ||||
|  | ||||
| 	def auth(self, xml=None): | ||||
| 		logging.debug("Starting jabber:iq:auth Authentication") | ||||
| 		log.debug("Starting jabber:iq:auth Authentication") | ||||
| 		auth_request = self.xmpp.makeIqGet() | ||||
| 		auth_request_query = ET.Element('{jabber:iq:auth}query') | ||||
| 		auth_request.attrib['to'] = self.xmpp.server | ||||
| @@ -47,12 +50,12 @@ class xep_0078(base.base_plugin): | ||||
| 		query.append(username) | ||||
| 		query.append(resource) | ||||
| 		if rquery.find('{jabber:iq:auth}digest') is None: | ||||
| 			logging.warning("Authenticating via jabber:iq:auth Plain.") | ||||
| 			log.warning("Authenticating via jabber:iq:auth Plain.") | ||||
| 			password = ET.Element('password') | ||||
| 			password.text = self.xmpp.password | ||||
| 			query.append(password) | ||||
| 		else: | ||||
| 			logging.debug("Authenticating via jabber:iq:auth Digest") | ||||
| 			log.debug("Authenticating via jabber:iq:auth Digest") | ||||
| 			digest = ET.Element('digest') | ||||
| 			digest.text = hashlib.sha1(b"%s%s" % (self.streamid, self.xmpp.password)).hexdigest() | ||||
| 			query.append(digest) | ||||
| @@ -64,6 +67,6 @@ class xep_0078(base.base_plugin): | ||||
| 				self.xmpp.sessionstarted = True | ||||
| 			self.xmpp.event("session_start") | ||||
| 		else: | ||||
| 			logging.info("Authentication failed") | ||||
| 			log.info("Authentication failed") | ||||
| 			self.xmpp.disconnect() | ||||
| 			self.xmpp.event("failed_auth") | ||||
|   | ||||
| @@ -14,15 +14,18 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID | ||||
| from .. stanza.message import Message | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class ChatState(ElementBase): | ||||
|     namespace = 'http://jabber.org/protocol/chatstates' | ||||
|     plugin_attrib = 'chat_state' | ||||
|     interface = set(('state',)) | ||||
|     states = set(('active', 'composing', 'gone', 'inactive', 'paused')) | ||||
|      | ||||
|  | ||||
|     def active(self): | ||||
|         self.setState('active') | ||||
|          | ||||
|  | ||||
|     def composing(self): | ||||
|         self.setState('composing') | ||||
|  | ||||
| @@ -67,11 +70,11 @@ class xep_0085(base.base_plugin): | ||||
|     """ | ||||
|     XEP-0085 Chat State Notifications | ||||
|     """ | ||||
|      | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xep = '0085' | ||||
|         self.description = 'Chat State Notifications' | ||||
|          | ||||
|  | ||||
|         handlers = [('Active Chat State', 'active'), | ||||
|                     ('Composing Chat State', 'composing'), | ||||
|                     ('Gone Chat State', 'gone'), | ||||
| @@ -79,10 +82,10 @@ class xep_0085(base.base_plugin): | ||||
|                     ('Paused Chat State', 'paused')] | ||||
|         for handler in handlers: | ||||
|             self.xmpp.registerHandler( | ||||
|                 Callback(handler[0],  | ||||
|                          MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,  | ||||
|                 Callback(handler[0], | ||||
|                          MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns, | ||||
|                                                             ChatState.namespace, | ||||
|                                                             handler[1])),  | ||||
|                                                             handler[1])), | ||||
|                          self._handleChatState)) | ||||
|  | ||||
|         registerStanzaPlugin(Message, Active) | ||||
| @@ -90,12 +93,12 @@ class xep_0085(base.base_plugin): | ||||
|         registerStanzaPlugin(Message, Gone) | ||||
|         registerStanzaPlugin(Message, Inactive) | ||||
|         registerStanzaPlugin(Message, Paused) | ||||
|          | ||||
|  | ||||
|     def post_init(self): | ||||
|         base.base_plugin.post_init(self) | ||||
|         self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates') | ||||
|          | ||||
|  | ||||
|     def _handleChatState(self, msg): | ||||
|         state = msg['chat_state'].name | ||||
|         logging.debug("Chat State: %s, %s" % (state, msg['from'].jid)) | ||||
|         log.debug("Chat State: %s, %s" % (state, msg['from'].jid)) | ||||
|         self.xmpp.event('chatstate_%s' % state, msg) | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     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 xml.etree import cElementTree as ET | ||||
| @@ -10,50 +10,54 @@ from . import base | ||||
| import time | ||||
| import logging | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class xep_0199(base.base_plugin): | ||||
| 	"""XEP-0199 XMPP Ping""" | ||||
|     """XEP-0199 XMPP Ping""" | ||||
|  | ||||
| 	def plugin_init(self): | ||||
| 		self.description = "XMPP Ping" | ||||
| 		self.xep = "0199" | ||||
| 		self.xmpp.add_handler("<iq type='get' xmlns='%s'><ping xmlns='http://www.xmpp.org/extensions/xep-0199.html#ns'/></iq>" % self.xmpp.default_ns, self.handler_ping, name='XMPP Ping') | ||||
| 		self.running = False | ||||
| 		#if self.config.get('keepalive', True): | ||||
| 			#self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True) | ||||
| 	 | ||||
| 	def post_init(self): | ||||
| 		base.base_plugin.post_init(self) | ||||
| 		self.xmpp.plugin['xep_0030'].add_feature('http://www.xmpp.org/extensions/xep-0199.html#ns') | ||||
| 	 | ||||
| 	def handler_pingserver(self, xml): | ||||
| 		if not self.running: | ||||
| 			time.sleep(self.config.get('frequency', 300)) | ||||
| 			while self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is not False: | ||||
| 				time.sleep(self.config.get('frequency', 300)) | ||||
| 			logging.debug("Did not recieve ping back in time.  Requesting Reconnect.") | ||||
| 			self.xmpp.disconnect(reconnect=True) | ||||
| 	 | ||||
| 	def handler_ping(self, xml): | ||||
| 		iq = self.xmpp.makeIqResult(xml.get('id', 'unknown')) | ||||
| 		iq.attrib['to'] = xml.get('from', self.xmpp.server) | ||||
| 		self.xmpp.send(iq) | ||||
|     def plugin_init(self): | ||||
|         self.description = "XMPP Ping" | ||||
|         self.xep = "0199" | ||||
|         self.xmpp.add_handler("<iq type='get' xmlns='%s'><ping xmlns='urn:xmpp:ping'/></iq>" % self.xmpp.default_ns, self.handler_ping, name='XMPP Ping') | ||||
|         if self.config.get('keepalive', True): | ||||
|             self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True) | ||||
|  | ||||
| 	def sendPing(self, jid, timeout = 30): | ||||
| 		""" sendPing(jid, timeout) | ||||
| 		Sends a ping to the specified jid, returning the time (in seconds) | ||||
| 		to receive a reply, or None if no reply is received in timeout seconds. | ||||
| 		""" | ||||
| 		id = self.xmpp.getNewId() | ||||
| 		iq = self.xmpp.makeIq(id) | ||||
| 		iq.attrib['type'] = 'get' | ||||
| 		iq.attrib['to'] = jid | ||||
| 		ping = ET.Element('{http://www.xmpp.org/extensions/xep-0199.html#ns}ping') | ||||
| 		iq.append(ping) | ||||
| 		startTime = time.clock() | ||||
| 		#pingresult = self.xmpp.send(iq, self.xmpp.makeIq(id), timeout) | ||||
| 		pingresult = iq.send() | ||||
| 		endTime = time.clock() | ||||
| 		if pingresult == False: | ||||
| 			#self.xmpp.disconnect(reconnect=True) | ||||
| 			return False | ||||
| 		return endTime - startTime | ||||
|     def post_init(self): | ||||
|         base.base_plugin.post_init(self) | ||||
|         self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:ping') | ||||
|  | ||||
|     def handler_pingserver(self, xml): | ||||
|         self.xmpp.schedule("xep-0119 ping", float(self.config.get('frequency', 300)), self.scheduled_ping, repeat=True) | ||||
|  | ||||
|     def scheduled_ping(self): | ||||
|         log.debug("pinging...") | ||||
|         if self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is False: | ||||
|             log.debug("Did not recieve ping back in time.  Requesting Reconnect.") | ||||
|             self.xmpp.reconnect() | ||||
|  | ||||
|     def handler_ping(self, xml): | ||||
|         iq = self.xmpp.makeIqResult(xml.get('id', 'unknown')) | ||||
|         iq.attrib['to'] = xml.get('from', self.xmpp.boundjid.domain) | ||||
|         self.xmpp.send(iq) | ||||
|  | ||||
|     def sendPing(self, jid, timeout = 30): | ||||
|         """ sendPing(jid, timeout) | ||||
|         Sends a ping to the specified jid, returning the time (in seconds) | ||||
|         to receive a reply, or None if no reply is received in timeout seconds. | ||||
|         """ | ||||
|         id = self.xmpp.getNewId() | ||||
|         iq = self.xmpp.makeIq(id) | ||||
|         iq.attrib['type'] = 'get' | ||||
|         iq.attrib['to'] = jid | ||||
|         ping = ET.Element('{urn:xmpp:ping}ping') | ||||
|         iq.append(ping) | ||||
|         startTime = time.clock() | ||||
|         #pingresult = self.xmpp.send(iq, self.xmpp.makeIq(id), timeout) | ||||
|         pingresult = iq.send() | ||||
|         endTime = time.clock() | ||||
|         if pingresult == False: | ||||
|             #self.xmpp.disconnect(reconnect=True) | ||||
|             return False | ||||
|         return endTime - startTime | ||||
|   | ||||
							
								
								
									
										115
									
								
								sleekxmpp/plugins/xep_0202.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								sleekxmpp/plugins/xep_0202.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| """ | ||||
|     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 datetime import datetime, tzinfo | ||||
| import logging | ||||
| import time | ||||
|  | ||||
| from . import base | ||||
| from .. stanza.iq import Iq | ||||
| from .. xmlstream.handler.callback import Callback | ||||
| from .. xmlstream.matcher.xpath import MatchXPath | ||||
| from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class EntityTime(ElementBase): | ||||
|     name = 'time' | ||||
|     namespace = 'urn:xmpp:time' | ||||
|     plugin_attrib = 'entity_time' | ||||
|     interfaces = set(('tzo', 'utc')) | ||||
|     sub_interfaces = set(('tzo', 'utc')) | ||||
|  | ||||
|     #def get_utc(self): # TODO: return a datetime.tzinfo object? | ||||
|         #pass | ||||
|  | ||||
|     def set_tzo(self, tzo): # TODO: support datetime.tzinfo objects? | ||||
|         if isinstance(tzo, tzinfo): | ||||
|             td = datetime.now(tzo).utcoffset() # What if we are faking the time? datetime.now() shouldn't be used here' | ||||
|             seconds = td.seconds + td.days * 24 * 3600 | ||||
|             sign = ('+' if seconds >= 0 else '-') | ||||
|             minutes = abs(seconds // 60) | ||||
|             tzo = '{sign}{hours:02d}:{minutes:02d}'.format(sign=sign, hours=minutes//60, minutes=minutes%60) | ||||
|         elif not isinstance(tzo, str): | ||||
|             raise TypeError('The time should be a string or a datetime.tzinfo object.') | ||||
|         self._set_sub_text('tzo', tzo) | ||||
|  | ||||
|     def get_utc(self): | ||||
|         # Returns a datetime object instead the string. Is this a good idea? | ||||
|         value = self._get_sub_text('utc') | ||||
|         if '.' in value: | ||||
|             return datetime.strptime(value, '%Y-%m-%d.%fT%H:%M:%SZ') | ||||
|         else: | ||||
|             return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') | ||||
|  | ||||
|     def set_utc(self, tim=None): | ||||
|         if isinstance(tim, datetime): | ||||
|             if tim.utcoffset(): | ||||
|                 tim = tim - tim.utcoffset() | ||||
|             tim = tim.strftime('%Y-%m-%dT%H:%M:%SZ') | ||||
|         elif isinstance(tim, time.struct_time): | ||||
|             tim = time.strftime('%Y-%m-%dT%H:%M:%SZ', tim) | ||||
|         elif not isinstance(tim, str): | ||||
|             raise TypeError('The time should be a string or a datetime.datetime or time.struct_time object.') | ||||
|  | ||||
|         self._set_sub_text('utc', tim) | ||||
|  | ||||
|  | ||||
| class xep_0202(base.base_plugin): | ||||
|     """ | ||||
|     XEP-0202 Entity Time | ||||
|     """ | ||||
|     def plugin_init(self): | ||||
|         self.description = "Entity Time" | ||||
|         self.xep = "0202" | ||||
|  | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Time Request', | ||||
|                  MatchXPath('{%s}iq/{%s}time' % (self.xmpp.default_ns, | ||||
|                                   EntityTime.namespace)), | ||||
|                  self.handle_entity_time_query)) | ||||
|         register_stanza_plugin(Iq, EntityTime) | ||||
|  | ||||
|         self.xmpp.add_event_handler('entity_time_request', self.handle_entity_time) | ||||
|  | ||||
|  | ||||
|     def post_init(self): | ||||
|         base.base_plugin.post_init(self) | ||||
|  | ||||
|         self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:time') | ||||
|  | ||||
|     def handle_entity_time_query(self, iq): | ||||
|         if iq['type'] == 'get': | ||||
|             log.debug("Entity time requested by %s" % iq['from']) | ||||
|             self.xmpp.event('entity_time_request', iq) | ||||
|         elif iq['type'] == 'result': | ||||
|             log.debug("Entity time result from %s" % iq['from']) | ||||
|             self.xmpp.event('entity_time', iq) | ||||
|  | ||||
|     def handle_entity_time(self, iq): | ||||
|         iq = iq.reply() | ||||
|         iq.enable('entity_time') | ||||
|         tzo = time.strftime('%z') # %z is not on all ANSI C libraries | ||||
|         tzo = tzo[:3] + ':' + tzo[3:] | ||||
|         iq['entity_time']['tzo'] = tzo | ||||
|         iq['entity_time']['utc'] = datetime.utcnow() | ||||
|         iq.send() | ||||
|  | ||||
|     def get_entity_time(self, jid): | ||||
|         iq = self.xmpp.makeIqGet() | ||||
|         iq.enable('entity_time') | ||||
|         iq.attrib['to'] = jid | ||||
|         iq.attrib['from'] = self.xmpp.boundjid.full | ||||
|         id = iq.get('id') | ||||
|         result = iq.send() | ||||
|         if result and result is not None and result.get('type', 'error') != 'error': | ||||
|             return {'utc': result['entity_time']['utc'], 'tzo': result['entity_time']['tzo']} | ||||
|         else: | ||||
|             return False | ||||
| @@ -92,6 +92,12 @@ class Presence(RootStanza): | ||||
|  | ||||
|         return StanzaBase.setup(self, xml) | ||||
|  | ||||
|     def exception(self, e): | ||||
|         """ | ||||
|         Override exception passback for presence. | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def set_show(self, show): | ||||
|         """ | ||||
|         Set the value of the <show> element. | ||||
|   | ||||
| @@ -15,6 +15,9 @@ from sleekxmpp.stanza import Error | ||||
| from sleekxmpp.xmlstream import ET, StanzaBase, register_stanza_plugin | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class RootStanza(StanzaBase): | ||||
|  | ||||
|     """ | ||||
| @@ -58,7 +61,7 @@ class RootStanza(StanzaBase): | ||||
|                 self['error']['text'] = "SleekXMPP got into trouble." | ||||
|             else: | ||||
|                 self['error']['text'] = traceback.format_tb(e.__traceback__) | ||||
|                 logging.exception('Error handling {%s}%s stanza' % | ||||
|                 log.exception('Error handling {%s}%s stanza' % | ||||
|                                   (self.namespace, self.name)) | ||||
|         self.send() | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| """ | ||||
|  | ||||
|     SleekXMPP: The Sleek XMPP Library | ||||
|     Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of SleekXMPP. | ||||
| @@ -27,27 +26,29 @@ class SleekTest(unittest.TestCase): | ||||
|         Message              -- Create a Message stanza object. | ||||
|         Iq                   -- Create an Iq stanza object. | ||||
|         Presence             -- Create a Presence stanza object. | ||||
|         check_stanza         -- Compare a generic stanza against an XML string. | ||||
|         check_message        -- Compare a Message stanza against an XML string. | ||||
|         check_iq             -- Compare an Iq stanza against an XML string. | ||||
|         check_presence       -- Compare a Presence stanza against an XML string. | ||||
|         check_jid            -- Check a JID and its component parts. | ||||
|         check                -- Compare a stanza against an XML string. | ||||
|         stream_start         -- Initialize a dummy XMPP client. | ||||
|         stream_recv          -- Queue data for XMPP client to receive. | ||||
|         stream_make_header   -- Create a stream header. | ||||
|         stream_send_header   -- Check that the given header has been sent. | ||||
|         stream_send_message  -- Check that the XMPP client sent the given | ||||
|                                 Message stanza. | ||||
|         stream_send_iq       -- Check that the XMPP client sent the given | ||||
|                                 Iq stanza. | ||||
|         stream_send_presence -- Check thatt the XMPP client sent the given | ||||
|                                 Presence stanza. | ||||
|         stream_send_stanza   -- Check that the XMPP client sent the given | ||||
|                                 generic stanza. | ||||
|         stream_close         -- Disconnect the XMPP client. | ||||
|         make_header          -- Create a stream header. | ||||
|         send_header          -- Check that the given header has been sent. | ||||
|         send_feature         -- Send a raw XML element. | ||||
|         send                 -- Check that the XMPP client sent the given | ||||
|                                 generic stanza. | ||||
|         recv                 -- Queue data for XMPP client to receive, or | ||||
|                                 verify the data that was received from a | ||||
|                                 live connection. | ||||
|         recv_header          -- Check that a given stream header | ||||
|                                 was received. | ||||
|         recv_feature         -- Check that a given, raw XML element | ||||
|                                 was recveived. | ||||
|         fix_namespaces       -- Add top-level namespace to an XML object. | ||||
|         compare              -- Compare XML objects against each other. | ||||
|     """ | ||||
|  | ||||
|     def runTest(self): | ||||
|         pass | ||||
|  | ||||
|     def parse_xml(self, xml_string): | ||||
|         try: | ||||
|             xml = ET.fromstring(xml_string) | ||||
| @@ -103,11 +104,44 @@ class SleekTest(unittest.TestCase): | ||||
|         """ | ||||
|         return Presence(None, *args, **kwargs) | ||||
|  | ||||
|     def check_jid(self, jid, user=None, domain=None, resource=None, | ||||
|                   bare=None, full=None, string=None): | ||||
|         """ | ||||
|         Verify the components of a JID. | ||||
|  | ||||
|         Arguments: | ||||
|             jid      -- The JID object to test. | ||||
|             user     -- Optional. The user name portion of the JID. | ||||
|             domain   -- Optional. The domain name portion of the JID. | ||||
|             resource -- Optional. The resource portion of the JID. | ||||
|             bare     -- Optional. The bare JID. | ||||
|             full     -- Optional. The full JID. | ||||
|             string   -- Optional. The string version of the JID. | ||||
|         """ | ||||
|         if user is not None: | ||||
|             self.assertEqual(jid.user, user, | ||||
|                     "User does not match: %s" % jid.user) | ||||
|         if domain is not None: | ||||
|             self.assertEqual(jid.domain, domain, | ||||
|                     "Domain does not match: %s" % jid.domain) | ||||
|         if resource is not None: | ||||
|             self.assertEqual(jid.resource, resource, | ||||
|                     "Resource does not match: %s" % jid.resource) | ||||
|         if bare is not None: | ||||
|             self.assertEqual(jid.bare, bare, | ||||
|                     "Bare JID does not match: %s" % jid.bare) | ||||
|         if full is not None: | ||||
|             self.assertEqual(jid.full, full, | ||||
|                     "Full JID does not match: %s" % jid.full) | ||||
|         if string is not None: | ||||
|             self.assertEqual(str(jid), string, | ||||
|                     "String does not match: %s" % str(jid)) | ||||
|  | ||||
|     # ------------------------------------------------------------------ | ||||
|     # Methods for comparing stanza objects to XML strings | ||||
|  | ||||
|     def check_stanza(self, stanza_class, stanza, xml_string, | ||||
|                      defaults=None, use_values=True): | ||||
|     def check(self, stanza, xml_string, | ||||
|               defaults=None, use_values=True): | ||||
|         """ | ||||
|         Create and compare several stanza objects to a correct XML string. | ||||
|  | ||||
| @@ -126,7 +160,6 @@ class SleekTest(unittest.TestCase): | ||||
|         must take into account any extra elements that are included by default. | ||||
|  | ||||
|         Arguments: | ||||
|             stanza_class -- The class of the stanza being tested. | ||||
|             stanza       -- The stanza object to test. | ||||
|             xml_string   -- A string version of the correct XML expected. | ||||
|             defaults     -- A list of stanza interfaces that have default | ||||
| @@ -137,6 +170,7 @@ class SleekTest(unittest.TestCase): | ||||
|                             setStanzaValues() should be used. Defaults to | ||||
|                             True. | ||||
|         """ | ||||
|         stanza_class = stanza.__class__ | ||||
|         xml = self.parse_xml(xml_string) | ||||
|  | ||||
|         # Ensure that top level namespaces are used, even if they | ||||
| @@ -153,7 +187,11 @@ class SleekTest(unittest.TestCase): | ||||
|             # so that they will compare correctly. | ||||
|             default_stanza = stanza_class() | ||||
|             if defaults is None: | ||||
|                 defaults = [] | ||||
|                 known_defaults = { | ||||
|                     Message: ['type'], | ||||
|                     Presence: ['priority'] | ||||
|                 } | ||||
|                 defaults = known_defaults.get(stanza_class, []) | ||||
|             for interface in defaults: | ||||
|                 stanza[interface] = stanza[interface] | ||||
|                 stanza2[interface] = stanza2[interface] | ||||
| @@ -184,62 +222,6 @@ class SleekTest(unittest.TestCase): | ||||
|  | ||||
|         self.failUnless(result, debug) | ||||
|  | ||||
|     def check_message(self, msg, xml_string, use_values=True): | ||||
|         """ | ||||
|         Create and compare several message stanza objects to a | ||||
|         correct XML string. | ||||
|  | ||||
|         If use_values is False, the test using getStanzaValues() and | ||||
|         setStanzaValues() will not be used. | ||||
|  | ||||
|         Arguments: | ||||
|             msg        -- The Message stanza object to check. | ||||
|             xml_string -- The XML contents to compare against. | ||||
|             use_values -- Indicates if the test using getStanzaValues | ||||
|                           and setStanzaValues should be used. Defaults | ||||
|                           to True. | ||||
|         """ | ||||
|  | ||||
|         return self.check_stanza(Message, msg, xml_string, | ||||
|                                  defaults=['type'], | ||||
|                                  use_values=use_values) | ||||
|  | ||||
|     def check_iq(self, iq, xml_string, use_values=True): | ||||
|         """ | ||||
|         Create and compare several iq stanza objects to a | ||||
|         correct XML string. | ||||
|  | ||||
|         If use_values is False, the test using getStanzaValues() and | ||||
|         setStanzaValues() will not be used. | ||||
|  | ||||
|         Arguments: | ||||
|             iq         -- The Iq stanza object to check. | ||||
|             xml_string -- The XML contents to compare against. | ||||
|             use_values -- Indicates if the test using getStanzaValues | ||||
|                           and setStanzaValues should be used. Defaults | ||||
|                           to True. | ||||
|         """ | ||||
|         return self.check_stanza(Iq, iq, xml_string, use_values=use_values) | ||||
|  | ||||
|     def check_presence(self, pres, xml_string, use_values=True): | ||||
|         """ | ||||
|         Create and compare several presence stanza objects to a | ||||
|         correct XML string. | ||||
|  | ||||
|         If use_values is False, the test using getStanzaValues() and | ||||
|         setStanzaValues() will not be used. | ||||
|  | ||||
|         Arguments: | ||||
|             iq         -- The Iq stanza object to check. | ||||
|             xml_string -- The XML contents to compare against. | ||||
|             use_values -- Indicates if the test using getStanzaValues | ||||
|                           and setStanzaValues should be used. Defaults | ||||
|                           to True. | ||||
|         """ | ||||
|         return self.check_stanza(Presence, pres, xml_string, | ||||
|                                  defaults=['priority'], | ||||
|                                  use_values=use_values) | ||||
|  | ||||
|     # ------------------------------------------------------------------ | ||||
|     # Methods for simulating stanza streams. | ||||
|  | ||||
| @@ -267,7 +249,6 @@ class SleekTest(unittest.TestCase): | ||||
|             port     -- The port to use when connecting to the server. | ||||
|                         Defaults to 5222. | ||||
|         """ | ||||
|  | ||||
|         if mode == 'client': | ||||
|             self.xmpp = ClientXMPP(jid, password) | ||||
|         elif mode == 'component': | ||||
| @@ -294,6 +275,7 @@ class SleekTest(unittest.TestCase): | ||||
|         else: | ||||
|             raise ValueError("Unknown socket type.") | ||||
|  | ||||
|         self.xmpp.register_plugins() | ||||
|         self.xmpp.process(threaded=True) | ||||
|         if skip: | ||||
|             # Clear startup stanzas | ||||
| @@ -301,13 +283,13 @@ class SleekTest(unittest.TestCase): | ||||
|             if mode == 'component': | ||||
|                 self.xmpp.socket.next_sent(timeout=1) | ||||
|  | ||||
|     def stream_make_header(self, sto='', | ||||
|                                  sfrom='', | ||||
|                                  sid='', | ||||
|                                  stream_ns="http://etherx.jabber.org/streams", | ||||
|                                  default_ns="jabber:client", | ||||
|                                  version="1.0", | ||||
|                                  xml_header=True): | ||||
|     def make_header(self, sto='', | ||||
|                           sfrom='', | ||||
|                           sid='', | ||||
|                           stream_ns="http://etherx.jabber.org/streams", | ||||
|                           default_ns="jabber:client", | ||||
|                           version="1.0", | ||||
|                           xml_header=True): | ||||
|         """ | ||||
|         Create a stream header to be received by the test XMPP agent. | ||||
|  | ||||
| @@ -338,8 +320,8 @@ class SleekTest(unittest.TestCase): | ||||
|         parts.append('xmlns="%s"' % default_ns) | ||||
|         return header % ' '.join(parts) | ||||
|  | ||||
|     def stream_recv(self, data, stanza_class=StanzaBase, defaults=[], | ||||
|                     use_values=True, timeout=1): | ||||
|     def recv(self, data, stanza_class=StanzaBase, defaults=[], | ||||
|              use_values=True, timeout=1): | ||||
|         """ | ||||
|         Pass data to the dummy XMPP client as if it came from an XMPP server. | ||||
|  | ||||
| @@ -366,7 +348,7 @@ class SleekTest(unittest.TestCase): | ||||
|             if recv_data is None: | ||||
|                 return False | ||||
|             stanza = stanza_class(xml=self.parse_xml(recv_data)) | ||||
|             return self.check_stanza(stanza_class, stanza, data, | ||||
|             return self.check(stanza_class, stanza, data, | ||||
|                                      defaults=defaults, | ||||
|                                      use_values=use_values) | ||||
|         else: | ||||
| @@ -374,14 +356,14 @@ class SleekTest(unittest.TestCase): | ||||
|             data = str(data) | ||||
|             self.xmpp.socket.recv_data(data) | ||||
|  | ||||
|     def stream_recv_header(self, sto='', | ||||
|                                  sfrom='', | ||||
|                                  sid='', | ||||
|                                  stream_ns="http://etherx.jabber.org/streams", | ||||
|                                  default_ns="jabber:client", | ||||
|                                  version="1.0", | ||||
|                                  xml_header=False, | ||||
|                                  timeout=1): | ||||
|     def recv_header(self, sto='', | ||||
|                           sfrom='', | ||||
|                           sid='', | ||||
|                           stream_ns="http://etherx.jabber.org/streams", | ||||
|                           default_ns="jabber:client", | ||||
|                           version="1.0", | ||||
|                           xml_header=False, | ||||
|                           timeout=1): | ||||
|         """ | ||||
|         Check that a given stream header was received. | ||||
|  | ||||
| @@ -397,11 +379,11 @@ class SleekTest(unittest.TestCase): | ||||
|             timeout    -- Length of time to wait in seconds for a | ||||
|                           response. | ||||
|         """ | ||||
|         header = self.stream_make_header(sto, sfrom, sid, | ||||
|                                          stream_ns=stream_ns, | ||||
|                                          default_ns=default_ns, | ||||
|                                          version=version, | ||||
|                                          xml_header=xml_header) | ||||
|         header = self.make_header(sto, sfrom, sid, | ||||
|                                   stream_ns=stream_ns, | ||||
|                                   default_ns=default_ns, | ||||
|                                   version=version, | ||||
|                                   xml_header=xml_header) | ||||
|         recv_header = self.xmpp.socket.next_recv(timeout) | ||||
|         if recv_header is None: | ||||
|             raise ValueError("Socket did not return data.") | ||||
| @@ -441,9 +423,8 @@ class SleekTest(unittest.TestCase): | ||||
|             "Stream headers do not match:\nDesired:\n%s\nReceived:\n%s" % ( | ||||
|                 '%s %s' % (xml.tag, xml.attrib), | ||||
|                 '%s %s' % (recv_xml.tag, recv_xml.attrib))) | ||||
|                 #tostring(xml), tostring(recv_xml)))#recv_header)) | ||||
|  | ||||
|     def stream_recv_feature(self, data, use_values=True, timeout=1): | ||||
|     def recv_feature(self, data, use_values=True, timeout=1): | ||||
|         """ | ||||
|         """ | ||||
|         if self.xmpp.socket.is_live: | ||||
| @@ -463,39 +444,14 @@ class SleekTest(unittest.TestCase): | ||||
|             data = str(data) | ||||
|             self.xmpp.socket.recv_data(data) | ||||
|  | ||||
|  | ||||
|  | ||||
|     def stream_recv_message(self, data, use_values=True, timeout=1): | ||||
|         """ | ||||
|         """ | ||||
|         return self.stream_recv(data, stanza_class=Message, | ||||
|                                       defaults=['type'], | ||||
|                                       use_values=use_values, | ||||
|                                       timeout=timeout) | ||||
|  | ||||
|     def stream_recv_iq(self, data, use_values=True, timeout=1): | ||||
|         """ | ||||
|         """ | ||||
|         return self.stream_recv(data, stanza_class=Iq, | ||||
|                                       use_values=use_values, | ||||
|                                       timeout=timeout) | ||||
|  | ||||
|     def stream_recv_presence(self, data, use_values=True, timeout=1): | ||||
|         """ | ||||
|         """ | ||||
|         return self.stream_recv(data, stanza_class=Presence, | ||||
|                                       defaults=['priority'], | ||||
|                                       use_values=use_values, | ||||
|                                       timeout=timeout) | ||||
|  | ||||
|     def stream_send_header(self, sto='', | ||||
|                                  sfrom='', | ||||
|                                  sid='', | ||||
|                                  stream_ns="http://etherx.jabber.org/streams", | ||||
|                                  default_ns="jabber:client", | ||||
|                                  version="1.0", | ||||
|                                  xml_header=False, | ||||
|                                  timeout=1): | ||||
|     def send_header(self, sto='', | ||||
|                           sfrom='', | ||||
|                           sid='', | ||||
|                           stream_ns="http://etherx.jabber.org/streams", | ||||
|                           default_ns="jabber:client", | ||||
|                           version="1.0", | ||||
|                           xml_header=False, | ||||
|                           timeout=1): | ||||
|         """ | ||||
|         Check that a given stream header was sent. | ||||
|  | ||||
| @@ -511,11 +467,11 @@ class SleekTest(unittest.TestCase): | ||||
|             timeout    -- Length of time to wait in seconds for a | ||||
|                           response. | ||||
|         """ | ||||
|         header = self.stream_make_header(sto, sfrom, sid, | ||||
|                                          stream_ns=stream_ns, | ||||
|                                          default_ns=default_ns, | ||||
|                                          version=version, | ||||
|                                          xml_header=xml_header) | ||||
|         header = self.make_header(sto, sfrom, sid, | ||||
|                                   stream_ns=stream_ns, | ||||
|                                   default_ns=default_ns, | ||||
|                                   version=version, | ||||
|                                   xml_header=xml_header) | ||||
|         sent_header = self.xmpp.socket.next_sent(timeout) | ||||
|         if sent_header is None: | ||||
|             raise ValueError("Socket did not return data.") | ||||
| @@ -533,7 +489,7 @@ class SleekTest(unittest.TestCase): | ||||
|             "Stream headers do not match:\nDesired:\n%s\nSent:\n%s" % ( | ||||
|                 header, sent_header)) | ||||
|  | ||||
|     def stream_send_feature(self, data, use_values=True, timeout=1): | ||||
|     def send_feature(self, data, use_values=True, timeout=1): | ||||
|         """ | ||||
|         """ | ||||
|         sent_data = self.xmpp.socket.next_sent(timeout) | ||||
| @@ -545,13 +501,13 @@ class SleekTest(unittest.TestCase): | ||||
|             "Features do not match.\nDesired:\n%s\nSent:\n%s" % ( | ||||
|                 tostring(xml), tostring(sent_xml))) | ||||
|  | ||||
|     def stream_send_stanza(self, stanza_class, data, defaults=None, | ||||
|                            use_values=True, timeout=.1): | ||||
|     def send(self, data, defaults=None, | ||||
|              use_values=True, timeout=.1): | ||||
|         """ | ||||
|         Check that the XMPP client sent the given stanza XML. | ||||
|  | ||||
|         Extracts the next sent stanza and compares it with the given | ||||
|         XML using check_stanza. | ||||
|         XML using check. | ||||
|  | ||||
|         Arguments: | ||||
|             stanza_class -- The class of the sent stanza object. | ||||
| @@ -563,70 +519,15 @@ class SleekTest(unittest.TestCase): | ||||
|             timeout      -- Time in seconds to wait for a stanza before | ||||
|                             failing the check. | ||||
|         """ | ||||
|         if isintance(data, str): | ||||
|             data = stanza_class(xml=self.parse_xml(data)) | ||||
|         if isinstance(data, str): | ||||
|             xml = self.parse_xml(data) | ||||
|             self.fix_namespaces(xml, 'jabber:client') | ||||
|             data = self.xmpp._build_stanza(xml, 'jabber:client') | ||||
|         sent = self.xmpp.socket.next_sent(timeout) | ||||
|         self.check_stanza(stanza_class, data, sent, | ||||
|         self.check(data, sent, | ||||
|                           defaults=defaults, | ||||
|                           use_values=use_values) | ||||
|  | ||||
|     def stream_send_message(self, data, use_values=True, timeout=.1): | ||||
|         """ | ||||
|         Check that the XMPP client sent the given stanza XML. | ||||
|  | ||||
|         Extracts the next sent stanza and compares it with the given | ||||
|         XML using check_message. | ||||
|  | ||||
|         Arguments: | ||||
|             data       -- The XML string of the expected Message stanza, | ||||
|                           or an equivalent stanza object. | ||||
|             use_values -- Modifies the type of tests used by check_message. | ||||
|             timeout    -- Time in seconds to wait for a stanza before | ||||
|                           failing the check. | ||||
|         """ | ||||
|         if isinstance(data, str): | ||||
|             data = self.Message(xml=self.parse_xml(data)) | ||||
|         sent = self.xmpp.socket.next_sent(timeout) | ||||
|         self.check_message(data, sent, use_values) | ||||
|  | ||||
|     def stream_send_iq(self, data, use_values=True, timeout=.1): | ||||
|         """ | ||||
|         Check that the XMPP client sent the given stanza XML. | ||||
|  | ||||
|         Extracts the next sent stanza and compares it with the given | ||||
|         XML using check_iq. | ||||
|  | ||||
|         Arguments: | ||||
|             data       -- The XML string of the expected Iq stanza, | ||||
|                           or an equivalent stanza object. | ||||
|             use_values -- Modifies the type of tests used by check_iq. | ||||
|             timeout    -- Time in seconds to wait for a stanza before | ||||
|                           failing the check. | ||||
|         """ | ||||
|         if isinstance(data, str): | ||||
|             data = self.Iq(xml=self.parse_xml(data)) | ||||
|         sent = self.xmpp.socket.next_sent(timeout) | ||||
|         self.check_iq(data, sent, use_values) | ||||
|  | ||||
|     def stream_send_presence(self, data, use_values=True, timeout=.1): | ||||
|         """ | ||||
|         Check that the XMPP client sent the given stanza XML. | ||||
|  | ||||
|         Extracts the next sent stanza and compares it with the given | ||||
|         XML using check_presence. | ||||
|  | ||||
|         Arguments: | ||||
|             data       -- The XML string of the expected Presence stanza, | ||||
|                           or an equivalent stanza object. | ||||
|             use_values -- Modifies the type of tests used by check_presence. | ||||
|             timeout    -- Time in seconds to wait for a stanza before | ||||
|                           failing the check. | ||||
|         """ | ||||
|         if isinstance(data, str): | ||||
|             data = self.Presence(xml=self.parse_xml(data)) | ||||
|         sent = self.xmpp.socket.next_sent(timeout) | ||||
|         self.check_presence(data, sent, use_values) | ||||
|  | ||||
|     def stream_close(self): | ||||
|         """ | ||||
|         Disconnect the dummy XMPP client. | ||||
|   | ||||
							
								
								
									
										96
									
								
								sleekxmpp/thirdparty/statemachine.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										96
									
								
								sleekxmpp/thirdparty/statemachine.py
									
									
									
									
										vendored
									
									
								
							| @@ -21,7 +21,7 @@ class StateMachine(object): | ||||
|         self.addStates(states) | ||||
|         self.__default_state = self.__states[0] | ||||
|         self.__current_state = self.__default_state | ||||
|      | ||||
|  | ||||
|     def addStates(self, states): | ||||
|         self.lock.acquire() | ||||
|         try: | ||||
| @@ -30,19 +30,19 @@ class StateMachine(object): | ||||
|                     raise IndexError("The state '%s' is already in the StateMachine." % state) | ||||
|                 self.__states.append(state) | ||||
|         finally: self.lock.release() | ||||
|      | ||||
|      | ||||
|  | ||||
|  | ||||
|     def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}): | ||||
|         ''' | ||||
|         Transition from the given `from_state` to the given `to_state`.   | ||||
|         Transition from the given `from_state` to the given `to_state`. | ||||
|         This method will return `True` if the state machine is now in `to_state`.  It | ||||
|         will return `False` if a timeout occurred the transition did not occur.   | ||||
|         If `wait` is 0 (the default,) this method returns immediately if the state machine  | ||||
|         will return `False` if a timeout occurred the transition did not occur. | ||||
|         If `wait` is 0 (the default,) this method returns immediately if the state machine | ||||
|         is not in `from_state`. | ||||
|  | ||||
|         If you want the thread to block and transition once the state machine to enters | ||||
|         `from_state`, set `wait` to a non-negative value.  Note there is no 'block  | ||||
|         indefinitely' flag since this leads to deadlock.  If you want to wait indefinitely,  | ||||
|         `from_state`, set `wait` to a non-negative value.  Note there is no 'block | ||||
|         indefinitely' flag since this leads to deadlock.  If you want to wait indefinitely, | ||||
|         choose a reasonable value for `wait` (e.g. 20 seconds) and do so in a while loop like so: | ||||
|  | ||||
|         :: | ||||
| @@ -60,42 +60,42 @@ class StateMachine(object): | ||||
|         True value or if an exception is thrown, the transition will not occur.  Any thrown | ||||
|         exception is not caught by the state machine and is the caller's responsibility to handle. | ||||
|         If `func` completes normally, this method will return the value returned by `func.`  If | ||||
|         values for `args` and `kwargs` are provided, they are expanded and passed like so:   | ||||
|         values for `args` and `kwargs` are provided, they are expanded and passed like so: | ||||
|         `func( *args, **kwargs )`. | ||||
|         ''' | ||||
|  | ||||
|         return self.transition_any((from_state,), to_state, wait=wait,  | ||||
|         return self.transition_any((from_state,), to_state, wait=wait, | ||||
|                                     func=func, args=args, kwargs=kwargs) | ||||
|      | ||||
|      | ||||
|  | ||||
|  | ||||
|     def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kwargs={}): | ||||
|         ''' | ||||
|         Transition from any of the given `from_states` to the given `to_state`. | ||||
|         ''' | ||||
|  | ||||
|         if not (isinstance(from_states,tuple) or isinstance(from_states,list)):  | ||||
|         if not (isinstance(from_states,tuple) or isinstance(from_states,list)): | ||||
|                 raise ValueError("from_states should be a list or tuple") | ||||
|  | ||||
|         for state in from_states: | ||||
|             if not state in self.__states:  | ||||
|             if not state in self.__states: | ||||
|                 raise ValueError("StateMachine does not contain from_state %s." % state) | ||||
|         if not to_state in self.__states:  | ||||
|         if not to_state in self.__states: | ||||
|             raise ValueError("StateMachine does not contain to_state %s." % to_state) | ||||
|  | ||||
|         start = time.time() | ||||
|         while not self.lock.acquire(False): | ||||
|             time.sleep(.001) | ||||
|             if (start + wait - time.time()) <= 0.0: | ||||
|                 logging.debug("Could not acquire lock") | ||||
|                 log.debug("Could not acquire lock") | ||||
|                 return False | ||||
|  | ||||
|         while not self.__current_state in from_states: | ||||
|             # detect timeout: | ||||
|             remainder = start + wait - time.time() | ||||
|             if remainder > 0:  | ||||
|             if remainder > 0: | ||||
|                 self.notifier.wait(remainder) | ||||
|             else:  | ||||
|                 logging.debug("State was not ready") | ||||
|             else: | ||||
|                 log.debug("State was not ready") | ||||
|                 self.lock.release() | ||||
|                 return False | ||||
|  | ||||
| @@ -105,9 +105,9 @@ class StateMachine(object): | ||||
|                 # Note that func might throw an exception, but that's OK, it aborts the transition | ||||
|                 return_val = func(*args,**kwargs) if func is not None else True | ||||
|  | ||||
|                 # some 'false' value returned from func,  | ||||
|                 # some 'false' value returned from func, | ||||
|                 # indicating that transition should not occur: | ||||
|                 if not return_val: return return_val  | ||||
|                 if not return_val: return return_val | ||||
|  | ||||
|                 log.debug(' ==== TRANSITION %s -> %s', self.__current_state, to_state) | ||||
|                 self._set_state(to_state) | ||||
| @@ -115,7 +115,7 @@ class StateMachine(object): | ||||
|             else: | ||||
|                 log.error("StateMachine bug!!  The lock should ensure this doesn't happen!") | ||||
|                 return False | ||||
|         finally:  | ||||
|         finally: | ||||
|             self.notifier.set() # notify any waiting threads that the state has changed. | ||||
|             self.notifier.clear() | ||||
|             self.lock.release() | ||||
| @@ -125,13 +125,13 @@ class StateMachine(object): | ||||
|         ''' | ||||
|         Use the state machine as a context manager.  The transition occurs on /exit/ from | ||||
|         the `with` context, so long as no exception is thrown.  For example: | ||||
|          | ||||
|  | ||||
|         :: | ||||
|  | ||||
|             with state_machine.transition_ctx('one','two', wait=5) as locked: | ||||
|                 if locked: | ||||
|                     # the state machine is currently locked in state 'one', and will  | ||||
|                     # transition to 'two' when the 'with' statement ends, so long as  | ||||
|                     # the state machine is currently locked in state 'one', and will | ||||
|                     # transition to 'two' when the 'with' statement ends, so long as | ||||
|                     # no exception is thrown. | ||||
|                     print 'Currently locked in state one: %s' % state_machine['one'] | ||||
|  | ||||
| @@ -142,20 +142,20 @@ class StateMachine(object): | ||||
|             print 'Since no exception was thrown, we are now in state "two": %s' % state_machine['two'] | ||||
|  | ||||
|  | ||||
|         The other main difference between this method and `transition()` is that the  | ||||
|         state machine is locked for the duration of the `with` statement.  Normally,  | ||||
|         after a `transition()` occurs, the state machine is immediately unlocked and  | ||||
|         The other main difference between this method and `transition()` is that the | ||||
|         state machine is locked for the duration of the `with` statement.  Normally, | ||||
|         after a `transition()` occurs, the state machine is immediately unlocked and | ||||
|         available to another thread to call `transition()` again. | ||||
|         ''' | ||||
|  | ||||
|         if not from_state in self.__states:  | ||||
|         if not from_state in self.__states: | ||||
|             raise ValueError("StateMachine does not contain from_state %s." % from_state) | ||||
|         if not to_state in self.__states:  | ||||
|         if not to_state in self.__states: | ||||
|             raise ValueError("StateMachine does not contain to_state %s." % to_state) | ||||
|  | ||||
|         return _StateCtx(self, from_state, to_state, wait) | ||||
|  | ||||
|      | ||||
|  | ||||
|     def ensure(self, state, wait=0.0, block_on_transition=False): | ||||
|         ''' | ||||
|         Ensure the state machine is currently in `state`, or wait until it enters `state`. | ||||
| @@ -168,24 +168,24 @@ class StateMachine(object): | ||||
|         Ensure we are currently in one of the given `states` or wait until | ||||
|         we enter one of those states. | ||||
|  | ||||
|         Note that due to the nature of the function, you cannot guarantee that  | ||||
|         Note that due to the nature of the function, you cannot guarantee that | ||||
|         the entirety of some operation completes while you remain in a given | ||||
|         state.  That would require acquiring and holding a lock, which  | ||||
|         state.  That would require acquiring and holding a lock, which | ||||
|         would mean no other threads could do the same.  (You'd essentially | ||||
|         be serializing all of the threads that are 'ensuring' their tasks | ||||
|         occurred in some state.   | ||||
|         occurred in some state. | ||||
|         ''' | ||||
|         if not (isinstance(states,tuple) or isinstance(states,list)):  | ||||
|         if not (isinstance(states,tuple) or isinstance(states,list)): | ||||
|             raise ValueError('states arg should be a tuple or list') | ||||
|  | ||||
|         for state in states: | ||||
|             if not state in self.__states:  | ||||
|             if not state in self.__states: | ||||
|                 raise ValueError("StateMachine does not contain state '%s'" % state) | ||||
|  | ||||
|         # if we're in the middle of a transition, determine whether we should  | ||||
|         # 'fall back' to the 'current' state, or wait for the new state, in order to  | ||||
|         # if we're in the middle of a transition, determine whether we should | ||||
|         # 'fall back' to the 'current' state, or wait for the new state, in order to | ||||
|         # avoid an operation occurring in the wrong state. | ||||
|         # TODO another option would be an ensure_ctx that uses a semaphore to allow  | ||||
|         # TODO another option would be an ensure_ctx that uses a semaphore to allow | ||||
|         # threads to indicate they want to remain in a particular state. | ||||
|  | ||||
|         # will return immediately if no transition is in process. | ||||
| @@ -196,16 +196,16 @@ class StateMachine(object): | ||||
|             else: self.notifier.wait() | ||||
|  | ||||
|         start = time.time() | ||||
|         while not self.__current_state in states:  | ||||
|         while not self.__current_state in states: | ||||
|             # detect timeout: | ||||
|             remainder = start + wait - time.time() | ||||
|             if remainder > 0: self.notifier.wait(remainder) | ||||
|             else: return False | ||||
|         return True | ||||
|  | ||||
|      | ||||
|  | ||||
|     def reset(self): | ||||
|         # TODO need to lock before calling this?  | ||||
|         # TODO need to lock before calling this? | ||||
|         self.transition(self.__current_state, self.__default_state) | ||||
|  | ||||
|  | ||||
| @@ -231,7 +231,7 @@ class StateMachine(object): | ||||
|     def __str__(self): | ||||
|         return "".join(("StateMachine(", ','.join(self.__states), "): ", self.__current_state)) | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
| class _StateCtx: | ||||
|  | ||||
| @@ -244,28 +244,28 @@ class _StateCtx: | ||||
|  | ||||
|     def __enter__(self): | ||||
|         start = time.time() | ||||
|         while not self.state_machine[self.from_state] or not self.state_machine.lock.acquire(False):  | ||||
|         while not self.state_machine[self.from_state] or not self.state_machine.lock.acquire(False): | ||||
|             # detect timeout: | ||||
|             remainder = start + self.wait - time.time() | ||||
|             if remainder > 0: self.state_machine.notifier.wait(remainder) | ||||
|             else:  | ||||
|             else: | ||||
|                 log.debug('StateMachine timeout while waiting for state: %s', self.from_state) | ||||
|                 return False | ||||
|  | ||||
|         self._locked = True # lock has been acquired at this point | ||||
|         self.state_machine.notifier.clear() | ||||
|         log.debug('StateMachine entered context in state: %s',  | ||||
|         log.debug('StateMachine entered context in state: %s', | ||||
|                 self.state_machine.current_state()) | ||||
|         return True | ||||
|  | ||||
|     def __exit__(self, exc_type, exc_val, exc_tb): | ||||
|         if exc_val is not None: | ||||
|             log.exception("StateMachine exception in context, remaining in state: %s\n%s:%s",  | ||||
|             log.exception("StateMachine exception in context, remaining in state: %s\n%s:%s", | ||||
|                 self.state_machine.current_state(), exc_type.__name__, exc_val) | ||||
|  | ||||
|         if self._locked: | ||||
|             if exc_val is None: | ||||
|                 log.debug(' ==== TRANSITION %s -> %s',  | ||||
|                 log.debug(' ==== TRANSITION %s -> %s', | ||||
|                         self.state_machine.current_state(), self.to_state) | ||||
|                 self.state_machine._set_state(self.to_state) | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import StanzaBase, RESPONSE_TIMEOUT | ||||
| from sleekxmpp.xmlstream.handler.base import BaseHandler | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Waiter(BaseHandler): | ||||
|  | ||||
|     """ | ||||
| @@ -85,7 +88,7 @@ class Waiter(BaseHandler): | ||||
|             stanza = self._payload.get(True, timeout) | ||||
|         except queue.Empty: | ||||
|             stanza = False | ||||
|             logging.warning("Timed out waiting for %s" % self.name) | ||||
|             log.warning("Timed out waiting for %s" % self.name) | ||||
|         self.stream.removeHandler(self.name) | ||||
|         return stanza | ||||
|  | ||||
|   | ||||
| @@ -57,7 +57,7 @@ class JID(object): | ||||
|                     full, or bare. | ||||
|         """ | ||||
|         if name == 'resource': | ||||
|             if self._resource is None: | ||||
|             if self._resource is None and '/' in self._jid: | ||||
|                 self._resource = self._jid.split('/', 1)[-1] | ||||
|             return self._resource or "" | ||||
|         elif name == 'user': | ||||
| @@ -94,21 +94,15 @@ class JID(object): | ||||
|         elif name in ('server', 'domain', 'host'): | ||||
|             self.domain = value | ||||
|         elif name in ('full', 'jid'): | ||||
|             if '@' not in value: | ||||
|                 if '/' in value: | ||||
|                     d, r = value.split('/', 1) | ||||
|                     object.__setattr__(self, "_resource", r) | ||||
|                 else: | ||||
|                     d = value | ||||
|                 object.__setattr__(self, "_domain", d) | ||||
|             else: | ||||
|                 self.reset(value) | ||||
|             self.reset(value) | ||||
|             self.regenerate() | ||||
|         elif name == 'bare': | ||||
|             if '@' in value: | ||||
|                 u, d = value.split('@', 1) | ||||
|                 object.__setattr__(self, "_user", u) | ||||
|                 object.__setattr__(self, "_domain", d) | ||||
|             else: | ||||
|                 object.__setattr__(self, "_user", '') | ||||
|                 object.__setattr__(self, "_domain", value) | ||||
|             self.regenerate() | ||||
|         else: | ||||
|   | ||||
| @@ -6,6 +6,8 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from xml.parsers.expat import ExpatError | ||||
|  | ||||
| from sleekxmpp.xmlstream.stanzabase import ET | ||||
| @@ -18,6 +20,9 @@ from sleekxmpp.xmlstream.matcher.base import MatcherBase | ||||
| IGNORE_NS = False | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class MatchXMLMask(MatcherBase): | ||||
|  | ||||
|     """ | ||||
| @@ -97,8 +102,7 @@ class MatchXMLMask(MatcherBase): | ||||
|             try: | ||||
|                 mask = ET.fromstring(mask) | ||||
|             except ExpatError: | ||||
|                 logging.log(logging.WARNING, | ||||
|                             "Expat error: %s\nIn parsing: %s" % ('', mask)) | ||||
|                 log.warning("Expat error: %s\nIn parsing: %s" % ('', mask)) | ||||
|  | ||||
|         if not use_ns: | ||||
|             # Compare the element without using namespaces. | ||||
|   | ||||
| @@ -15,6 +15,9 @@ except ImportError: | ||||
|     import Queue as queue | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Task(object): | ||||
|  | ||||
|     """ | ||||
| @@ -146,6 +149,8 @@ class Scheduler(object): | ||||
|                         if wait <= 0.0: | ||||
|                             newtask = self.addq.get(False) | ||||
|                         else: | ||||
|                             if wait >= 3.0: | ||||
|                                 wait = 3.0 | ||||
|                             newtask = self.addq.get(True, wait) | ||||
|                     except queue.Empty: | ||||
|                         cleanup = [] | ||||
| @@ -168,13 +173,13 @@ class Scheduler(object): | ||||
|         except KeyboardInterrupt: | ||||
|             self.run = False | ||||
|             if self.parentstop is not None: | ||||
|                 logging.debug("stopping parent") | ||||
|                 log.debug("stopping parent") | ||||
|                 self.parentstop.set() | ||||
|         except SystemExit: | ||||
|             self.run = False | ||||
|             if self.parentstop is not None: | ||||
|                 self.parentstop.set() | ||||
|         logging.debug("Quitting Scheduler thread") | ||||
|         log.debug("Quitting Scheduler thread") | ||||
|         if self.parentqueue is not None: | ||||
|             self.parentqueue.put(('quit', None, None)) | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import JID | ||||
| from sleekxmpp.xmlstream.tostring import tostring | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| # Used to check if an argument is an XML object. | ||||
| XML_TYPE = type(ET.Element('xml')) | ||||
|  | ||||
| @@ -1140,7 +1143,7 @@ class StanzaBase(ElementBase): | ||||
|  | ||||
|         Meant to be overridden. | ||||
|         """ | ||||
|         logging.exception('Error handling {%s}%s stanza' % (self.namespace, | ||||
|         log.exception('Error handling {%s}%s stanza' % (self.namespace, | ||||
|                                                             self.name)) | ||||
|  | ||||
|     def send(self): | ||||
|   | ||||
| @@ -68,9 +68,6 @@ def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''): | ||||
|             for child in xml.getchildren(): | ||||
|                 output.append(tostring(child, tag_xmlns, stanza_ns, stream)) | ||||
|         output.append(u"</%s>" % tag_name) | ||||
|         if xml.tail: | ||||
|             # If there is additional text after the element. | ||||
|             output.append(xml_escape(xml.tail)) | ||||
|     elif xml.text: | ||||
|         # If we only have text content. | ||||
|         output.append(u">%s</%s>" % (xml_escape(xml.text), tag_name)) | ||||
|   | ||||
| @@ -44,6 +44,9 @@ HANDLER_THREADS = 1 | ||||
| SSL_SUPPORT = True | ||||
|  | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class RestartStream(Exception): | ||||
|     """ | ||||
|     Exception to restart stream processing, including | ||||
| @@ -87,6 +90,8 @@ class XMLStream(object): | ||||
|         send_queue    -- A queue of stanzas to be sent on the stream. | ||||
|         socket        -- The connection to the server. | ||||
|         ssl_support   -- Indicates if a SSL library is available for use. | ||||
|         ssl_version   -- The version of the SSL protocol to use. | ||||
|                          Defaults to ssl.PROTOCOL_TLSv1. | ||||
|         state         -- A state machine for managing the stream's | ||||
|                          connection state. | ||||
|         stream_footer -- The start tag and any attributes for the stream's | ||||
| @@ -155,6 +160,7 @@ class XMLStream(object): | ||||
|         self.sendXML = self.send_xml | ||||
|  | ||||
|         self.ssl_support = SSL_SUPPORT | ||||
|         self.ssl_version = ssl.PROTOCOL_TLSv1 | ||||
|  | ||||
|         self.state = StateMachine(('disconnected', 'connected')) | ||||
|         self.state._set_state('disconnected') | ||||
| @@ -196,8 +202,15 @@ class XMLStream(object): | ||||
|         self.auto_reconnect = True | ||||
|         self.is_client = False | ||||
|  | ||||
|         signal.signal(signal.SIGHUP, self._handle_kill) | ||||
|         signal.signal(signal.SIGTERM, self._handle_kill) # used in Windows | ||||
|         try: | ||||
|             if hasattr(signal, 'SIGHUP'): | ||||
|                 signal.signal(signal.SIGHUP, self._handle_kill) | ||||
|             if hasattr(signal, 'SIGTERM'): | ||||
|                 # Used in Windows | ||||
|                 signal.signal(signal.SIGTERM, self._handle_kill) | ||||
|         except: | ||||
|             log.debug("Can not set interrupt signal handlers. " + \ | ||||
|                           "SleekXMPP is not running from a main thread.") | ||||
|  | ||||
|     def _handle_kill(self, signum, frame): | ||||
|         """ | ||||
| @@ -265,7 +278,7 @@ class XMLStream(object): | ||||
|         self.socket = self.socket_class(Socket.AF_INET, Socket.SOCK_STREAM) | ||||
|         self.socket.settimeout(None) | ||||
|         if self.use_ssl and self.ssl_support: | ||||
|             logging.debug("Socket Wrapped for SSL") | ||||
|             log.debug("Socket Wrapped for SSL") | ||||
|             ssl_socket = ssl.wrap_socket(self.socket) | ||||
|             if hasattr(self.socket, 'socket'): | ||||
|                 # We are using a testing socket, so preserve the top | ||||
| @@ -275,7 +288,7 @@ class XMLStream(object): | ||||
|                 self.socket = ssl_socket | ||||
|  | ||||
|         try: | ||||
|             logging.debug("Connecting to %s:%s" % self.address) | ||||
|             log.debug("Connecting to %s:%s" % self.address) | ||||
|             self.socket.connect(self.address) | ||||
|             self.set_socket(self.socket, ignore=True) | ||||
|             #this event is where you should set your application state | ||||
| @@ -283,7 +296,7 @@ class XMLStream(object): | ||||
|             return True | ||||
|         except Socket.error as serr: | ||||
|             error_msg = "Could not connect to %s:%s. Socket Error #%s: %s" | ||||
|             logging.error(error_msg % (self.address[0], self.address[1], | ||||
|             log.error(error_msg % (self.address[0], self.address[1], | ||||
|                                        serr.errno, serr.strerror)) | ||||
|             time.sleep(1) | ||||
|             return False | ||||
| @@ -328,10 +341,10 @@ class XMLStream(object): | ||||
|         """ | ||||
|         Reset the stream's state and reconnect to the server. | ||||
|         """ | ||||
|         logging.debug("reconnecting...") | ||||
|         log.debug("reconnecting...") | ||||
|         self.state.transition('connected', 'disconnected', wait=2.0, | ||||
|                               func=self._disconnect, args=(True,)) | ||||
|         logging.debug("connecting...") | ||||
|         log.debug("connecting...") | ||||
|         return self.state.transition('disconnected', 'connected', | ||||
|                                      wait=2.0, func=self._connect) | ||||
|  | ||||
| @@ -368,9 +381,10 @@ class XMLStream(object): | ||||
|         to be restarted. | ||||
|         """ | ||||
|         if self.ssl_support: | ||||
|             logging.info("Negotiating TLS") | ||||
|             log.info("Negotiating TLS") | ||||
|             log.info("Using SSL version: %s" % str(self.ssl_version)) | ||||
|             ssl_socket = ssl.wrap_socket(self.socket, | ||||
|                                          ssl_version=ssl.PROTOCOL_TLSv1, | ||||
|                                          ssl_version=self.ssl_version, | ||||
|                                          do_handshake_on_connect=False) | ||||
|             if hasattr(self.socket, 'socket'): | ||||
|                 # We are using a testing socket, so preserve the top | ||||
| @@ -382,7 +396,7 @@ class XMLStream(object): | ||||
|             self.set_socket(self.socket) | ||||
|             return True | ||||
|         else: | ||||
|             logging.warning("Tried to enable TLS, but ssl module not found.") | ||||
|             log.warning("Tried to enable TLS, but ssl module not found.") | ||||
|             return False | ||||
|  | ||||
|     def start_stream_handler(self, xml): | ||||
| @@ -517,6 +531,17 @@ class XMLStream(object): | ||||
|         self.__event_handlers[name] = filter(filter_pointers, | ||||
|                                              self.__event_handlers[name]) | ||||
|  | ||||
|     def event_handled(self, name): | ||||
|         """ | ||||
|         Indicates if an event has any associated handlers. | ||||
|  | ||||
|         Returns the number of registered handlers. | ||||
|  | ||||
|         Arguments: | ||||
|             name -- The name of the event to check. | ||||
|         """ | ||||
|         return len(self.__event_handlers.get(name, [])) | ||||
|  | ||||
|     def event(self, name, data={}, direct=False): | ||||
|         """ | ||||
|         Manually trigger a custom event. | ||||
| @@ -525,13 +550,22 @@ class XMLStream(object): | ||||
|             name     -- The name of the event to trigger. | ||||
|             data     -- Data that will be passed to each event handler. | ||||
|                         Defaults to an empty dictionary. | ||||
|             direct   -- Runs the event directly if True. | ||||
|             direct   -- Runs the event directly if True, skipping the | ||||
|                         event queue. All event handlers will run in the | ||||
|                         same thread. | ||||
|         """ | ||||
|         for handler in self.__event_handlers.get(name, []): | ||||
|             if direct: | ||||
|                 handler[0](copy.copy(data)) | ||||
|                 try: | ||||
|                     handler[0](copy.copy(data)) | ||||
|                 except Exception as e: | ||||
|                     error_msg = 'Error processing event handler: %s' | ||||
|                     log.exception(error_msg % str(handler[0])) | ||||
|                     if hasattr(data, 'exception'): | ||||
|                         data.exception(e) | ||||
|             else: | ||||
|                 self.event_queue.put(('event', handler, copy.copy(data))) | ||||
|  | ||||
|             if handler[2]: | ||||
|                 # If the handler is disposable, we will go ahead and | ||||
|                 # remove it now instead of waiting for it to be | ||||
| @@ -591,7 +625,7 @@ class XMLStream(object): | ||||
|             mask = mask.xml | ||||
|         data = str(data) | ||||
|         if mask is not None: | ||||
|             logging.warning("Use of send mask waiters is deprecated.") | ||||
|             log.warning("Use of send mask waiters is deprecated.") | ||||
|             wait_for = Waiter("SendWait_%s" % self.new_id(), | ||||
|                               MatchXMLMask(mask)) | ||||
|             self.register_handler(wait_for) | ||||
| @@ -648,7 +682,7 @@ class XMLStream(object): | ||||
|             self.__thread[name].start() | ||||
|  | ||||
|         for t in range(0, HANDLER_THREADS): | ||||
|             logging.debug("Starting HANDLER THREAD") | ||||
|             log.debug("Starting HANDLER THREAD") | ||||
|             start_thread('stream_event_handler_%s' % t, self._event_runner) | ||||
|  | ||||
|         start_thread('send_thread', self._send_thread) | ||||
| @@ -686,16 +720,16 @@ class XMLStream(object): | ||||
|                     if self.is_client: | ||||
|                         self.send_raw(self.stream_header) | ||||
|             except KeyboardInterrupt: | ||||
|                 logging.debug("Keyboard Escape Detected in _process") | ||||
|                 log.debug("Keyboard Escape Detected in _process") | ||||
|                 self.stop.set() | ||||
|             except SystemExit: | ||||
|                 logging.debug("SystemExit in _process") | ||||
|                 log.debug("SystemExit in _process") | ||||
|                 self.stop.set() | ||||
|             except Socket.error: | ||||
|                 logging.exception('Socket Error') | ||||
|                 log.exception('Socket Error') | ||||
|             except: | ||||
|                 if not self.stop.isSet(): | ||||
|                     logging.exception('Connection error.') | ||||
|                     log.exception('Connection error.') | ||||
|             if not self.stop.isSet() and self.auto_reconnect: | ||||
|                 self.reconnect() | ||||
|             else: | ||||
| @@ -725,7 +759,7 @@ class XMLStream(object): | ||||
|                 if depth == 0: | ||||
|                     # The stream's root element has closed, | ||||
|                     # terminating the stream. | ||||
|                     logging.debug("End of stream recieved") | ||||
|                     log.debug("End of stream recieved") | ||||
|                     self.stream_end_event.set() | ||||
|                     return False | ||||
|                 elif depth == 1: | ||||
| @@ -739,7 +773,29 @@ class XMLStream(object): | ||||
|                         # Keep the root element empty of children to | ||||
|                         # save on memory use. | ||||
|                         root.clear() | ||||
|         logging.debug("Ending read XML loop") | ||||
|         log.debug("Ending read XML loop") | ||||
|  | ||||
|     def _build_stanza(self, xml, default_ns=None): | ||||
|         """ | ||||
|         Create a stanza object from a given XML object. | ||||
|  | ||||
|         If a specialized stanza type is not found for the XML, then | ||||
|         a generic StanzaBase stanza will be returned. | ||||
|  | ||||
|         Arguments: | ||||
|             xml        -- The XML object to convert into a stanza object. | ||||
|             default_ns -- Optional default namespace to use instead of the | ||||
|                           stream's current default namespace. | ||||
|         """ | ||||
|         if default_ns is None: | ||||
|             default_ns = self.default_ns | ||||
|         stanza_type = StanzaBase | ||||
|         for stanza_class in self.__root_stanza: | ||||
|             if xml.tag == "{%s}%s" % (default_ns, stanza_class.name): | ||||
|                 stanza_type = stanza_class | ||||
|                 break | ||||
|         stanza = stanza_type(self, xml) | ||||
|         return stanza | ||||
|  | ||||
|     def __spawn_event(self, xml): | ||||
|         """ | ||||
| @@ -750,7 +806,7 @@ class XMLStream(object): | ||||
|         Arguments: | ||||
|             xml -- The XML stanza to analyze. | ||||
|         """ | ||||
|         logging.debug("RECV: %s" % tostring(xml, | ||||
|         log.debug("RECV: %s" % tostring(xml, | ||||
|                                             xmlns=self.default_ns, | ||||
|                                             stream=self)) | ||||
|         # Apply any preprocessing filters. | ||||
| @@ -786,6 +842,23 @@ class XMLStream(object): | ||||
|         if unhandled: | ||||
|             stanza.unhandled() | ||||
|  | ||||
|     def _threaded_event_wrapper(self, func, args): | ||||
|         """ | ||||
|         Capture exceptions for event handlers that run | ||||
|         in individual threads. | ||||
|  | ||||
|         Arguments: | ||||
|             func -- The event handler to execute. | ||||
|             args -- Arguments to the event handler. | ||||
|         """ | ||||
|         try: | ||||
|             func(*args) | ||||
|         except Exception as e: | ||||
|             error_msg = 'Error processing event handler: %s' | ||||
|             log.exception(error_msg % str(func)) | ||||
|             if hasattr(args[0], 'exception'): | ||||
|                 args[0].exception(e) | ||||
|  | ||||
|     def _event_runner(self): | ||||
|         """ | ||||
|         Process the event queue and execute handlers. | ||||
| @@ -795,7 +868,7 @@ class XMLStream(object): | ||||
|         Stream event handlers will all execute in this thread. Custom event | ||||
|         handlers may be spawned in individual threads. | ||||
|         """ | ||||
|         logging.debug("Loading event runner") | ||||
|         log.debug("Loading event runner") | ||||
|         try: | ||||
|             while not self.stop.isSet(): | ||||
|                 try: | ||||
| @@ -813,31 +886,35 @@ class XMLStream(object): | ||||
|                         handler.run(args[0]) | ||||
|                     except Exception as e: | ||||
|                         error_msg = 'Error processing stream handler: %s' | ||||
|                         logging.exception(error_msg % handler.name) | ||||
|                         log.exception(error_msg % handler.name) | ||||
|                         args[0].exception(e) | ||||
|                 elif etype == 'schedule': | ||||
|                     try: | ||||
|                         logging.debug(args) | ||||
|                         log.debug(args) | ||||
|                         handler(*args[0]) | ||||
|                     except: | ||||
|                         logging.exception('Error processing scheduled task') | ||||
|                         log.exception('Error processing scheduled task') | ||||
|                 elif etype == 'event': | ||||
|                     func, threaded, disposable = handler | ||||
|                     try: | ||||
|                         if threaded: | ||||
|                             x = threading.Thread(name="Event_%s" % str(func), | ||||
|                                                  target=func, | ||||
|                                                  args=args) | ||||
|                             x = threading.Thread( | ||||
|                                     name="Event_%s" % str(func), | ||||
|                                     target=self._threaded_event_wrapper, | ||||
|                                     args=(func, args)) | ||||
|                             x.start() | ||||
|                         else: | ||||
|                             func(*args) | ||||
|                     except: | ||||
|                         logging.exception('Error processing event handler: %s') | ||||
|                     except Exception as e: | ||||
|                         error_msg = 'Error processing event handler: %s' | ||||
|                         log.exception(error_msg % str(func)) | ||||
|                         if hasattr(args[0], 'exception'): | ||||
|                             args[0].exception(e) | ||||
|                 elif etype == 'quit': | ||||
|                     logging.debug("Quitting event runner thread") | ||||
|                     log.debug("Quitting event runner thread") | ||||
|                     return False | ||||
|         except KeyboardInterrupt: | ||||
|             logging.debug("Keyboard Escape Detected in _event_runner") | ||||
|             log.debug("Keyboard Escape Detected in _event_runner") | ||||
|             self.disconnect() | ||||
|             return | ||||
|         except SystemExit: | ||||
| @@ -855,14 +932,14 @@ class XMLStream(object): | ||||
|                     data = self.send_queue.get(True, 1) | ||||
|                 except queue.Empty: | ||||
|                     continue | ||||
|                 logging.debug("SEND: %s" % data) | ||||
|                 log.debug("SEND: %s" % data) | ||||
|                 try: | ||||
|                     self.socket.send(data.encode('utf-8')) | ||||
|                 except: | ||||
|                     logging.warning("Failed to send %s" % data) | ||||
|                     log.warning("Failed to send %s" % data) | ||||
|                     self.disconnect(self.auto_reconnect) | ||||
|         except KeyboardInterrupt: | ||||
|             logging.debug("Keyboard Escape Detected in _send_thread") | ||||
|             log.debug("Keyboard Escape Detected in _send_thread") | ||||
|             self.disconnect() | ||||
|             return | ||||
|         except SystemExit: | ||||
|   | ||||
| @@ -20,9 +20,9 @@ class TestLiveStream(SleekTest): | ||||
|  | ||||
|         # Use sid=None to ignore any id sent by the server since | ||||
|         # we can't know it in advance. | ||||
|         self.stream_recv_header(sfrom='localhost', sid=None) | ||||
|         self.stream_send_header(sto='localhost') | ||||
|         self.stream_recv_feature(""" | ||||
|         self.recv_header(sfrom='localhost', sid=None) | ||||
|         self.send_header(sto='localhost') | ||||
|         self.recv_feature(""" | ||||
|           <stream:features> | ||||
|             <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> | ||||
|             <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> | ||||
| @@ -35,15 +35,15 @@ class TestLiveStream(SleekTest): | ||||
|             <register xmlns="http://jabber.org/features/iq-register" /> | ||||
|           </stream:features> | ||||
|         """) | ||||
|         self.stream_send_feature(""" | ||||
|         self.send_feature(""" | ||||
|           <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> | ||||
|         """) | ||||
|         self.stream_recv_feature(""" | ||||
|         self.recv_feature(""" | ||||
|           <proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> | ||||
|         """) | ||||
|         self.stream_send_header(sto='localhost') | ||||
|         self.stream_recv_header(sfrom='localhost', sid=None) | ||||
|         self.stream_recv_feature(""" | ||||
|         self.send_header(sto='localhost') | ||||
|         self.recv_header(sfrom='localhost', sid=None) | ||||
|         self.recv_feature(""" | ||||
|           <stream:features> | ||||
|             <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> | ||||
|               <mechanism>DIGEST-MD5</mechanism> | ||||
| @@ -56,16 +56,16 @@ class TestLiveStream(SleekTest): | ||||
|             <register xmlns="http://jabber.org/features/iq-register" /> | ||||
|           </stream:features> | ||||
|         """) | ||||
|         self.stream_send_feature(""" | ||||
|         self.send_feature(""" | ||||
|           <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" | ||||
|                 mechanism="PLAIN">AHVzZXIAdXNlcg==</auth> | ||||
|         """) | ||||
|         self.stream_recv_feature(""" | ||||
|         self.recv_feature(""" | ||||
|           <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl" /> | ||||
|         """) | ||||
|         self.stream_send_header(sto='localhost') | ||||
|         self.stream_recv_header(sfrom='localhost', sid=None) | ||||
|         self.stream_recv_feature(""" | ||||
|         self.send_header(sto='localhost') | ||||
|         self.recv_header(sfrom='localhost', sid=None) | ||||
|         self.recv_feature(""" | ||||
|           <stream:features> | ||||
|             <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind" /> | ||||
|             <session xmlns="urn:ietf:params:xml:ns:xmpp-session" /> | ||||
| @@ -77,16 +77,16 @@ class TestLiveStream(SleekTest): | ||||
|           </stream:features> | ||||
|         """) | ||||
|  | ||||
|         # Should really use stream_send_iq, but our Iq stanza objects | ||||
|         # Should really use send, but our Iq stanza objects | ||||
|         # can't handle bind element payloads yet. | ||||
|         self.stream_send_feature(""" | ||||
|         self.send_feature(""" | ||||
|           <iq type="set" id="1"> | ||||
|             <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> | ||||
|               <resource>test</resource> | ||||
|             </bind> | ||||
|           </iq> | ||||
|         """) | ||||
|         self.stream_recv_feature(""" | ||||
|         self.recv_feature(""" | ||||
|           <iq type="result" id="1"> | ||||
|             <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> | ||||
|               <jid>user@localhost/test</jid> | ||||
|   | ||||
| @@ -3,26 +3,126 @@ from sleekxmpp.xmlstream.jid import JID | ||||
|  | ||||
|  | ||||
| class TestJIDClass(SleekTest): | ||||
|     def testJIDfromfull(self): | ||||
|         j = JID('user@someserver/some/resource') | ||||
|         self.assertEqual(j.user, 'user', "User does not match") | ||||
|         self.assertEqual(j.domain, 'someserver', "Domain does not match") | ||||
|         self.assertEqual(j.resource, 'some/resource', "Resource does not match") | ||||
|         self.assertEqual(j.bare, 'user@someserver', "Bare does not match") | ||||
|         self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match") | ||||
|         self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match") | ||||
|  | ||||
|     """Verify that the JID class can parse and manipulate JIDs.""" | ||||
|  | ||||
|     def testJIDFromFull(self): | ||||
|         """Test using JID of the form 'user@server/resource/with/slashes'.""" | ||||
|         self.check_jid(JID('user@someserver/some/resource'), | ||||
|                        'user', | ||||
|                        'someserver', | ||||
|                        'some/resource', | ||||
|                        'user@someserver', | ||||
|                        'user@someserver/some/resource', | ||||
|                        'user@someserver/some/resource') | ||||
|  | ||||
|     def testJIDchange(self): | ||||
|         """Test changing JID of the form 'user@server/resource/with/slashes'""" | ||||
|         j = JID('user1@someserver1/some1/resource1') | ||||
|         j.user = 'user' | ||||
|         j.domain = 'someserver' | ||||
|         j.resource = 'some/resource' | ||||
|         self.assertEqual(j.user, 'user', "User does not match") | ||||
|         self.assertEqual(j.domain, 'someserver', "Domain does not match") | ||||
|         self.assertEqual(j.resource, 'some/resource', "Resource does not match") | ||||
|         self.assertEqual(j.bare, 'user@someserver', "Bare does not match") | ||||
|         self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match") | ||||
|         self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match") | ||||
|         self.check_jid(j, | ||||
|                        'user', | ||||
|                        'someserver', | ||||
|                        'some/resource', | ||||
|                        'user@someserver', | ||||
|                        'user@someserver/some/resource', | ||||
|                        'user@someserver/some/resource') | ||||
|  | ||||
|     def testJIDaliases(self): | ||||
|         """Test changing JID using aliases for domain.""" | ||||
|         j = JID('user@someserver/resource') | ||||
|         j.server = 'anotherserver' | ||||
|         self.check_jid(j, domain='anotherserver') | ||||
|         j.host = 'yetanother' | ||||
|         self.check_jid(j, domain='yetanother') | ||||
|  | ||||
|     def testJIDSetFullWithUser(self): | ||||
|         """Test setting the full JID with a user portion.""" | ||||
|         j = JID('user@domain/resource') | ||||
|         j.full = 'otheruser@otherdomain/otherresource' | ||||
|         self.check_jid(j, | ||||
|                        'otheruser', | ||||
|                        'otherdomain', | ||||
|                        'otherresource', | ||||
|                        'otheruser@otherdomain', | ||||
|                        'otheruser@otherdomain/otherresource', | ||||
|                        'otheruser@otherdomain/otherresource') | ||||
|  | ||||
|     def testJIDFullNoUserWithResource(self): | ||||
|         """ | ||||
|         Test setting the full JID without a user | ||||
|         portion and with a resource. | ||||
|         """ | ||||
|         j = JID('user@domain/resource') | ||||
|         j.full = 'otherdomain/otherresource' | ||||
|         self.check_jid(j, | ||||
|                        '', | ||||
|                        'otherdomain', | ||||
|                        'otherresource', | ||||
|                        'otherdomain', | ||||
|                        'otherdomain/otherresource', | ||||
|                        'otherdomain/otherresource') | ||||
|  | ||||
|     def testJIDFullNoUserNoResource(self): | ||||
|         """ | ||||
|         Test setting the full JID without a user | ||||
|         portion and without a resource. | ||||
|         """ | ||||
|         j = JID('user@domain/resource') | ||||
|         j.full = 'otherdomain' | ||||
|         self.check_jid(j, | ||||
|                        '', | ||||
|                        'otherdomain', | ||||
|                        '', | ||||
|                        'otherdomain', | ||||
|                        'otherdomain', | ||||
|                        'otherdomain') | ||||
|  | ||||
|     def testJIDBareUser(self): | ||||
|         """Test setting the bare JID with a user.""" | ||||
|         j = JID('user@domain/resource') | ||||
|         j.bare = 'otheruser@otherdomain' | ||||
|         self.check_jid(j, | ||||
|                        'otheruser', | ||||
|                        'otherdomain', | ||||
|                        'resource', | ||||
|                        'otheruser@otherdomain', | ||||
|                        'otheruser@otherdomain/resource', | ||||
|                        'otheruser@otherdomain/resource') | ||||
|  | ||||
|     def testJIDBareNoUser(self): | ||||
|         """Test setting the bare JID without a user.""" | ||||
|         j = JID('user@domain/resource') | ||||
|         j.bare = 'otherdomain' | ||||
|         self.check_jid(j, | ||||
|                        '', | ||||
|                        'otherdomain', | ||||
|                        'resource', | ||||
|                        'otherdomain', | ||||
|                        'otherdomain/resource', | ||||
|                        'otherdomain/resource') | ||||
|  | ||||
|     def testJIDNoResource(self): | ||||
|         """Test using JID of the form 'user@domain'.""" | ||||
|         self.check_jid(JID('user@someserver'), | ||||
|                        'user', | ||||
|                        'someserver', | ||||
|                        '', | ||||
|                        'user@someserver', | ||||
|                        'user@someserver', | ||||
|                        'user@someserver') | ||||
|  | ||||
|     def testJIDNoUser(self): | ||||
|         """Test JID of the form 'component.domain.tld'.""" | ||||
|         self.check_jid(JID('component.someserver'), | ||||
|                        '', | ||||
|                        'component.someserver', | ||||
|                        '', | ||||
|                        'component.someserver', | ||||
|                        'component.someserver', | ||||
|                        'component.someserver') | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestJIDClass) | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class TestElementBase(SleekTest): | ||||
|             namespace = "test" | ||||
|  | ||||
|         stanza = TestStanza() | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="test"> | ||||
|             <bar> | ||||
|               <baz /> | ||||
| @@ -117,7 +117,7 @@ class TestElementBase(SleekTest): | ||||
|                                   'baz': ''}]} | ||||
|         stanza.setStanzaValues(values) | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" bar="a"> | ||||
|             <pluginfoo baz="b" /> | ||||
|             <pluginfoo2 bar="d" baz="e" /> | ||||
| @@ -198,7 +198,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza['qux'] = 'overridden' | ||||
|         stanza['foobar'] = 'plugin' | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" bar="attribute!"> | ||||
|             <baz>element!</baz> | ||||
|             <foobar foobar="plugin" /> | ||||
| @@ -231,7 +231,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza['qux'] = 'c' | ||||
|         stanza['foobar']['foobar'] = 'd' | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" baz="b" qux="c"> | ||||
|             <bar>a</bar> | ||||
|             <foobar foobar="d" /> | ||||
| @@ -243,7 +243,7 @@ class TestElementBase(SleekTest): | ||||
|         del stanza['qux'] | ||||
|         del stanza['foobar'] | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" qux="c" /> | ||||
|         """) | ||||
|  | ||||
| @@ -257,7 +257,7 @@ class TestElementBase(SleekTest): | ||||
|  | ||||
|         stanza = TestStanza() | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" /> | ||||
|         """) | ||||
|  | ||||
| @@ -267,7 +267,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza._set_attr('bar', 'a') | ||||
|         stanza._set_attr('baz', 'b') | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" bar="a" baz="b" /> | ||||
|         """) | ||||
|  | ||||
| @@ -277,7 +277,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza._set_attr('bar', None) | ||||
|         stanza._del_attr('baz') | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo" /> | ||||
|         """) | ||||
|  | ||||
| @@ -307,7 +307,7 @@ class TestElementBase(SleekTest): | ||||
|             "Default _get_sub_text value incorrect.") | ||||
|  | ||||
|         stanza['bar'] = 'found' | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <wrapper> | ||||
|               <bar>found</bar> | ||||
| @@ -340,7 +340,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza = TestStanza() | ||||
|         stanza['bar'] = 'a' | ||||
|         stanza['baz'] = 'b' | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <wrapper> | ||||
|               <bar>a</bar> | ||||
| @@ -349,7 +349,7 @@ class TestElementBase(SleekTest): | ||||
|           </foo> | ||||
|         """) | ||||
|         stanza._set_sub_text('wrapper/bar', text='', keep=True) | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <wrapper> | ||||
|               <bar /> | ||||
| @@ -360,7 +360,7 @@ class TestElementBase(SleekTest): | ||||
|  | ||||
|         stanza['bar'] = 'a' | ||||
|         stanza._set_sub_text('wrapper/bar', text='') | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <wrapper> | ||||
|               <baz>b</baz> | ||||
| @@ -398,7 +398,7 @@ class TestElementBase(SleekTest): | ||||
|         stanza['bar'] = 'a' | ||||
|         stanza['baz'] = 'b' | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <path> | ||||
|               <to> | ||||
| @@ -416,7 +416,7 @@ class TestElementBase(SleekTest): | ||||
|         del stanza['bar'] | ||||
|         del stanza['baz'] | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <path> | ||||
|               <to> | ||||
| @@ -432,7 +432,7 @@ class TestElementBase(SleekTest): | ||||
|  | ||||
|         stanza._del_sub('path/to/only/bar', all=True) | ||||
|  | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <path> | ||||
|               <to> | ||||
| @@ -603,7 +603,7 @@ class TestElementBase(SleekTest): | ||||
|             "Incorrect empty stanza size.") | ||||
|  | ||||
|         stanza.append(substanza1) | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <foobar qux="a" /> | ||||
|           </foo> | ||||
| @@ -612,7 +612,7 @@ class TestElementBase(SleekTest): | ||||
|             "Incorrect stanza size with 1 substanza.") | ||||
|  | ||||
|         stanza.append(substanza2) | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <foobar qux="a" /> | ||||
|             <foobar qux="b" /> | ||||
| @@ -623,7 +623,7 @@ class TestElementBase(SleekTest): | ||||
|  | ||||
|         # Test popping substanzas | ||||
|         stanza.pop(0) | ||||
|         self.check_stanza(TestStanza, stanza, """ | ||||
|         self.check(stanza, """ | ||||
|           <foo xmlns="foo"> | ||||
|             <foobar qux="b" /> | ||||
|           </foo> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ class TestErrorStanzas(SleekTest): | ||||
|         """Test setting initial values in error stanza.""" | ||||
|         msg = self.Message() | ||||
|         msg.enable('error') | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
| @@ -20,7 +20,7 @@ class TestErrorStanzas(SleekTest): | ||||
|         msg = self.Message() | ||||
|         msg['error']['condition'] = 'item-not-found' | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
| @@ -32,7 +32,7 @@ class TestErrorStanzas(SleekTest): | ||||
|  | ||||
|         msg['error']['condition'] = 'resource-constraint' | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <resource-constraint xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
| @@ -48,7 +48,7 @@ class TestErrorStanzas(SleekTest): | ||||
|  | ||||
|         del msg['error']['condition'] | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Error!</text> | ||||
| @@ -56,4 +56,21 @@ class TestErrorStanzas(SleekTest): | ||||
|           </message> | ||||
|         """, use_values=False) | ||||
|  | ||||
|     def testDelText(self): | ||||
|         """Test deleting the text of an error.""" | ||||
|         msg = self.Message() | ||||
|         msg['error']['test'] = 'Error!' | ||||
|         msg['error']['condition'] = 'internal-server-error' | ||||
|  | ||||
|         del msg['error']['text'] | ||||
|  | ||||
|         self.check(msg, """ | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
|             </error> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestErrorStanzas) | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class TestGmail(SleekTest): | ||||
|         iq['gmail']['newer-than-time'] = '1140638252542' | ||||
|         iq['gmail']['newer-than-tid'] = '11134623426430234' | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq type="get"> | ||||
|             <query xmlns="google:mail:notify" | ||||
|                    newer-than-time="1140638252542" | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class TestIqStanzas(SleekTest): | ||||
|     def testSetup(self): | ||||
|         """Test initializing default Iq values.""" | ||||
|         iq = self.Iq() | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0" /> | ||||
|         """) | ||||
|  | ||||
| @@ -19,7 +19,7 @@ class TestIqStanzas(SleekTest): | ||||
|         """Test setting Iq stanza payload.""" | ||||
|         iq = self.Iq() | ||||
|         iq.setPayload(ET.Element('{test}tester')) | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <tester xmlns="test" /> | ||||
|           </iq> | ||||
| @@ -29,7 +29,7 @@ class TestIqStanzas(SleekTest): | ||||
|     def testUnhandled(self): | ||||
|         """Test behavior for Iq.unhandled.""" | ||||
|         self.stream_start() | ||||
|         self.stream_recv(""" | ||||
|         self.recv(""" | ||||
|           <iq id="test" type="get"> | ||||
|             <query xmlns="test" /> | ||||
|            </iq> | ||||
| @@ -40,7 +40,7 @@ class TestIqStanzas(SleekTest): | ||||
|         iq['error']['condition'] = 'feature-not-implemented' | ||||
|         iq['error']['text'] = 'No handlers registered for this request.' | ||||
|  | ||||
|         self.stream_send_iq(iq, """ | ||||
|         self.send(iq, """ | ||||
|           <iq id="test" type="error"> | ||||
|             <error type="cancel"> | ||||
|               <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
| @@ -56,14 +56,14 @@ class TestIqStanzas(SleekTest): | ||||
|         iq = self.Iq() | ||||
|  | ||||
|         iq['query'] = 'query_ns' | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="query_ns" /> | ||||
|           </iq> | ||||
|         """) | ||||
|  | ||||
|         iq['query'] = 'query_ns2' | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="query_ns2" /> | ||||
|           </iq> | ||||
| @@ -72,7 +72,7 @@ class TestIqStanzas(SleekTest): | ||||
|         self.failUnless(iq['query'] == 'query_ns2', "Query namespace doesn't match") | ||||
|  | ||||
|         del iq['query'] | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0" /> | ||||
|         """) | ||||
|  | ||||
| @@ -83,7 +83,7 @@ class TestIqStanzas(SleekTest): | ||||
|         iq['type'] = 'get' | ||||
|         iq.reply() | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0" type="result" /> | ||||
|         """) | ||||
|  | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class TestMessageStanzas(SleekTest): | ||||
|         p = ET.Element('{http://www.w3.org/1999/xhtml}p') | ||||
|         p.text = "This is the htmlim message" | ||||
|         msg['html']['body'] = p | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message to="fritzy@netflint.net/sleekxmpp" type="chat"> | ||||
|             <body>this is the plaintext message</body> | ||||
|             <html xmlns="http://jabber.org/protocol/xhtml-im"> | ||||
| @@ -47,7 +47,7 @@ class TestMessageStanzas(SleekTest): | ||||
|         "Test message/nick/nick stanza." | ||||
|         msg = self.Message() | ||||
|         msg['nick']['nick'] = 'A nickname!' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick> | ||||
|           </message> | ||||
|   | ||||
| @@ -8,26 +8,26 @@ class TestPresenceStanzas(SleekTest): | ||||
|         """Regression check presence['type'] = 'dnd' show value working""" | ||||
|         p = self.Presence() | ||||
|         p['type'] = 'dnd' | ||||
|         self.check_presence(p, "<presence><show>dnd</show></presence>") | ||||
|         self.check(p, "<presence><show>dnd</show></presence>") | ||||
|  | ||||
|     def testPresenceType(self): | ||||
|         """Test manipulating presence['type']""" | ||||
|         p = self.Presence() | ||||
|         p['type'] = 'available' | ||||
|         self.check_presence(p, "<presence />") | ||||
|         self.check(p, "<presence />") | ||||
|         self.failUnless(p['type'] == 'available', | ||||
|             "Incorrect presence['type'] for type 'available': %s" % p['type']) | ||||
|  | ||||
|         for showtype in ['away', 'chat', 'dnd', 'xa']: | ||||
|             p['type'] = showtype | ||||
|             self.check_presence(p, """ | ||||
|             self.check(p, """ | ||||
|               <presence><show>%s</show></presence> | ||||
|             """ % showtype) | ||||
|             self.failUnless(p['type'] == showtype, | ||||
|                 "Incorrect presence['type'] for type '%s'" % showtype) | ||||
|  | ||||
|         p['type'] = None | ||||
|         self.check_presence(p, "<presence />") | ||||
|         self.check(p, "<presence />") | ||||
|  | ||||
|     def testPresenceUnsolicitedOffline(self): | ||||
|         """ | ||||
| @@ -56,7 +56,7 @@ class TestPresenceStanzas(SleekTest): | ||||
|         """Test presence/nick/nick stanza.""" | ||||
|         p = self.Presence() | ||||
|         p['nick']['nick'] = 'A nickname!' | ||||
|         self.check_presence(p, """ | ||||
|         self.check(p, """ | ||||
|           <presence> | ||||
|             <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick> | ||||
|           </presence> | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class TestRosterStanzas(SleekTest): | ||||
|                 'name': 'Other User', | ||||
|                 'subscription': 'both', | ||||
|                 'groups': []}}) | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq> | ||||
|             <query xmlns="jabber:iq:roster"> | ||||
|               <item jid="user@example.com" name="User" subscription="both"> | ||||
| @@ -74,7 +74,7 @@ class TestRosterStanzas(SleekTest): | ||||
|         """ | ||||
|         iq = self.Iq(ET.fromstring(xml_string)) | ||||
|         del iq['roster']['items'] | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq> | ||||
|             <query xmlns="jabber:iq:roster" /> | ||||
|           </iq> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class TestDataForms(SleekTest): | ||||
|         msg = self.Message() | ||||
|         msg['form']['instructions'] = "Instructions\nSecond batch" | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <x xmlns="jabber:x:data" type="form"> | ||||
|               <instructions>Instructions</instructions> | ||||
| @@ -35,7 +35,7 @@ class TestDataForms(SleekTest): | ||||
|                       required=True, | ||||
|                       value='Some text!') | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <x xmlns="jabber:x:data" type="form"> | ||||
|               <field var="f1" type="text-single" label="Text"> | ||||
| @@ -62,7 +62,7 @@ class TestDataForms(SleekTest): | ||||
|                                                'value': 'cool'}, | ||||
|                                               {'label': 'Urgh!', | ||||
|                                                'value': 'urgh'}]})] | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <x xmlns="jabber:x:data" type="form"> | ||||
|               <field var="f1" type="text-single" label="Username"> | ||||
| @@ -99,7 +99,7 @@ class TestDataForms(SleekTest): | ||||
|         form.setValues({'foo': 'Foo!', | ||||
|                         'bar': ['a', 'b']}) | ||||
|  | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <x xmlns="jabber:x:data" type="form"> | ||||
|               <field var="foo" type="text-single"> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class TestDisco(SleekTest): | ||||
|         iq['id'] = "0" | ||||
|         iq['disco_info']['node'] = '' | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#info" /> | ||||
|           </iq> | ||||
| @@ -26,7 +26,7 @@ class TestDisco(SleekTest): | ||||
|         iq['id'] = "0" | ||||
|         iq['disco_info']['node'] = 'foo' | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#info" node="foo" /> | ||||
|           </iq> | ||||
| @@ -38,7 +38,7 @@ class TestDisco(SleekTest): | ||||
|         iq['id'] = "0" | ||||
|         iq['disco_items']['node'] = '' | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#items" /> | ||||
|           </iq> | ||||
| @@ -50,7 +50,7 @@ class TestDisco(SleekTest): | ||||
|         iq['id'] = "0" | ||||
|         iq['disco_items']['node'] = 'foo' | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#items" node="foo" /> | ||||
|           </iq> | ||||
| @@ -63,7 +63,7 @@ class TestDisco(SleekTest): | ||||
|         iq['disco_info']['node'] = 'foo' | ||||
|         iq['disco_info'].addIdentity('conference', 'text', 'Chatroom') | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#info" node="foo"> | ||||
|               <identity category="conference" type="text" name="Chatroom" /> | ||||
| @@ -79,7 +79,7 @@ class TestDisco(SleekTest): | ||||
|         iq['disco_info'].addFeature('foo') | ||||
|         iq['disco_info'].addFeature('bar') | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#info" node="foo"> | ||||
|               <feature var="foo" /> | ||||
| @@ -97,7 +97,7 @@ class TestDisco(SleekTest): | ||||
|         iq['disco_items'].addItem('user@localhost', 'foo') | ||||
|         iq['disco_items'].addItem('user@localhost', 'bar', 'Testing') | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <query xmlns="http://jabber.org/protocol/disco#items" node="foo"> | ||||
|               <item jid="user@localhost" /> | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class TestAddresses(SleekTest): | ||||
|         """Testing adding extended stanza address.""" | ||||
|         msg = self.Message() | ||||
|         msg['addresses'].addAddress(atype='to', jid='to@header1.org') | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|         <message> | ||||
|           <addresses xmlns="http://jabber.org/protocol/address"> | ||||
|             <address jid="to@header1.org" type="to" /> | ||||
| @@ -23,7 +23,7 @@ class TestAddresses(SleekTest): | ||||
|         msg['addresses'].addAddress(atype='replyto', | ||||
|                                     jid='replyto@header1.org', | ||||
|                                     desc='Reply address') | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <addresses xmlns="http://jabber.org/protocol/address"> | ||||
|               <address jid="replyto@header1.org" type="replyto" desc="Reply address" /> | ||||
| @@ -53,14 +53,14 @@ class TestAddresses(SleekTest): | ||||
|              'jid':'cc@header2.org'}, | ||||
|             {'type':'bcc', | ||||
|              'jid':'bcc@header2.org'}]) | ||||
|         self.check_message(msg, xmlstring) | ||||
|         self.check(msg, xmlstring) | ||||
|  | ||||
|         msg = self.Message() | ||||
|         msg['addresses']['replyto'] = [{'jid':'replyto@header1.org', | ||||
|                                                 'desc':'Reply address'}] | ||||
|         msg['addresses']['cc'] = [{'jid':'cc@header2.org'}] | ||||
|         msg['addresses']['bcc'] = [{'jid':'bcc@header2.org'}] | ||||
|         self.check_message(msg, xmlstring) | ||||
|         self.check(msg, xmlstring) | ||||
|  | ||||
|     def testAddURI(self): | ||||
|         """Testing adding URI attribute to extended stanza address.""" | ||||
| @@ -69,7 +69,7 @@ class TestAddresses(SleekTest): | ||||
|         addr = msg['addresses'].addAddress(atype='to', | ||||
|                                            jid='to@header1.org', | ||||
|                                            node='foo') | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <addresses xmlns="http://jabber.org/protocol/address"> | ||||
|               <address node="foo" jid="to@header1.org" type="to" /> | ||||
| @@ -78,7 +78,7 @@ class TestAddresses(SleekTest): | ||||
|         """) | ||||
|  | ||||
|         addr['uri'] = 'mailto:to@header2.org' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message> | ||||
|             <addresses xmlns="http://jabber.org/protocol/address"> | ||||
|               <address type="to" uri="mailto:to@header2.org" /> | ||||
| @@ -99,13 +99,13 @@ class TestAddresses(SleekTest): | ||||
|  | ||||
|         msg = self.Message() | ||||
|         addr = msg['addresses'].addAddress(jid='to@header1.org', atype='to') | ||||
|         self.check_message(msg, xmlstring % '') | ||||
|         self.check(msg, xmlstring % '') | ||||
|  | ||||
|         addr['delivered'] = True | ||||
|         self.check_message(msg, xmlstring % 'delivered="true"') | ||||
|         self.check(msg, xmlstring % 'delivered="true"') | ||||
|  | ||||
|         addr['delivered'] = False | ||||
|         self.check_message(msg, xmlstring % '') | ||||
|         self.check(msg, xmlstring % '') | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestAddresses) | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         aff2['affiliation'] = 'publisher' | ||||
|         iq['pubsub']['affiliations'].append(aff1) | ||||
|         iq['pubsub']['affiliations'].append(aff2) | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <affiliations> | ||||
| @@ -38,7 +38,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         sub2['subscription'] = 'subscribed' | ||||
|         iq['pubsub']['subscriptions'].append(sub1) | ||||
|         iq['pubsub']['subscriptions'].append(sub2) | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <subscriptions> | ||||
| @@ -55,7 +55,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         iq['pubsub']['subscription']['node'] = 'testnode alsdkjfas' | ||||
|         iq['pubsub']['subscription']['jid'] = "fritzy@netflint.net/sleekxmpp" | ||||
|         iq['pubsub']['subscription']['subscription'] = 'unconfigured' | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <subscription node="testnode alsdkjfas" jid="fritzy@netflint.net/sleekxmpp" subscription="unconfigured"> | ||||
| @@ -88,7 +88,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         item2['payload'] = payload2 | ||||
|         iq['pubsub']['items'].append(item) | ||||
|         iq['pubsub']['items'].append(item2) | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <items node="crap"> | ||||
| @@ -115,7 +115,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         iq['pubsub']['configure']['form'].addField('pubsub#title', | ||||
|                                                    ftype='text-single', | ||||
|                                                    value='This thing is awesome') | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <create node="mynode" /> | ||||
| @@ -136,7 +136,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         iq['psstate']['item']= 'myitem' | ||||
|         pl = ET.Element('{http://andyet.net/protocol/pubsubqueue}claimed') | ||||
|         iq['psstate']['payload'] = pl | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <state xmlns="http://jabber.org/protocol/psstate" node="mynode" item="myitem"> | ||||
|               <claimed xmlns="http://andyet.net/protocol/pubsubqueue" /> | ||||
| @@ -152,7 +152,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         iq['pubsub_owner']['default']['form'].addField('pubsub#title', | ||||
|                                                        ftype='text-single', | ||||
|                                                        value='This thing is awesome') | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
| 	      <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub#owner"> | ||||
|               <default node="mynode" type="leaf"> | ||||
| @@ -176,7 +176,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         form = xep_0004.Form() | ||||
|         form.addField('pubsub#title', ftype='text-single', value='this thing is awesome') | ||||
|         iq['pubsub']['subscribe']['options']['options'] = form | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|         <iq id="0"> | ||||
|           <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|             <subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp"> | ||||
| @@ -214,7 +214,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         iq['pubsub']['publish'].append(item) | ||||
|         iq['pubsub']['publish'].append(item2) | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <publish node="thingers"> | ||||
| @@ -238,7 +238,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         "Testing iq/pubsub_owner/delete stanzas" | ||||
|         iq = self.Iq() | ||||
|         iq['pubsub_owner']['delete']['node'] = 'thingers' | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq id="0"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub#owner"> | ||||
|               <delete node="thingers" /> | ||||
| @@ -300,7 +300,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|                                                     'label': 'Deliver notification only to available users'}), | ||||
|                 ]) | ||||
|  | ||||
|         self.check_iq(iq, """ | ||||
|         self.check(iq, """ | ||||
|           <iq to="pubsub.asdf" type="set" id="E" from="fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7"> | ||||
|             <pubsub xmlns="http://jabber.org/protocol/pubsub"> | ||||
|               <create node="testnode2" /> | ||||
| @@ -357,7 +357,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['items'].append(item) | ||||
|         msg['pubsub_event']['items']['node'] = 'cheese' | ||||
|         msg['type'] = 'normal' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="normal"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <items node="cheese"> | ||||
| @@ -383,7 +383,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['items'].append(item2) | ||||
|         msg['pubsub_event']['items']['node'] = 'cheese' | ||||
|         msg['type'] = 'normal' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="normal"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <items node="cheese"> | ||||
| @@ -415,7 +415,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['items'].append(item2) | ||||
|         msg['pubsub_event']['items']['node'] = 'cheese' | ||||
|         msg['type'] = 'normal' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="normal"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <items node="cheese"> | ||||
| @@ -435,7 +435,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['collection']['associate']['node'] = 'cheese' | ||||
|         msg['pubsub_event']['collection']['node'] = 'cheeseburger' | ||||
|         msg['type'] = 'headline' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="headline"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <collection node="cheeseburger"> | ||||
| @@ -450,7 +450,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['collection']['disassociate']['node'] = 'cheese' | ||||
|         msg['pubsub_event']['collection']['node'] = 'cheeseburger' | ||||
|         msg['type'] = 'headline' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="headline"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <collection node="cheeseburger"> | ||||
| @@ -467,7 +467,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|                                                               ftype='text-single', | ||||
|                                                               value='This thing is awesome') | ||||
|         msg['type'] = 'headline' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|         <message type="headline"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <configuration node="cheese"> | ||||
| @@ -485,7 +485,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg = self.Message() | ||||
|         msg['pubsub_event']['purge']['node'] = 'pickles' | ||||
|         msg['type'] = 'headline' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="headline"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <purge node="pickles" /> | ||||
| @@ -501,7 +501,7 @@ class TestPubsubStanzas(SleekTest): | ||||
|         msg['pubsub_event']['subscription']['subscription'] = 'subscribed' | ||||
|         msg['pubsub_event']['subscription']['expiry'] = 'presence' | ||||
|         msg['type'] = 'headline' | ||||
|         self.check_message(msg, """ | ||||
|         self.check(msg, """ | ||||
|           <message type="headline"> | ||||
|             <event xmlns="http://jabber.org/protocol/pubsub#event"> | ||||
|               <subscription node="pickles" subid="aabb1122" jid="fritzy@netflint.net/test" subscription="subscribed" expiry="presence" /> | ||||
|   | ||||
| @@ -21,24 +21,24 @@ class TestChatStates(SleekTest): | ||||
|  | ||||
|         msg = self.Message() | ||||
|         msg['chat_state'].active() | ||||
|         self.check_message(msg, xmlstring % 'active', | ||||
|         self.check(msg, xmlstring % 'active', | ||||
|                           use_values=False) | ||||
|  | ||||
|         msg['chat_state'].composing() | ||||
|         self.check_message(msg, xmlstring % 'composing', | ||||
|         self.check(msg, xmlstring % 'composing', | ||||
|                           use_values=False) | ||||
|  | ||||
|  | ||||
|         msg['chat_state'].gone() | ||||
|         self.check_message(msg, xmlstring % 'gone', | ||||
|         self.check(msg, xmlstring % 'gone', | ||||
|                           use_values=False) | ||||
|  | ||||
|         msg['chat_state'].inactive() | ||||
|         self.check_message(msg, xmlstring % 'inactive', | ||||
|         self.check(msg, xmlstring % 'inactive', | ||||
|                           use_values=False) | ||||
|  | ||||
|         msg['chat_state'].paused() | ||||
|         self.check_message(msg, xmlstring % 'paused', | ||||
|         self.check(msg, xmlstring % 'paused', | ||||
|                           use_values=False) | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestChatStates) | ||||
|   | ||||
| @@ -19,13 +19,13 @@ class TestStreamTester(SleekTest): | ||||
|  | ||||
|         self.xmpp.add_event_handler('message', echo) | ||||
|  | ||||
|         self.stream_recv(""" | ||||
|         self.recv(""" | ||||
|           <message to="tester@localhost" from="user@localhost"> | ||||
|             <body>Hi!</body> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|         self.stream_send_message(""" | ||||
|         self.send(""" | ||||
|           <message to="user@localhost"> | ||||
|             <body>Thanks for sending: Hi!</body> | ||||
|           </message> | ||||
| @@ -40,13 +40,13 @@ class TestStreamTester(SleekTest): | ||||
|  | ||||
|         self.xmpp.add_event_handler('message', echo) | ||||
|  | ||||
|         self.stream_recv(""" | ||||
|         self.recv(""" | ||||
|           <message to="tester.localhost" from="user@localhost"> | ||||
|             <body>Hi!</body> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|         self.stream_send_message(""" | ||||
|         self.send(""" | ||||
|           <message to="user@localhost" from="tester.localhost"> | ||||
|             <body>Thanks for sending: Hi!</body> | ||||
|           </message> | ||||
| @@ -55,6 +55,6 @@ class TestStreamTester(SleekTest): | ||||
|     def testSendStreamHeader(self): | ||||
|         """Test that we can check a sent stream header.""" | ||||
|         self.stream_start(mode='client', skip=False) | ||||
|         self.stream_send_header(sto='localhost') | ||||
|         self.send_header(sto='localhost') | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamTester) | ||||
|   | ||||
							
								
								
									
										110
									
								
								tests/test_stream_exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								tests/test_stream_exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| import sys | ||||
| import sleekxmpp | ||||
| from sleekxmpp.exceptions import XMPPError | ||||
| from sleekxmpp.test import * | ||||
|  | ||||
|  | ||||
| class TestStreamExceptions(SleekTest): | ||||
|     """ | ||||
|     Test handling roster updates. | ||||
|     """ | ||||
|  | ||||
|     def tearDown(self): | ||||
|         self.stream_close() | ||||
|  | ||||
|     def testXMPPErrorException(self): | ||||
|         """Test raising an XMPPError exception.""" | ||||
|  | ||||
|         def message(msg): | ||||
|             raise XMPPError(condition='feature-not-implemented', | ||||
|                             text="We don't do things that way here.", | ||||
|                             etype='cancel', | ||||
|                             extension='foo', | ||||
|                             extension_ns='foo:error', | ||||
|                             extension_args={'test': 'true'}) | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('message', message) | ||||
|  | ||||
|         self.recv(""" | ||||
|           <message> | ||||
|             <body>This is going to cause an error.</body> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|         self.send(""" | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <feature-not-implemented | ||||
|                   xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
|               <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> | ||||
|                 We don't do things that way here. | ||||
|               </text> | ||||
|               <foo xmlns="foo:error" test="true" /> | ||||
|             </error> | ||||
|           </message> | ||||
|         """, use_values=False) | ||||
|  | ||||
|     def testThreadedXMPPErrorException(self): | ||||
|         """Test raising an XMPPError exception in a threaded handler.""" | ||||
|  | ||||
|         def message(msg): | ||||
|             raise XMPPError(condition='feature-not-implemented', | ||||
|                             text="We don't do things that way here.", | ||||
|                             etype='cancel') | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('message', message, | ||||
|                                     threaded=True) | ||||
|  | ||||
|         self.recv(""" | ||||
|           <message> | ||||
|             <body>This is going to cause an error.</body> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|         self.send(""" | ||||
|           <message type="error"> | ||||
|             <error type="cancel"> | ||||
|               <feature-not-implemented | ||||
|                   xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
|               <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> | ||||
|                 We don't do things that way here. | ||||
|               </text> | ||||
|             </error> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|     def testUnknownException(self): | ||||
|         """Test raising an generic exception in a threaded handler.""" | ||||
|  | ||||
|         def message(msg): | ||||
|             raise ValueError("Did something wrong") | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('message', message) | ||||
|  | ||||
|         self.recv(""" | ||||
|           <message> | ||||
|             <body>This is going to cause an error.</body> | ||||
|           </message> | ||||
|         """) | ||||
|  | ||||
|         if sys.version_info < (3, 0): | ||||
|             self.send(""" | ||||
|               <message type="error"> | ||||
|                 <error type="cancel"> | ||||
|                   <undefined-condition | ||||
|                       xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||
|                   <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> | ||||
|                     SleekXMPP got into trouble. | ||||
|                   </text> | ||||
|                 </error> | ||||
|               </message> | ||||
|             """) | ||||
|         else: | ||||
|             # Unfortunately, tracebacks do not make for very portable tests. | ||||
|             pass | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamExceptions) | ||||
| @@ -30,11 +30,11 @@ class TestHandlers(SleekTest): | ||||
|  | ||||
|         self.xmpp.registerHandler(callback) | ||||
|  | ||||
|         self.stream_recv("""<tester xmlns="test" />""") | ||||
|         self.recv("""<tester xmlns="test" />""") | ||||
|  | ||||
|         msg = self.Message() | ||||
|         msg['body'] = 'Success!' | ||||
|         self.stream_send_message(msg) | ||||
|         self.send(msg) | ||||
|  | ||||
|     def testWaiter(self): | ||||
|         """Test using stream waiter handler.""" | ||||
| @@ -55,7 +55,7 @@ class TestHandlers(SleekTest): | ||||
|         self.xmpp.add_event_handler('message', waiter_handler, threaded=True) | ||||
|  | ||||
|         # Send message to trigger waiter_handler | ||||
|         self.stream_recv(""" | ||||
|         self.recv(""" | ||||
|           <message> | ||||
|             <body>Testing</body> | ||||
|           </message> | ||||
| @@ -66,10 +66,10 @@ class TestHandlers(SleekTest): | ||||
|         iq['id'] = 'test' | ||||
|         iq['type'] = 'set' | ||||
|         iq['query'] = 'test' | ||||
|         self.stream_send_iq(iq) | ||||
|         self.send(iq) | ||||
|  | ||||
|         # Send the reply Iq | ||||
|         self.stream_recv(""" | ||||
|         self.recv(""" | ||||
|           <iq id="test" type="result"> | ||||
|             <query xmlns="test" /> | ||||
|           </iq> | ||||
| @@ -78,7 +78,7 @@ class TestHandlers(SleekTest): | ||||
|         # Check that waiter_handler received the reply | ||||
|         msg = self.Message() | ||||
|         msg['body'] = 'Successful: test' | ||||
|         self.stream_send_message(msg) | ||||
|         self.send(msg) | ||||
|  | ||||
|     def testWaiterTimeout(self): | ||||
|         """Test that waiter handler is removed after timeout.""" | ||||
| @@ -93,14 +93,14 @@ class TestHandlers(SleekTest): | ||||
|         self.xmpp.add_event_handler('message', waiter_handler, threaded=True) | ||||
|  | ||||
|         # Start test by triggerig waiter_handler | ||||
|         self.stream_recv("""<message><body>Start Test</body></message>""") | ||||
|         self.recv("""<message><body>Start Test</body></message>""") | ||||
|  | ||||
|         # Check that Iq was sent to trigger start of timeout period | ||||
|         iq = self.Iq() | ||||
|         iq['id'] = 'test2' | ||||
|         iq['type'] = 'set' | ||||
|         iq['query'] = 'test2' | ||||
|         self.stream_send_iq(iq) | ||||
|         self.send(iq) | ||||
|  | ||||
|         # Check that the waiter is no longer registered | ||||
|         waiter_exists = self.xmpp.removeHandler('IqWait_test2') | ||||
|   | ||||
							
								
								
									
										188
									
								
								tests/test_stream_presence.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								tests/test_stream_presence.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| import time | ||||
| from sleekxmpp.test import * | ||||
|  | ||||
|  | ||||
| class TestStreamPresence(SleekTest): | ||||
|     """ | ||||
|     Test handling roster updates. | ||||
|     """ | ||||
|  | ||||
|     def tearDown(self): | ||||
|         self.stream_close() | ||||
|  | ||||
|     def testInitialUnavailablePresences(self): | ||||
|         """ | ||||
|         Test receiving unavailable presences from JIDs that | ||||
|         are not online. | ||||
|         """ | ||||
|         events = set() | ||||
|  | ||||
|         def got_offline(presence): | ||||
|             # The got_offline event should not be triggered. | ||||
|             events.add('got_offline') | ||||
|  | ||||
|         def unavailable(presence): | ||||
|             # The presence_unavailable event should be triggered. | ||||
|             events.add('unavailable') | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('got_offline', got_offline) | ||||
|         self.xmpp.add_event_handler('presence_unavailable', unavailable) | ||||
|  | ||||
|         self.recv(""" | ||||
|           <presence type="unavailable" from="otheruser@localhost" /> | ||||
|         """) | ||||
|  | ||||
|         # Give event queue time to process. | ||||
|         time.sleep(0.1) | ||||
|  | ||||
|         self.assertEqual(events, set(('unavailable',)), | ||||
|                 "Got offline incorrectly triggered: %s." % events) | ||||
|  | ||||
|     def testGotOffline(self): | ||||
|         """Test that got_offline is triggered properly.""" | ||||
|         events = [] | ||||
|  | ||||
|         def got_offline(presence): | ||||
|             events.append('got_offline') | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('got_offline', got_offline) | ||||
|  | ||||
|         # Setup roster. Use a 'set' instead of 'result' so we | ||||
|         # don't have to handle get_roster() blocking. | ||||
|         # | ||||
|         # We use the stream to initialize the roster to make | ||||
|         # the test independent of the roster implementation. | ||||
|         self.recv(""" | ||||
|           <iq type="set"> | ||||
|             <query xmlns="jabber:iq:roster"> | ||||
|               <item jid="otheruser@localhost" | ||||
|                     name="Other User" | ||||
|                     subscription="both"> | ||||
|                 <group>Testers</group> | ||||
|               </item> | ||||
|             </query> | ||||
|           </iq> | ||||
|         """) | ||||
|  | ||||
|         # Contact comes online. | ||||
|         self.recv(""" | ||||
|           <presence from="otheruser@localhost/foobar" /> | ||||
|         """) | ||||
|  | ||||
|         # Contact goes offline, should trigger got_offline. | ||||
|         self.recv(""" | ||||
|           <presence from="otheruser@localhost/foobar" | ||||
|                     type="unavailable" /> | ||||
|         """) | ||||
|  | ||||
|         # Give event queue time to process. | ||||
|         time.sleep(0.1) | ||||
|  | ||||
|         self.assertEqual(events, ['got_offline'], | ||||
|                 "Got offline incorrectly triggered: %s" % events) | ||||
|  | ||||
|     def testGotOnline(self): | ||||
|         """Test that got_online is triggered properly.""" | ||||
|  | ||||
|         events = set() | ||||
|  | ||||
|         def presence_available(p): | ||||
|             events.add('presence_available') | ||||
|  | ||||
|         def got_online(p): | ||||
|             events.add('got_online') | ||||
|  | ||||
|         self.stream_start() | ||||
|         self.xmpp.add_event_handler('presence_available', presence_available) | ||||
|         self.xmpp.add_event_handler('got_online', got_online) | ||||
|  | ||||
|         self.recv(""" | ||||
|           <presence from="user@localhost" /> | ||||
|         """) | ||||
|  | ||||
|         # Give event queue time to process. | ||||
|         time.sleep(0.1) | ||||
|  | ||||
|         expected = set(('presence_available', 'got_online')) | ||||
|         self.assertEqual(events, expected, | ||||
|                 "Incorrect events triggered: %s" % events) | ||||
|  | ||||
|     def testAutoAuthorizeAndSubscribe(self): | ||||
|         """ | ||||
|         Test auto authorizing and auto subscribing | ||||
|         to subscription requests. | ||||
|         """ | ||||
|  | ||||
|         events = set() | ||||
|  | ||||
|         def presence_subscribe(p): | ||||
|             events.add('presence_subscribe') | ||||
|  | ||||
|         def changed_subscription(p): | ||||
|             events.add('changed_subscription') | ||||
|  | ||||
|         self.stream_start(jid='tester@localhost') | ||||
|  | ||||
|         self.xmpp.add_event_handler('changed_subscription', | ||||
|                                     changed_subscription) | ||||
|         self.xmpp.add_event_handler('presence_subscribe', | ||||
|                                     presence_subscribe) | ||||
|  | ||||
|         # With these settings we should accept a subscription | ||||
|         # and request a subscription in return. | ||||
|         self.xmpp.auto_authorize = True | ||||
|         self.xmpp.auto_subscribe = True | ||||
|  | ||||
|         self.recv(""" | ||||
|           <presence from="user@localhost" type="subscribe" /> | ||||
|         """) | ||||
|  | ||||
|         self.send(""" | ||||
|           <presence to="user@localhost" type="subscribed" /> | ||||
|         """) | ||||
|  | ||||
|         self.send(""" | ||||
|           <presence to="user@localhost" type="subscribe" /> | ||||
|         """) | ||||
|  | ||||
|         expected = set(('presence_subscribe', 'changed_subscription')) | ||||
|         self.assertEqual(events, expected, | ||||
|                 "Incorrect events triggered: %s" % events) | ||||
|  | ||||
|     def testNoAutoAuthorize(self): | ||||
|         """Test auto rejecting subscription requests.""" | ||||
|  | ||||
|         events = set() | ||||
|  | ||||
|         def presence_subscribe(p): | ||||
|             events.add('presence_subscribe') | ||||
|  | ||||
|         def changed_subscription(p): | ||||
|             events.add('changed_subscription') | ||||
|  | ||||
|         self.stream_start(jid='tester@localhost') | ||||
|  | ||||
|         self.xmpp.add_event_handler('changed_subscription', | ||||
|                                     changed_subscription) | ||||
|         self.xmpp.add_event_handler('presence_subscribe', | ||||
|                                     presence_subscribe) | ||||
|  | ||||
|         # With this setting we should reject all subscriptions. | ||||
|         self.xmpp.auto_authorize = False | ||||
|  | ||||
|         self.recv(""" | ||||
|           <presence from="user@localhost" type="subscribe" /> | ||||
|         """) | ||||
|  | ||||
|         self.send(""" | ||||
|           <presence to="user@localhost" type="unsubscribed" /> | ||||
|         """) | ||||
|  | ||||
|         expected = set(('presence_subscribe', 'changed_subscription')) | ||||
|         self.assertEqual(events, expected, | ||||
|                 "Incorrect events triggered: %s" % events) | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamPresence) | ||||
							
								
								
									
										86
									
								
								tests/test_stream_roster.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tests/test_stream_roster.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| from sleekxmpp.test import * | ||||
| import time | ||||
| import threading | ||||
|  | ||||
|  | ||||
| class TestStreamRoster(SleekTest): | ||||
|     """ | ||||
|     Test handling roster updates. | ||||
|     """ | ||||
|  | ||||
|     def tearDown(self): | ||||
|         self.stream_close() | ||||
|  | ||||
|     def testGetRoster(self): | ||||
|         """Test handling roster requests.""" | ||||
|         self.stream_start(mode='client') | ||||
|         self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.") | ||||
|  | ||||
|         # Since get_roster blocks, we need to run it in a thread. | ||||
|         t = threading.Thread(name='get_roster', target=self.xmpp.get_roster) | ||||
|         t.start() | ||||
|  | ||||
|         self.send(""" | ||||
|           <iq type="get" id="1"> | ||||
|             <query xmlns="jabber:iq:roster" /> | ||||
|           </iq> | ||||
|         """) | ||||
|         self.recv(""" | ||||
|           <iq type="result" id="1"> | ||||
|             <query xmlns="jabber:iq:roster"> | ||||
|               <item jid="user@localhost" | ||||
|                     name="User" | ||||
|                     subscription="both"> | ||||
|                 <group>Friends</group> | ||||
|                 <group>Examples</group> | ||||
|               </item> | ||||
|             </query> | ||||
|           </iq> | ||||
|         """) | ||||
|  | ||||
|         # Wait for get_roster to return. | ||||
|         t.join() | ||||
|  | ||||
|         roster = {'user@localhost': {'name': 'User', | ||||
|                                      'subscription': 'both', | ||||
|                                      'groups': ['Friends', 'Examples'], | ||||
|                                      'presence': {}, | ||||
|                                      'in_roster': True}} | ||||
|         self.failUnless(self.xmpp.roster == roster, | ||||
|                 "Unexpected roster values: %s" % self.xmpp.roster) | ||||
|  | ||||
|     def testRosterSet(self): | ||||
|         """Test handling pushed roster updates.""" | ||||
|         self.stream_start(mode='client') | ||||
|         self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.") | ||||
|  | ||||
|         self.recv(""" | ||||
|           <iq type="set" id="1"> | ||||
|             <query xmlns="jabber:iq:roster"> | ||||
|               <item jid="user@localhost" | ||||
|                     name="User" | ||||
|                     subscription="both"> | ||||
|                 <group>Friends</group> | ||||
|                 <group>Examples</group> | ||||
|               </item> | ||||
|             </query> | ||||
|           </iq> | ||||
|         """) | ||||
|         self.send(""" | ||||
|           <iq type="result" id="1"> | ||||
|             <query xmlns="jabber:iq:roster" /> | ||||
|           </iq> | ||||
|         """) | ||||
|  | ||||
|         roster = {'user@localhost': {'name': 'User', | ||||
|                                      'subscription': 'both', | ||||
|                                      'groups': ['Friends', 'Examples'], | ||||
|                                      'presence': {}, | ||||
|                                      'in_roster': True}} | ||||
|         self.failUnless(self.xmpp.roster == roster, | ||||
|                 "Unexpected roster values: %s" % self.xmpp.roster) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster) | ||||
| @@ -73,6 +73,16 @@ class TestToString(SleekTest): | ||||
|             message="The xmlns parameter was not used properly.", | ||||
|             xmlns='foo') | ||||
|  | ||||
|     def testTailContent(self): | ||||
|         """ | ||||
|         Test that elements of the form <a>foo <b>bar</b> baz</a> only | ||||
|         include " baz" once. | ||||
|         """ | ||||
|         self.tryTostring( | ||||
|             original='<a>foo <b>bar</b> baz</a>', | ||||
|             message='Element tail content is incorrect.') | ||||
|  | ||||
|  | ||||
|     def testStanzaNs(self): | ||||
|         """ | ||||
|         Test using the stanza_ns tostring parameter, which will prevent | ||||
|   | ||||
		Reference in New Issue
	
	Block a user