summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2013-01-21 02:34:22 -0800
committerLance Stout <lancestout@gmail.com>2013-01-21 02:34:22 -0800
commitbad405bea96405eb952888dc6996b8b40be85ac4 (patch)
treeb5998707d5d8d836eaa807f1e755047c1aed5c48
parent648f3f978a84445ed188be055453ac4cab3ed4f5 (diff)
parent4f9a95b011277ab64a137aceeb4df2e9fc0f1e25 (diff)
downloadslixmpp-bad405bea96405eb952888dc6996b8b40be85ac4.tar.gz
slixmpp-bad405bea96405eb952888dc6996b8b40be85ac4.tar.bz2
slixmpp-bad405bea96405eb952888dc6996b8b40be85ac4.tar.xz
slixmpp-bad405bea96405eb952888dc6996b8b40be85ac4.zip
Merge branch 'master' into develop
-rwxr-xr-xexamples/ping.py22
-rw-r--r--sleekxmpp/plugins/__init__.py2
-rw-r--r--sleekxmpp/plugins/gmail/__init__.py15
-rw-r--r--sleekxmpp/plugins/gmail/notifications.py77
-rw-r--r--sleekxmpp/plugins/gmail/stanza.py101
-rw-r--r--sleekxmpp/plugins/gmail_notify.py149
-rw-r--r--sleekxmpp/plugins/google_nosave/__init__.py15
-rw-r--r--sleekxmpp/plugins/google_nosave/nosave.py83
-rw-r--r--sleekxmpp/plugins/google_nosave/stanza.py59
-rw-r--r--sleekxmpp/plugins/google_settings/__init__.py15
-rw-r--r--sleekxmpp/plugins/google_settings/settings.py68
-rw-r--r--sleekxmpp/plugins/google_settings/stanza.py110
-rw-r--r--sleekxmpp/plugins/jobs.py49
-rw-r--r--sleekxmpp/plugins/old_0050.py133
-rw-r--r--sleekxmpp/plugins/old_0060.py313
-rw-r--r--sleekxmpp/plugins/xep_0092/version.py14
-rw-r--r--sleekxmpp/plugins/xep_0199/ping.py141
-rw-r--r--sleekxmpp/plugins/xep_0231/bob.py4
-rw-r--r--sleekxmpp/xmlstream/matcher/xmlmask.py67
-rw-r--r--sleekxmpp/xmlstream/matcher/xpath.py37
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py2
-rw-r--r--tests/test_stream_xep_0092.py6
22 files changed, 657 insertions, 825 deletions
diff --git a/examples/ping.py b/examples/ping.py
index 0e53b1dd..8fbb5655 100755
--- a/examples/ping.py
+++ b/examples/ping.py
@@ -62,16 +62,18 @@ class PingTest(sleekxmpp.ClientXMPP):
"""
self.send_presence()
self.get_roster()
- result = self['xep_0199'].send_ping(self.pingjid,
- timeout=10,
- errorfalse=True)
- logging.info("Pinging...")
- if result is False:
- logging.info("Couldn't ping.")
- self.disconnect()
- sys.exit(1)
- else:
- logging.info("Success! RTT: %s", str(result))
+
+ try:
+ rtt = self['xep_0199'].ping(self.pingjid,
+ timeout=10)
+ logging.info("Success! RTT: %s", rtt)
+ except IqError as e:
+ logging.info("Error pinging %s: %s",
+ self.pingjid,
+ e.iq['error']['condition'])
+ except IqTimeout:
+ logging.info("No response from %s", self.pingjid)
+ finally:
self.disconnect()
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index e240a0d6..c2d89c46 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -12,7 +12,7 @@ from sleekxmpp.plugins.base import register_plugin, load_plugin
__all__ = [
# Non-standard
- 'gmail_notify', # Gmail searching and notifications
+ 'gmail', # Gmail searching and notifications
# XEPS
'xep_0004', # Data Forms
diff --git a/sleekxmpp/plugins/gmail/__init__.py b/sleekxmpp/plugins/gmail/__init__.py
new file mode 100644
index 00000000..a87c78bb
--- /dev/null
+++ b/sleekxmpp/plugins/gmail/__init__.py
@@ -0,0 +1,15 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.gmail import stanza
+from sleekxmpp.plugins.gmail.notifications import Gmail
+
+
+register_plugin(Gmail)
diff --git a/sleekxmpp/plugins/gmail/notifications.py b/sleekxmpp/plugins/gmail/notifications.py
new file mode 100644
index 00000000..dbc68162
--- /dev/null
+++ b/sleekxmpp/plugins/gmail/notifications.py
@@ -0,0 +1,77 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.stanza import Iq
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import MatchXPath
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.gmail import stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class Gmail(BasePlugin):
+
+ """
+ Google: Gmail Notifications
+
+ Also see <https://developers.google.com/talk/jep_extensions/gmail>.
+ """
+
+ name = 'gmail'
+ description = 'Google: Gmail Notifications'
+ dependencies = set()
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(Iq, stanza.GmailQuery)
+ register_stanza_plugin(Iq, stanza.MailBox)
+ register_stanza_plugin(Iq, stanza.NewMail)
+
+ self.xmpp.register_handler(
+ Callback('Gmail New Mail',
+ MatchXPath('{%s}iq/{%s}%s' % (
+ self.xmpp.default_ns,
+ stanza.NewMail.namespace,
+ stanza.NewMail.name)),
+ self._handle_new_mail))
+
+ self._last_result_time = None
+
+ def plugin_end(self):
+ self.xmpp.remove_handler('Gmail New Mail')
+
+ def _handle_new_mail(self, iq):
+ log.info("Gmail: New email!")
+ iq.reply().send()
+ self.xmpp.event('gmail_notification')
+
+ def check(self, block=True, timeout=None, callback=None):
+ last_time = self._last_result_time
+ self._last_result_time = str(int(time.time() * 1000))
+ return self.search(newer=last_time,
+ block=block,
+ timeout=timeout,
+ callback=callback)
+
+ def search(self, query=None, newer=None, block=True,
+ timeout=None, callback=None):
+ if not query:
+ log.info('Gmail: Checking for new email')
+ else:
+ log.info('Gmail: Searching for emails matching: "%s"', query)
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq['to'] = self.xmpp.boundjid.bare
+ iq['gmail']['search'] = query
+ iq['gmail']['newer_than_time'] = newer
+ return iq.send(block=block, timeout=timeout, callback=callback)
diff --git a/sleekxmpp/plugins/gmail/stanza.py b/sleekxmpp/plugins/gmail/stanza.py
new file mode 100644
index 00000000..e7e308e1
--- /dev/null
+++ b/sleekxmpp/plugins/gmail/stanza.py
@@ -0,0 +1,101 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
+
+
+class GmailQuery(ElementBase):
+ namespace = 'google:mail:notify'
+ name = 'query'
+ plugin_attrib = 'gmail'
+ interfaces = set(['newer_than_time', 'newer_than_tid', 'search'])
+
+ def get_search(self):
+ return self._get_attr('q', '')
+
+ def set_search(self, search):
+ self._set_attr('q', search)
+
+ def del_search(self):
+ self._del_attr('q')
+
+ def get_newer_than_time(self):
+ return self._get_attr('newer-than-time', '')
+
+ def set_newer_than_time(self, value):
+ self._set_attr('newer-than-time', value)
+
+ def del_newer_than_time(self):
+ self._del_attr('newer-than-time')
+
+ def get_newer_than_tid(self):
+ return self._get_attr('newer-than-tid', '')
+
+ def set_newer_than_tid(self, value):
+ self._set_attr('newer-than-tid', value)
+
+ def del_newer_than_tid(self):
+ self._del_attr('newer-than-tid')
+
+
+class MailBox(ElementBase):
+ namespace = 'google:mail:notify'
+ name = 'mailbox'
+ plugin_attrib = 'gmail_messages'
+ interfaces = set(['result_time', 'url', 'matched', 'estimate'])
+
+ def get_matched(self):
+ return self._get_attr('total-matched', '')
+
+ def get_estimate(self):
+ return self._get_attr('total-estimate', '') == '1'
+
+ def get_result_time(self):
+ return self._get_attr('result-time', '')
+
+
+class MailThread(ElementBase):
+ namespace = 'google:mail:notify'
+ name = 'mail-thread-info'
+ plugin_attrib = 'thread'
+ plugin_multi_attrib = 'threads'
+ interfaces = set(['tid', 'participation', 'messages', 'date',
+ 'senders', 'url', 'labels', 'subject', 'snippet'])
+ sub_interfaces = set(['labels', 'subject', 'snippet'])
+
+ def get_senders(self):
+ result = []
+ senders = self.xml.findall('{%s}senders/{%s}sender' % (
+ self.namespace, self.namespace))
+
+ for sender in senders:
+ result.append(MailSender(xml=sender))
+
+ return result
+
+
+class MailSender(ElementBase):
+ namespace = 'google:mail:notify'
+ name = 'sender'
+ plugin_attrib = name
+ interfaces = set(['address', 'name', 'originator', 'unread'])
+
+ def get_originator(self):
+ return self.xml.attrib.get('originator', '0') == '1'
+
+ def get_unread(self):
+ return self.xml.attrib.get('unread', '0') == '1'
+
+
+class NewMail(ElementBase):
+ namespace = 'google:mail:notify'
+ name = 'new-mail'
+ plugin_attrib = 'gmail_notification'
+
+
+register_stanza_plugin(MailBox, MailThread, iterable=True)
diff --git a/sleekxmpp/plugins/gmail_notify.py b/sleekxmpp/plugins/gmail_notify.py
deleted file mode 100644
index fc97a2ab..00000000
--- a/sleekxmpp/plugins/gmail_notify.py
+++ /dev/null
@@ -1,149 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-import logging
-from . import base
-from .. xmlstream.handler.callback import Callback
-from .. xmlstream.matcher.xpath import MatchXPath
-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'
- plugin_attrib = 'gmail'
- interfaces = set(('newer-than-time', 'newer-than-tid', 'q', 'search'))
-
- def getSearch(self):
- return self['q']
-
- def setSearch(self, search):
- self['q'] = search
-
- def delSearch(self):
- del self['q']
-
-
-class MailBox(ElementBase):
- namespace = 'google:mail:notify'
- name = 'mailbox'
- plugin_attrib = 'mailbox'
- 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,
- MailThread.name)):
- threads.append(MailThread(xml=threadXML, parent=None))
- return threads
-
- def getMatched(self):
- return self['total-matched']
-
- def getEstimate(self):
- return self['total-estimate'] == '1'
-
-
-class MailThread(ElementBase):
- namespace = 'google:mail:notify'
- name = 'mail-thread-info'
- plugin_attrib = 'thread'
- 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)
- if sendersXML is not None:
- for senderXML in sendersXML.findall('{%s}sender' % self.namespace):
- senders.append(MailSender(xml=senderXML, parent=None))
- return senders
-
-
-class MailSender(ElementBase):
- namespace = 'google:mail:notify'
- name = 'sender'
- plugin_attrib = 'sender'
- interfaces = set(('address', 'name', 'originator', 'unread'))
-
- def getOriginator(self):
- return self.xml.attrib.get('originator', '0') == '1'
-
- def getUnread(self):
- return self.xml.attrib.get('unread', '0') == '1'
-
-
-class NewMail(ElementBase):
- namespace = 'google:mail:notify'
- name = 'new-mail'
- plugin_attrib = 'new-mail'
-
-
-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,
- MailBox.namespace,
- MailBox.name)),
- self.handle_gmail))
-
- self.xmpp.registerHandler(
- Callback('Gmail New Mail',
- MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,
- NewMail.namespace,
- NewMail.name)),
- self.handle_new_mail))
-
- registerStanzaPlugin(Iq, GmailQuery)
- registerStanzaPlugin(Iq, MailBox)
- registerStanzaPlugin(Iq, NewMail)
-
- self.last_result_time = None
-
- def handle_gmail(self, iq):
- mailbox = iq['mailbox']
- approx = ' approximately' if mailbox['estimated'] else ''
- 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):
- log.info("Gmail: New emails received!")
- self.xmpp.event('gmail_notify')
- self.checkEmail()
-
- def getEmail(self, query=None):
- return self.search(query)
-
- def checkEmail(self):
- return self.search(newer=self.last_result_time)
-
- def search(self, query=None, newer=None):
- if query is None:
- log.info("Gmail: Checking for new emails")
- else:
- log.info('Gmail: Searching for emails matching: "%s"', query)
- iq = self.xmpp.Iq()
- iq['type'] = 'get'
- iq['to'] = self.xmpp.boundjid.bare
- iq['gmail']['q'] = query
- iq['gmail']['newer-than-time'] = newer
- return iq.send()
diff --git a/sleekxmpp/plugins/google_nosave/__init__.py b/sleekxmpp/plugins/google_nosave/__init__.py
new file mode 100644
index 00000000..eba50a35
--- /dev/null
+++ b/sleekxmpp/plugins/google_nosave/__init__.py
@@ -0,0 +1,15 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.google_nosave import stanza
+from sleekxmpp.plugins.google_nosave.nosave import GoogleNoSave
+
+
+register_plugin(GoogleNoSave)
diff --git a/sleekxmpp/plugins/google_nosave/nosave.py b/sleekxmpp/plugins/google_nosave/nosave.py
new file mode 100644
index 00000000..1d3b36db
--- /dev/null
+++ b/sleekxmpp/plugins/google_nosave/nosave.py
@@ -0,0 +1,83 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.stanza import Iq, Message
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.google_nosave import stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class GoogleNoSave(BasePlugin):
+
+ """
+ Google: Off the Record Chats
+
+ NOTE: This is NOT an encryption method.
+
+ Also see <https://developers.google.com/talk/jep_extensions/otr>.
+ """
+
+ name = 'google_nosave'
+ description = 'Google: Off the Record Chats'
+ dependencies = set(['google_settings'])
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(Message, stanza.NoSave)
+ register_stanza_plugin(Iq, stanza.NoSaveQuery)
+
+ self.xmpp.register_handler(
+ Callback('Google Nosave',
+ StanzaPath('iq@type=set/google_nosave'),
+ self._handle_nosave_change))
+
+ def plugin_end(self):
+ self.xmpp.remove_handler('Google Nosave')
+
+ def enable(self, jid=None, block=True, timeout=None, callback=None):
+ if jid is None:
+ self.xmpp['google_settings'].update({'archiving_enabled': False},
+ block=block, timeout=timeout, callback=callback)
+ else:
+ iq = self.xmpp.Iq()
+ iq['type'] = 'set'
+ iq['google_nosave']['item']['jid'] = jid
+ iq['google_nosave']['item']['value'] = True
+ return iq.send(block=block, timeout=timeout, callback=callback)
+
+ def disable(self, jid=None, block=True, timeout=None, callback=None):
+ if jid is None:
+ self.xmpp['google_settings'].update({'archiving_enabled': True},
+ block=block, timeout=timeout, callback=callback)
+ else:
+ iq = self.xmpp.Iq()
+ iq['type'] = 'set'
+ iq['google_nosave']['item']['jid'] = jid
+ iq['google_nosave']['item']['value'] = False
+ return iq.send(block=block, timeout=timeout, callback=callback)
+
+ def get(self, block=True, timeout=None, callback=None):
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq.enable('google_nosave')
+ return iq.send(block=block, timeout=timeout, callback=callback)
+
+ def _handle_nosave_change(self, iq):
+ reply = self.xmpp.Iq()
+ reply['type'] = 'result'
+ reply['id'] = iq['id']
+ reply['to'] = iq['from']
+ reply.send()
+ self.xmpp.event('google_nosave_change', iq)
diff --git a/sleekxmpp/plugins/google_nosave/stanza.py b/sleekxmpp/plugins/google_nosave/stanza.py
new file mode 100644
index 00000000..d8701322
--- /dev/null
+++ b/sleekxmpp/plugins/google_nosave/stanza.py
@@ -0,0 +1,59 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.jid import JID
+from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
+
+
+class NoSave(ElementBase):
+ name = 'x'
+ namespace = 'google:nosave'
+ plugin_attrib = 'google_nosave'
+ interfaces = set(['value'])
+
+ def get_value(self):
+ return self._get_attr('value', '') == 'enabled'
+
+ def set_value(self, value):
+ self._set_attr('value', 'enabled' if value else 'disabled')
+
+
+class NoSaveQuery(ElementBase):
+ name = 'query'
+ namespace = 'google:nosave'
+ plugin_attrib = 'google_nosave'
+ interfaces = set()
+
+
+class Item(ElementBase):
+ name = 'item'
+ namespace = 'google:nosave'
+ plugin_attrib = 'item'
+ plugin_multi_attrib = 'items'
+ interfaces = set(['jid', 'source', 'value'])
+
+ def get_value(self):
+ return self._get_attr('value', '') == 'enabled'
+
+ def set_value(self, value):
+ self._set_attr('value', 'enabled' if value else 'disabled')
+
+ def get_jid(self):
+ return JID(self._get_attr('jid', ''))
+
+ def set_jid(self, value):
+ self._set_attr('jid', str(value))
+
+ def get_source(self):
+ return JID(self._get_attr('source', ''))
+
+ def set_source(self):
+ self._set_attr('source', str(value))
+
+
+register_stanza_plugin(NoSaveQuery, Item)
diff --git a/sleekxmpp/plugins/google_settings/__init__.py b/sleekxmpp/plugins/google_settings/__init__.py
new file mode 100644
index 00000000..ef4b2342
--- /dev/null
+++ b/sleekxmpp/plugins/google_settings/__init__.py
@@ -0,0 +1,15 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.google_settings import stanza
+from sleekxmpp.plugins.google_settings.settings import GoogleSettings
+
+
+register_plugin(GoogleSettings)
diff --git a/sleekxmpp/plugins/google_settings/settings.py b/sleekxmpp/plugins/google_settings/settings.py
new file mode 100644
index 00000000..6bd209c7
--- /dev/null
+++ b/sleekxmpp/plugins/google_settings/settings.py
@@ -0,0 +1,68 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.stanza import Iq
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.google_settings import stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class GoogleSettings(BasePlugin):
+
+ """
+ Google: Gmail Notifications
+
+ Also see <https://developers.google.com/talk/jep_extensions/usersettings>.
+ """
+
+ name = 'google_settings'
+ description = 'Google: User Settings'
+ dependencies = set()
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(Iq, stanza.UserSettings)
+
+ self.xmpp.register_handler(
+ Callback('Google Settings',
+ StanzaPath('iq@type=set/google_settings'),
+ self._handle_settings_change))
+
+ def plugin_end(self):
+ self.xmpp.remove_handler('Google Settings')
+
+ def get(self, block=True, timeout=None, callback=None):
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq.enable('google_settings')
+ return iq.send(block=block, timeout=timeout, callback=callback)
+
+ def update(self, settings, block=True, timeout=None, callback=None):
+ iq = self.xmpp.Iq()
+ iq['type'] = 'set'
+ iq.enable('google_settings')
+
+ for setting, value in settings.items():
+ iq['google_settings'][setting] = value
+
+ return iq.send(block=block, timeout=timeout, callback=callback)
+
+ def _handle_settings_change(self, iq):
+ reply = self.xmpp.Iq()
+ reply['type'] = 'result'
+ reply['id'] = iq['id']
+ reply['to'] = iq['from']
+ reply.send()
+ self.xmpp.event('google_settings_change', iq)
diff --git a/sleekxmpp/plugins/google_settings/stanza.py b/sleekxmpp/plugins/google_settings/stanza.py
new file mode 100644
index 00000000..d8161770
--- /dev/null
+++ b/sleekxmpp/plugins/google_settings/stanza.py
@@ -0,0 +1,110 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
+
+
+class UserSettings(ElementBase):
+ name = 'usersetting'
+ namespace = 'google:setting'
+ plugin_attrib = 'google_settings'
+ interfaces = set(['auto_accept_suggestions',
+ 'mail_notifications',
+ 'archiving_enabled',
+ 'gmail',
+ 'email_verified',
+ 'domain_privacy_notice',
+ 'display_name'])
+
+ def _get_setting(self, setting):
+ xml = self.xml.find('{%s}%s' % (self.namespace, setting))
+ if xml is not None:
+ return xml.attrib.get('value', '') == 'true'
+ return False
+
+ def _set_setting(self, setting, value):
+ self._del_setting(setting)
+ if value in (True, False):
+ xml = ET.Element('{%s}%s' % (self.namespace, setting))
+ xml.attrib['value'] = 'true' if value else 'false'
+ self.xml.append(xml)
+
+ def _del_setting(self, setting):
+ xml = self.xml.find('{%s}%s' % (self.namespace, setting))
+ if xml is not None:
+ self.xml.remove(xml)
+
+ def get_display_name(self):
+ xml = self.xml.find('{%s}%s' % (self.namespace, 'displayname'))
+ if xml is not None:
+ return xml.attrib.get('value', '')
+ return ''
+
+ def set_display_name(self, value):
+ self._del_setting(setting)
+ if value:
+ xml = ET.Element('{%s}%s' % (self.namespace, 'displayname'))
+ xml.attrib['value'] = value
+ self.xml.append(xml)
+
+ def del_display_name(self):
+ self._del_setting('displayname')
+
+ def get_auto_accept_suggestions(self):
+ return self._get_setting('autoacceptsuggestions')
+
+ def get_mail_notifications(self):
+ return self._get_setting('mailnotifications')
+
+ def get_archiving_enabled(self):
+ return self._get_setting('archivingenabled')
+
+ def get_gmail(self):
+ return self._get_setting('gmail')
+
+ def get_email_verified(self):
+ return self._get_setting('emailverified')
+
+ def get_domain_privacy_notice(self):
+ return self._get_setting('domainprivacynotice')
+
+ def set_auto_accept_suggestions(self, value):
+ self._set_setting('autoacceptsuggestions', value)
+
+ def set_mail_notifications(self, value):
+ self._set_setting('mailnotifications', value)
+
+ def set_archiving_enabled(self, value):
+ self._set_setting('archivingenabled', value)
+
+ def set_gmail(self, value):
+ self._set_setting('gmail', value)
+
+ def set_email_verified(self, value):
+ self._set_setting('emailverified', value)
+
+ def set_domain_privacy_notice(self, value):
+ self._set_setting('domainprivacynotice', value)
+
+ def del_auto_accept_suggestions(self):
+ self._del_setting('autoacceptsuggestions')
+
+ def del_mail_notifications(self):
+ self._del_setting('mailnotifications')
+
+ def del_archiving_enabled(self):
+ self._del_setting('archivingenabled')
+
+ def del_gmail(self):
+ self._del_setting('gmail')
+
+ def del_email_verified(self):
+ self._del_setting('emailverified')
+
+ def del_domain_privacy_notice(self):
+ self._del_setting('domainprivacynotice')
diff --git a/sleekxmpp/plugins/jobs.py b/sleekxmpp/plugins/jobs.py
deleted file mode 100644
index cb9deba8..00000000
--- a/sleekxmpp/plugins/jobs.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from . import base
-import logging
-from xml.etree import cElementTree as ET
-
-
-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
-
- def createJob(self, host, node, jobid=None, payload=None):
- return self.xmpp.plugin['xep_0060'].setItem(host, node, ((jobid, payload),))
-
- def claimJob(self, host, node, jobid, ifrom=None):
- return self._setState(host, node, jobid, ET.Element('{http://andyet.net/protocol/pubsubjob}claimed'))
-
- def unclaimJob(self, host, node, jobid):
- return self._setState(host, node, jobid, ET.Element('{http://andyet.net/protocol/pubsubjob}unclaimed'))
-
- def finishJob(self, host, node, jobid, payload=None):
- finished = ET.Element('{http://andyet.net/protocol/pubsubjob}finished')
- if payload is not None:
- finished.append(payload)
- return self._setState(host, node, jobid, finished)
-
- def _setState(self, host, node, jobid, state, ifrom=None):
- iq = self.xmpp.Iq()
- iq['to'] = host
- if ifrom: iq['from'] = ifrom
- iq['type'] = 'set'
- iq['psstate']['node'] = node
- iq['psstate']['item'] = jobid
- iq['psstate']['payload'] = state
- result = iq.send()
- if result is None or type(result) == bool or result['type'] != 'result':
- log.error("Unable to change %s:%s to %s", node, jobid, state)
- return False
- return True
-
diff --git a/sleekxmpp/plugins/old_0050.py b/sleekxmpp/plugins/old_0050.py
deleted file mode 100644
index 6e969a51..00000000
--- a/sleekxmpp/plugins/old_0050.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-from __future__ import with_statement
-from . import base
-import logging
-from xml.etree import cElementTree as ET
-import time
-
-class old_0050(base.base_plugin):
- """
- XEP-0050 Ad-Hoc Commands
- """
-
- def plugin_init(self):
- self.xep = '0050'
- self.description = 'Ad-Hoc Commands'
- self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='__None__'/></iq>" % self.xmpp.default_ns, self.handler_command, name='Ad-Hoc None')
- self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='execute'/></iq>" % self.xmpp.default_ns, self.handler_command, name='Ad-Hoc Execute')
- self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='next'/></iq>" % self.xmpp.default_ns, self.handler_command_next, name='Ad-Hoc Next', threaded=True)
- self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='cancel'/></iq>" % self.xmpp.default_ns, self.handler_command_cancel, name='Ad-Hoc Cancel')
- self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='complete'/></iq>" % self.xmpp.default_ns, self.handler_command_complete, name='Ad-Hoc Complete')
- self.commands = {}
- self.sessions = {}
- self.sd = self.xmpp.plugin['xep_0030']
-
- def post_init(self):
- base.base_plugin.post_init(self)
- self.sd.add_feature('http://jabber.org/protocol/commands')
-
- def addCommand(self, node, name, form, pointer=None, multi=False):
- self.sd.add_item(None, name, 'http://jabber.org/protocol/commands', node)
- self.sd.add_identity('automation', 'command-node', name, node)
- self.sd.add_feature('http://jabber.org/protocol/commands', node)
- self.sd.add_feature('jabber:x:data', node)
- self.commands[node] = (name, form, pointer, multi)
-
- def getNewSession(self):
- return str(time.time()) + '-' + self.xmpp.getNewId()
-
- def handler_command(self, xml):
- in_command = xml.find('{http://jabber.org/protocol/commands}command')
- sessionid = in_command.get('sessionid', None)
- node = in_command.get('node')
- sessionid = self.getNewSession()
- name, form, pointer, multi = self.commands[node]
- self.sessions[sessionid] = {}
- self.sessions[sessionid]['jid'] = xml.get('from')
- self.sessions[sessionid]['to'] = xml.get('to')
- self.sessions[sessionid]['past'] = [(form, None)]
- self.sessions[sessionid]['next'] = pointer
- npointer = pointer
- if multi:
- actions = ['next']
- status = 'executing'
- else:
- if pointer is None:
- status = 'completed'
- actions = []
- else:
- status = 'executing'
- actions = ['complete']
- self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=form, id=xml.attrib['id'], sessionid=sessionid, status=status, actions=actions))
-
- def handler_command_complete(self, xml):
- in_command = xml.find('{http://jabber.org/protocol/commands}command')
- sessionid = in_command.get('sessionid', None)
- pointer = self.sessions[sessionid]['next']
- results = self.xmpp.plugin['old_0004'].makeForm('result')
- results.fromXML(in_command.find('{jabber:x:data}x'))
- pointer(results,sessionid)
- self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=None, id=xml.attrib['id'], sessionid=sessionid, status='completed', actions=[]))
- del self.sessions[in_command.get('sessionid')]
-
-
- def handler_command_next(self, xml):
- in_command = xml.find('{http://jabber.org/protocol/commands}command')
- sessionid = in_command.get('sessionid', None)
- pointer = self.sessions[sessionid]['next']
- results = self.xmpp.plugin['old_0004'].makeForm('result')
- results.fromXML(in_command.find('{jabber:x:data}x'))
- form, npointer, next = pointer(results,sessionid)
- self.sessions[sessionid]['next'] = npointer
- self.sessions[sessionid]['past'].append((form, pointer))
- actions = []
- actions.append('prev')
- if npointer is None:
- status = 'completed'
- else:
- status = 'executing'
- if next:
- actions.append('next')
- else:
- actions.append('complete')
- self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=form, id=xml.attrib['id'], sessionid=sessionid, status=status, actions=actions))
-
- def handler_command_cancel(self, xml):
- command = xml.find('{http://jabber.org/protocol/commands}command')
- try:
- del self.sessions[command.get('sessionid')]
- except:
- pass
- self.xmpp.send(self.makeCommand(xml.attrib['from'], command.attrib['node'], id=xml.attrib['id'], sessionid=command.attrib['sessionid'], status='canceled'))
-
- def makeCommand(self, to, node, id=None, form=None, sessionid=None, status='executing', actions=[]):
- if not id:
- id = self.xmpp.getNewId()
- iq = self.xmpp.makeIqResult(id)
- iq.attrib['from'] = self.xmpp.boundjid.full
- iq.attrib['to'] = to
- command = ET.Element('{http://jabber.org/protocol/commands}command')
- command.attrib['node'] = node
- command.attrib['status'] = status
- xmlactions = ET.Element('actions')
- for action in actions:
- xmlactions.append(ET.Element(action))
- if xmlactions:
- command.append(xmlactions)
- if not sessionid:
- sessionid = self.getNewSession()
- else:
- iq.attrib['from'] = self.sessions[sessionid]['to']
- command.attrib['sessionid'] = sessionid
- if form is not None:
- if hasattr(form,'getXML'):
- form = form.getXML()
- command.append(form)
- iq.append(command)
- return iq
diff --git a/sleekxmpp/plugins/old_0060.py b/sleekxmpp/plugins/old_0060.py
deleted file mode 100644
index 93124fca..00000000
--- a/sleekxmpp/plugins/old_0060.py
+++ /dev/null
@@ -1,313 +0,0 @@
-from __future__ import with_statement
-from . import base
-import logging
-#from xml.etree import cElementTree as ET
-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
- """
-
- 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')
- create.set('node', node)
- pubsub.append(create)
- configure = ET.Element('configure')
- if collection:
- ntype = 'collection'
- #if config is None:
- # submitform = self.xmpp.plugin['xep_0004'].makeForm('submit')
- #else:
- if config is not None:
- submitform = config
- if 'FORM_TYPE' in submitform.field:
- submitform.field['FORM_TYPE'].setValue('http://jabber.org/protocol/pubsub#node_config')
- else:
- submitform.addField('FORM_TYPE', 'hidden', value='http://jabber.org/protocol/pubsub#node_config')
- if ntype:
- if 'pubsub#node_type' in submitform.field:
- submitform.field['pubsub#node_type'].setValue(ntype)
- else:
- submitform.addField('pubsub#node_type', value=ntype)
- else:
- if 'pubsub#node_type' in submitform.field:
- submitform.field['pubsub#node_type'].setValue('leaf')
- else:
- submitform.addField('pubsub#node_type', value='leaf')
- submitform['type'] = 'submit'
- configure.append(submitform.xml)
- pubsub.append(configure)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- 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')
- subscribe.attrib['node'] = node
- if subscribee is None:
- if bare:
- subscribe.attrib['jid'] = self.xmpp.boundjid.bare
- else:
- subscribe.attrib['jid'] = self.xmpp.boundjid.full
- else:
- subscribe.attrib['jid'] = subscribee
- pubsub.append(subscribe)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- 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')
- unsubscribe.attrib['node'] = node
- if subscribee is None:
- if bare:
- unsubscribe.attrib['jid'] = self.xmpp.boundjid.bare
- else:
- unsubscribe.attrib['jid'] = self.xmpp.boundjid.full
- else:
- unsubscribe.attrib['jid'] = subscribee
- pubsub.append(unsubscribe)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- 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:
- configure = ET.Element('configure')
- configure.attrib['node'] = node
- else:
- configure = ET.Element('default')
- pubsub.append(configure)
- #TODO: Add configure support.
- iq = self.xmpp.makeIqGet()
- iq.append(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- #self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse)
- result = iq.send()
- if result is None or result == False or result['type'] == 'error':
- 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:
- 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')
- subscriptions.attrib['node'] = node
- pubsub.append(subscriptions)
- iq = self.xmpp.makeIqGet()
- iq.append(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- result = iq.send()
- if result is None or result == False or result['type'] == 'error':
- 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')
- if results is None:
- return False
- subs = {}
- for sub in results:
- subs[sub.get('jid')] = sub.get('subscription')
- return subs
-
- def getNodeAffiliations(self, jid, node):
- pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
- affiliations = ET.Element('affiliations')
- affiliations.attrib['node'] = node
- pubsub.append(affiliations)
- iq = self.xmpp.makeIqGet()
- iq.append(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- result = iq.send()
- if result is None or result == False or result['type'] == 'error':
- 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')
- if results is None:
- return False
- subs = {}
- for sub in results:
- subs[sub.get('jid')] = sub.get('affiliation')
- return subs
-
- def deleteNode(self, jid, node):
- pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
- iq = self.xmpp.makeIqSet()
- delete = ET.Element('delete')
- delete.attrib['node'] = node
- pubsub.append(delete)
- iq.append(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- result = iq.send()
- if result is not None and result is not False and result['type'] != 'error':
- 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')
- configure.attrib['node'] = node
- config = config.getXML('submit')
- configure.append(config)
- pubsub.append(configure)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- result = iq.send()
- 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')
- publish.attrib['node'] = node
- for pub_item in items:
- id, payload = pub_item
- item = ET.Element('item')
- if id is not None:
- item.attrib['id'] = id
- item.append(payload)
- publish.append(item)
- pubsub.append(publish)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- 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)
-
- def deleteItem(self, jid, node, item):
- pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
- retract = ET.Element('retract')
- retract.attrib['node'] = node
- itemn = ET.Element('item')
- itemn.attrib['id'] = item
- retract.append(itemn)
- pubsub.append(retract)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- 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')
- nodes = {}
- if items is not None and items is not False:
- 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')
- nodeitems = []
- if items is not None and items is not False:
- for item in items:
- nodeitems.append(item.get('node'))
- return nodeitems
-
- def addNodeToCollection(self, jid, child, parent=''):
- config = self.getNodeConfig(jid, child)
- if not config or config is None:
- self.lasterror = "Config Error"
- return False
- try:
- config.field['pubsub#collection'].setValue(parent)
- except KeyError:
- 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
- return True
-
- def modifyAffiliation(self, ps_jid, node, user_jid, affiliation):
- if affiliation not in ('owner', 'publisher', 'member', 'none', 'outcast'):
- raise TypeError
- pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
- affs = ET.Element('affiliations')
- affs.attrib['node'] = node
- aff = ET.Element('affiliation')
- aff.attrib['jid'] = user_jid
- aff.attrib['affiliation'] = affiliation
- affs.append(aff)
- pubsub.append(affs)
- iq = self.xmpp.makeIqSet(pubsub)
- iq.attrib['to'] = ps_jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq['id']
- result = iq.send()
- if result is None or result is False or result['type'] == 'error':
- return False
- return True
-
- def addNodeToCollection(self, jid, child, parent=''):
- config = self.getNodeConfig(jid, child)
- if not config or config is None:
- self.lasterror = "Config Error"
- return False
- try:
- config.field['pubsub#collection'].setValue(parent)
- except KeyError:
- 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
- return True
-
- def removeNodeFromCollection(self, jid, child):
- self.addNodeToCollection(jid, child, '')
-
diff --git a/sleekxmpp/plugins/xep_0092/version.py b/sleekxmpp/plugins/xep_0092/version.py
index 35813e1d..b16ad516 100644
--- a/sleekxmpp/plugins/xep_0092/version.py
+++ b/sleekxmpp/plugins/xep_0092/version.py
@@ -70,7 +70,7 @@ class XEP_0092(BasePlugin):
iq['software_version']['os'] = self.os
iq.send()
- def get_version(self, jid, ifrom=None):
+ def get_version(self, jid, ifrom=None, block=True, timeout=None, callback=None):
"""
Retrieve the software version of a remote agent.
@@ -82,14 +82,4 @@ class XEP_0092(BasePlugin):
iq['from'] = ifrom
iq['type'] = 'get'
iq['query'] = Version.namespace
-
- result = iq.send()
-
- if result and result['type'] != 'error':
- values = result['software_version'].values
- del values['lang']
- return values
- return False
-
-
-XEP_0092.getVersion = XEP_0092.get_version
+ return iq.send(block=block, timeout=timeout, callback=callback)
diff --git a/sleekxmpp/plugins/xep_0199/ping.py b/sleekxmpp/plugins/xep_0199/ping.py
index 0bdeabf3..e095a551 100644
--- a/sleekxmpp/plugins/xep_0199/ping.py
+++ b/sleekxmpp/plugins/xep_0199/ping.py
@@ -9,8 +9,8 @@
import time
import logging
-import sleekxmpp
-from sleekxmpp import Iq
+from sleekxmpp.jid import JID
+from sleekxmpp.stanza import Iq
from sleekxmpp.exceptions import IqError, IqTimeout
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.xmlstream.matcher import StanzaPath
@@ -38,7 +38,7 @@ class XEP_0199(BasePlugin):
keepalive -- If True, periodically send ping requests
to the server. If a ping is not answered,
the connection will be reset.
- frequency -- Time in seconds between keepalive pings.
+ interval -- Time in seconds between keepalive pings.
Defaults to 300 seconds.
timeout -- Time in seconds to wait for a ping response.
Defaults to 30 seconds.
@@ -53,7 +53,7 @@ class XEP_0199(BasePlugin):
stanza = stanza
default_config = {
'keepalive': False,
- 'frequency': 300,
+ 'interval': 300,
'timeout': 30
}
@@ -61,6 +61,7 @@ class XEP_0199(BasePlugin):
"""
Start the XEP-0199 plugin.
"""
+
register_stanza_plugin(Iq, Ping)
self.xmpp.register_handler(
@@ -70,88 +71,70 @@ class XEP_0199(BasePlugin):
if self.keepalive:
self.xmpp.add_event_handler('session_start',
- self._handle_keepalive,
+ self.enable_keepalive,
threaded=True)
self.xmpp.add_event_handler('session_end',
- self._handle_session_end)
+ self.disable_keepalive)
def plugin_end(self):
self.xmpp['xep_0030'].del_feature(feature=Ping.namespace)
self.xmpp.remove_handler('Ping')
if self.keepalive:
self.xmpp.del_event_handler('session_start',
- self._handle_keepalive)
+ self.enable_keepalive)
self.xmpp.del_event_handler('session_end',
- self._handle_session_end)
+ self.disable_keepalive)
def session_bind(self, jid):
self.xmpp['xep_0030'].add_feature(Ping.namespace)
- def _handle_keepalive(self, event):
- """
- Begin periodic pinging of the server. If a ping is not
- answered, the connection will be restarted.
-
- The pinging interval can be adjused using self.frequency
- before beginning processing.
+ def enable_keepalive(self, interval=None, timeout=None):
+ if interval:
+ self.interval = interval
+ if timeout:
+ self.timeout = timeout
- Arguments:
- event -- The session_start event.
- """
- def scheduled_ping():
- """Send ping request to the server."""
- log.debug("Pinging...")
- try:
- self.send_ping(self.xmpp.boundjid.host, self.timeout)
- except IqError:
- log.debug("Ping response was an error." + \
- "Requesting Reconnect.")
- self.xmpp.reconnect()
- except IqTimeout:
- log.debug("Did not recieve ping back in time." + \
- "Requesting Reconnect.")
- self.xmpp.reconnect()
-
- self.xmpp.schedule('Ping Keep Alive',
- self.frequency,
- scheduled_ping,
+ self.keepalive = True
+ self.xmpp.schedule('Ping keepalive',
+ self.interval,
+ self._keepalive,
repeat=True)
- def _handle_session_end(self, event):
- self.xmpp.scheduler.remove('Ping Keep Alive')
+ def disable_keepalive(self, event=None):
+ self.xmpp.scheduler.remove('Ping keepalive')
- def _handle_ping(self, iq):
- """
- Automatically reply to ping requests.
+ def _keepalive(self, event):
+ log.debug("Keepalive ping...")
+ try:
+ rtt = self.ping(self.xmpp.boundjid.host, self.timeout)
+ except IqTimeout:
+ log.debug("Did not recieve ping back in time." + \
+ "Requesting Reconnect.")
+ self.xmpp.reconnect()
+ else:
+ log.debug('Keepalive RTT: %s' % rtt)
- Arguments:
- iq -- The ping request.
- """
+ def _handle_ping(self, iq):
+ """Automatically reply to ping requests."""
log.debug("Pinged by %s", iq['from'])
iq.reply().send()
- def send_ping(self, jid, timeout=None, errorfalse=False,
- ifrom=None, block=True, callback=None):
- """
- Send a ping request and calculate the response time.
+ def send_ping(self, jid, ifrom=None, block=True, timeout=None, callback=None):
+ """Send a ping request.
Arguments:
jid -- The JID that will receive the ping.
- timeout -- Time in seconds to wait for a response.
- Defaults to self.timeout.
- errorfalse -- Indicates if False should be returned
- if an error stanza is received. Defaults
- to False.
ifrom -- Specifiy the sender JID.
block -- Indicate if execution should block until
a pong response is received. Defaults
to True.
+ timeout -- Time in seconds to wait for a response.
+ Defaults to self.timeout.
callback -- Optional handler to execute when a pong
is received. Useful in conjunction with
the option block=False.
"""
- log.debug("Pinging %s", jid)
- if timeout is None:
+ if not timeout:
timeout = self.timeout
iq = self.xmpp.Iq()
@@ -160,21 +143,43 @@ class XEP_0199(BasePlugin):
iq['from'] = ifrom
iq.enable('ping')
- start_time = time.clock()
+ return iq.send(block=block, timeout=timeout, callback=callback)
- try:
- resp = iq.send(block=block,
- timeout=timeout,
- callback=callback)
- except IqError as err:
- resp = err.iq
+ def ping(self, jid=None, ifrom=None, timeout=None):
+ """Send a ping request and calculate RTT.
- end_time = time.clock()
-
- delay = end_time - start_time
+ Arguments:
+ jid -- The JID that will receive the ping.
+ ifrom -- Specifiy the sender JID.
+ timeout -- Time in seconds to wait for a response.
+ Defaults to self.timeout.
+ """
+ if not jid:
+ if self.xmpp.is_component:
+ jid = self.xmpp.server
+ else:
+ jid = self.xmpp.boundjid.host
+ jid = JID(jid)
+ if jid == self.xmpp.boundjid.host or \
+ self.xmpp.is_component and jid == self.xmpp.server:
+ own_host = True
+
+ if not timeout:
+ timeout = self.timeout
- if not block:
- return None
+ start = time.time()
- log.debug("Pong: %s %f", jid, delay)
- return delay
+ log.debug('Pinging %s' % jid)
+ try:
+ self.send_ping(jid, ifrom=ifrom, timeout=timeout)
+ except IqError as e:
+ if own_host:
+ rtt = time.time() - start
+ log.debug('Pinged %s, RTT: %s', jid, rtt)
+ return rtt
+ else:
+ raise e
+ else:
+ rtt = time.time() - start
+ log.debug('Pinged %s, RTT: %s', jid, rtt)
+ return rtt
diff --git a/sleekxmpp/plugins/xep_0231/bob.py b/sleekxmpp/plugins/xep_0231/bob.py
index d86a5ddf..5e1f590b 100644
--- a/sleekxmpp/plugins/xep_0231/bob.py
+++ b/sleekxmpp/plugins/xep_0231/bob.py
@@ -10,7 +10,7 @@
import logging
import hashlib
-from sleekxmpp.stanza import Iq
+from sleekxmpp.stanza import Iq, Message, Presence
from sleekxmpp.exceptions import XMPPError
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
@@ -36,6 +36,8 @@ class XEP_0231(BasePlugin):
self._cids = {}
register_stanza_plugin(Iq, BitsOfBinary)
+ register_stanza_plugin(Message, BitsOfBinary)
+ register_stanza_plugin(Presence, BitsOfBinary)
self.xmpp.register_handler(
Callback('Bits of Binary - Iq',
diff --git a/sleekxmpp/xmlstream/matcher/xmlmask.py b/sleekxmpp/xmlstream/matcher/xmlmask.py
index a0568f08..cb202448 100644
--- a/sleekxmpp/xmlstream/matcher/xmlmask.py
+++ b/sleekxmpp/xmlstream/matcher/xmlmask.py
@@ -14,12 +14,6 @@ from sleekxmpp.xmlstream.stanzabase import ET
from sleekxmpp.xmlstream.matcher.base import MatcherBase
-# Flag indicating if the builtin XPath matcher should be used, which
-# uses namespaces, or a custom matcher that ignores namespaces.
-# Changing this will affect ALL XMLMask matchers.
-IGNORE_NS = False
-
-
log = logging.getLogger(__name__)
@@ -39,10 +33,6 @@ class MatchXMLMask(MatcherBase):
:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath`
should be used instead.
- The use of namespaces in the mask comparison is controlled by
- ``IGNORE_NS``. Setting ``IGNORE_NS`` to ``True`` will disable namespace
- based matching for ALL XMLMask matchers.
-
:param criteria: Either an :class:`~xml.etree.ElementTree.Element` XML
object or XML string to use as a mask.
"""
@@ -84,8 +74,6 @@ class MatchXMLMask(MatcherBase):
do not have a specified namespace.
Defaults to ``"__no_ns__"``.
"""
- use_ns = not IGNORE_NS
-
if source is None:
# If the element was not found. May happend during recursive calls.
return False
@@ -96,17 +84,10 @@ class MatchXMLMask(MatcherBase):
mask = ET.fromstring(mask)
except ExpatError:
log.warning("Expat error: %s\nIn parsing: %s", '', mask)
- if not use_ns:
- # Compare the element without using namespaces.
- source_tag = source.tag.split('}', 1)[-1]
- mask_tag = mask.tag.split('}', 1)[-1]
- if source_tag != mask_tag:
- return False
- else:
- # Compare the element using namespaces
- mask_ns_tag = "{%s}%s" % (self.default_ns, mask.tag)
- if source.tag not in [mask.tag, mask_ns_tag]:
- return False
+
+ mask_ns_tag = "{%s}%s" % (self.default_ns, mask.tag)
+ if source.tag not in [mask.tag, mask_ns_tag]:
+ return False
# If the mask includes text, compare it.
if mask.text and source.text and \
@@ -122,37 +103,15 @@ class MatchXMLMask(MatcherBase):
# Recursively check subelements.
matched_elements = {}
for subelement in mask:
- if use_ns:
- matched = False
- for other in source.findall(subelement.tag):
- matched_elements[other] = False
- if self._mask_cmp(other, subelement, use_ns):
- if not matched_elements.get(other, False):
- matched_elements[other] = True
- matched = True
- if not matched:
- return False
- else:
- if not self._mask_cmp(self._get_child(source, subelement.tag),
- subelement, use_ns):
- return False
+ matched = False
+ for other in source.findall(subelement.tag):
+ matched_elements[other] = False
+ if self._mask_cmp(other, subelement, use_ns):
+ if not matched_elements.get(other, False):
+ matched_elements[other] = True
+ matched = True
+ if not matched:
+ return False
# Everything matches.
return True
-
- def _get_child(self, xml, tag):
- """Return a child element given its tag, ignoring namespace values.
-
- Returns ``None`` if the child was not found.
-
- :param xml: The :class:`~xml.etree.ElementTree.Element` XML object
- to search for the given child tag.
- :param tag: The name of the subelement to find.
- """
- tag = tag.split('}')[-1]
- try:
- children = [c.tag.split('}')[-1] for c in xml]
- index = children.index(tag)
- except ValueError:
- return None
- return list(xml)[index]
diff --git a/sleekxmpp/xmlstream/matcher/xpath.py b/sleekxmpp/xmlstream/matcher/xpath.py
index 3f03e68e..f3d28429 100644
--- a/sleekxmpp/xmlstream/matcher/xpath.py
+++ b/sleekxmpp/xmlstream/matcher/xpath.py
@@ -9,16 +9,10 @@
:license: MIT, see LICENSE for more details
"""
-from sleekxmpp.xmlstream.stanzabase import ET
+from sleekxmpp.xmlstream.stanzabase import ET, fix_ns
from sleekxmpp.xmlstream.matcher.base import MatcherBase
-# Flag indicating if the builtin XPath matcher should be used, which
-# uses namespaces, or a custom matcher that ignores namespaces.
-# Changing this will affect ALL XPath matchers.
-IGNORE_NS = False
-
-
class MatchXPath(MatcherBase):
"""
@@ -38,6 +32,9 @@ class MatchXPath(MatcherBase):
expressions will be matched without using namespaces.
"""
+ def __init__(self, criteria):
+ self._criteria = fix_ns(criteria)
+
def match(self, xml):
"""
Compare a stanza's XML contents to an XPath expression.
@@ -59,28 +56,4 @@ class MatchXPath(MatcherBase):
x = ET.Element('x')
x.append(xml)
- if not IGNORE_NS:
- # Use builtin, namespace respecting, XPath matcher.
- if x.find(self._criteria) is not None:
- return True
- return False
- else:
- # Remove namespaces from the XPath expression.
- criteria = []
- for ns_block in self._criteria.split('{'):
- criteria.extend(ns_block.split('}')[-1].split('/'))
-
- # Walk the XPath expression.
- xml = x
- for tag in criteria:
- if not tag:
- # Skip empty tag name artifacts from the cleanup phase.
- continue
-
- children = [c.tag.split('}')[-1] for c in xml]
- try:
- index = children.index(tag)
- except ValueError:
- return False
- xml = list(xml)[index]
- return True
+ return x.find(self._criteria) is not None
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 122d7eb4..120373db 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -192,7 +192,7 @@ def fix_ns(xpath, split=False, propagate_ns=True, default_ns=''):
for element in elements:
if element:
# Skip empty entry artifacts from splitting.
- if propagate_ns:
+ if propagate_ns and element[0] != '*':
tag = '{%s}%s' % (namespace, element)
else:
tag = element
diff --git a/tests/test_stream_xep_0092.py b/tests/test_stream_xep_0092.py
index 4a038558..a3960fa2 100644
--- a/tests/test_stream_xep_0092.py
+++ b/tests/test_stream_xep_0092.py
@@ -36,7 +36,9 @@ class TestStreamSet(SleekTest):
def query():
r = self.xmpp['xep_0092'].get_version('foo@bar')
- results.append(r)
+ results.append((r['software_version']['name'],
+ r['software_version']['version'],
+ r['software_version']['os']))
self.stream_start(mode='client', plugins=['xep_0030', 'xep_0092'])
@@ -61,7 +63,7 @@ class TestStreamSet(SleekTest):
t.join()
- expected = [{'name': 'Foo', 'version': '1.0', 'os':'Linux'}]
+ expected = [('Foo', '1.0', 'Linux')]
self.assertEqual(results, expected,
"Did not receive expected results: %s" % results)