summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/plugins')
-rw-r--r--sleekxmpp/plugins/__init__.py4
-rw-r--r--sleekxmpp/plugins/base.py16
-rw-r--r--sleekxmpp/plugins/xep_0027/gpg.py3
-rw-r--r--sleekxmpp/plugins/xep_0027/stanza.py3
-rw-r--r--sleekxmpp/plugins/xep_0030/disco.py12
-rw-r--r--sleekxmpp/plugins/xep_0033.py167
-rw-r--r--sleekxmpp/plugins/xep_0033/__init__.py20
-rw-r--r--sleekxmpp/plugins/xep_0033/addresses.py32
-rw-r--r--sleekxmpp/plugins/xep_0033/stanza.py131
-rw-r--r--sleekxmpp/plugins/xep_0050/stanza.py4
-rw-r--r--sleekxmpp/plugins/xep_0054/stanza.py40
-rw-r--r--sleekxmpp/plugins/xep_0054/vcard_temp.py8
-rw-r--r--sleekxmpp/plugins/xep_0059/stanza.py4
-rw-r--r--sleekxmpp/plugins/xep_0060/stanza/pubsub.py8
-rw-r--r--sleekxmpp/plugins/xep_0060/stanza/pubsub_errors.py4
-rw-r--r--sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py4
-rw-r--r--sleekxmpp/plugins/xep_0078/stanza.py2
-rw-r--r--sleekxmpp/plugins/xep_0080/geoloc.py18
-rw-r--r--sleekxmpp/plugins/xep_0080/stanza.py42
-rw-r--r--sleekxmpp/plugins/xep_0082.py9
-rw-r--r--sleekxmpp/plugins/xep_0084/__init__.py16
-rw-r--r--sleekxmpp/plugins/xep_0084/avatar.py101
-rw-r--r--sleekxmpp/plugins/xep_0084/stanza.py78
-rw-r--r--sleekxmpp/plugins/xep_0086/stanza.py44
-rw-r--r--sleekxmpp/plugins/xep_0092/version.py4
-rw-r--r--sleekxmpp/plugins/xep_0107/user_mood.py4
-rw-r--r--sleekxmpp/plugins/xep_0108/stanza.py8
-rw-r--r--sleekxmpp/plugins/xep_0108/user_activity.py4
-rw-r--r--sleekxmpp/plugins/xep_0115/caps.py18
-rw-r--r--sleekxmpp/plugins/xep_0115/static.py6
-rw-r--r--sleekxmpp/plugins/xep_0118/stanza.py2
-rw-r--r--sleekxmpp/plugins/xep_0118/user_tune.py6
-rw-r--r--sleekxmpp/plugins/xep_0153/vcard_avatar.py6
-rw-r--r--sleekxmpp/plugins/xep_0163.py5
-rw-r--r--sleekxmpp/plugins/xep_0172/user_nick.py2
-rw-r--r--sleekxmpp/plugins/xep_0184/receipt.py4
-rw-r--r--sleekxmpp/plugins/xep_0198/stanza.py5
-rw-r--r--sleekxmpp/plugins/xep_0198/stream_management.py14
-rw-r--r--sleekxmpp/plugins/xep_0202/time.py6
-rw-r--r--sleekxmpp/plugins/xep_0203/__init__.py2
-rw-r--r--sleekxmpp/plugins/xep_0222.py126
-rw-r--r--sleekxmpp/plugins/xep_0223.py126
-rw-r--r--sleekxmpp/plugins/xep_0231/__init__.py2
-rw-r--r--sleekxmpp/plugins/xep_0231/bob.py7
-rw-r--r--sleekxmpp/plugins/xep_0231/stanza.py2
-rw-r--r--sleekxmpp/plugins/xep_0258/__init__.py18
-rw-r--r--sleekxmpp/plugins/xep_0258/security_labels.py40
-rw-r--r--sleekxmpp/plugins/xep_0258/stanza.py142
48 files changed, 1014 insertions, 315 deletions
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index a4be9e65..0fbebf33 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -33,6 +33,7 @@ __all__ = [
# 'xep_0078', # Non-SASL auth. Don't automatically load
'xep_0080', # User Location
'xep_0082', # XMPP Date and Time Profiles
+ 'xep_0084', # User Avatar
'xep_0085', # Chat State Notifications
'xep_0086', # Legacy Error Codes
'xep_0092', # Software Version
@@ -49,7 +50,10 @@ __all__ = [
'xep_0199', # Ping
'xep_0202', # Entity Time
'xep_0203', # Delayed Delivery
+ 'xep_0222', # Persistent Storage of Public Data via Pubsub
+ 'xep_0223', # Persistent Storage of Private Data via Pubsub
'xep_0224', # Attention
'xep_0231', # Bits of Binary
'xep_0249', # Direct MUC Invitations
+ 'xep_0258', # Security Labels in XMPP
]
diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py
index 9a7e1b19..337db2db 100644
--- a/sleekxmpp/plugins/base.py
+++ b/sleekxmpp/plugins/base.py
@@ -31,10 +31,10 @@ log = logging.getLogger(__name__)
PLUGIN_REGISTRY = {}
#: In order to do cascading plugin disabling, reverse dependencies
-#: must be tracked.
+#: must be tracked.
PLUGIN_DEPENDENTS = {}
-#: Only allow one thread to manipulate the plugin registry at a time.
+#: Only allow one thread to manipulate the plugin registry at a time.
REGISTRY_LOCK = threading.RLock()
@@ -75,7 +75,7 @@ def load_plugin(name, module=None):
plugins are in packages matching their name,
even though the plugin class name does not
have to match.
- :param str module: The name of the base module to search
+ :param str module: The name of the base module to search
for the plugin.
"""
try:
@@ -84,7 +84,7 @@ def load_plugin(name, module=None):
module = 'sleekxmpp.plugins.%s' % name
__import__(module)
mod = sys.modules[module]
- except:
+ except ImportError:
module = 'sleekxmpp.features.%s' % name
__import__(module)
mod = sys.modules[module]
@@ -103,11 +103,11 @@ def load_plugin(name, module=None):
# we can work around dependency issues.
plugin.old_style = True
register_plugin(plugin, name)
- except:
+ except ImportError:
log.exception("Unable to load plugin: %s", name)
-class PluginManager(object):
+class PluginManager(object):
def __init__(self, xmpp, config=None):
#: We will track all enabled plugins in a set so that we
#: can enable plugins in batches and pull in dependencies
@@ -181,7 +181,7 @@ class PluginManager(object):
def enable_all(self, names=None, config=None):
"""Enable all registered plugins.
-
+
:param list names: A list of plugin names to enable. If
none are provided, all registered plugins
will be enabled.
@@ -292,7 +292,7 @@ class BasePlugin(object):
def post_init(self):
"""Initialize any cross-plugin state.
-
+
Only needed if the plugin has circular dependencies.
"""
pass
diff --git a/sleekxmpp/plugins/xep_0027/gpg.py b/sleekxmpp/plugins/xep_0027/gpg.py
index ccc7c400..7cc128bd 100644
--- a/sleekxmpp/plugins/xep_0027/gpg.py
+++ b/sleekxmpp/plugins/xep_0027/gpg.py
@@ -81,7 +81,8 @@ class XEP_0027(BasePlugin):
def _sign_presence(self, stanza):
if isinstance(stanza, Presence):
- if stanza['type'] == 'available' or stanza['type'] in Presence.showtypes:
+ if stanza['type'] == 'available' or \
+ stanza['type'] in Presence.showtypes:
stanza['signed'] = stanza['status']
return stanza
diff --git a/sleekxmpp/plugins/xep_0027/stanza.py b/sleekxmpp/plugins/xep_0027/stanza.py
index 927693ad..3170ca6e 100644
--- a/sleekxmpp/plugins/xep_0027/stanza.py
+++ b/sleekxmpp/plugins/xep_0027/stanza.py
@@ -51,6 +51,3 @@ class Encrypted(ElementBase):
if self.xml.text:
return xmpp['xep_0027'].decrypt(self.xml.text, parent['to'])
return None
-
-
-
diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py
index a66ab935..18c1dba2 100644
--- a/sleekxmpp/plugins/xep_0030/disco.py
+++ b/sleekxmpp/plugins/xep_0030/disco.py
@@ -339,8 +339,8 @@ class XEP_0030(BasePlugin):
if local:
log.debug("Looking up local disco#info data " + \
"for %s, node %s.", jid, node)
- info = self.api['get_info'](jid, node,
- kwargs.get('ifrom', None),
+ info = self.api['get_info'](jid, node,
+ kwargs.get('ifrom', None),
kwargs)
info = self._fix_default_info(info)
return self._wrap(kwargs.get('ifrom', None), jid, info)
@@ -348,8 +348,8 @@ class XEP_0030(BasePlugin):
if cached:
log.debug("Looking up cached disco#info data " + \
"for %s, node %s.", jid, node)
- info = self.api['get_cached_info'](jid, node,
- kwargs.get('ifrom', None),
+ info = self.api['get_cached_info'](jid, node,
+ kwargs.get('ifrom', None),
kwargs)
if info is not None:
return self._wrap(kwargs.get('ifrom', None), jid, info)
@@ -405,8 +405,8 @@ class XEP_0030(BasePlugin):
Otherwise the parameter is ignored.
"""
if local or jid is None:
- items = self.api['get_items'](jid, node,
- kwargs.get('ifrom', None),
+ items = self.api['get_items'](jid, node,
+ kwargs.get('ifrom', None),
kwargs)
return self._wrap(kwargs.get('ifrom', None), jid, items)
diff --git a/sleekxmpp/plugins/xep_0033.py b/sleekxmpp/plugins/xep_0033.py
deleted file mode 100644
index feef5a13..00000000
--- a/sleekxmpp/plugins/xep_0033.py
+++ /dev/null
@@ -1,167 +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 sleekxmpp import Message
-from sleekxmpp.xmlstream.handler.callback import Callback
-from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
-from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID
-from sleekxmpp.plugins import BasePlugin, register_plugin
-
-
-class Addresses(ElementBase):
- namespace = 'http://jabber.org/protocol/address'
- name = 'addresses'
- plugin_attrib = 'addresses'
- interfaces = set(('addresses', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
-
- def addAddress(self, atype='to', jid='', node='', uri='', desc='', delivered=False):
- address = Address(parent=self)
- address['type'] = atype
- address['jid'] = jid
- address['node'] = node
- address['uri'] = uri
- address['desc'] = desc
- address['delivered'] = delivered
- return address
-
- def getAddresses(self, atype=None):
- addresses = []
- for addrXML in self.xml.findall('{%s}address' % Address.namespace):
- # ElementTree 1.2.6 does not support [@attr='value'] in findall
- if atype is None or addrXML.attrib.get('type') == atype:
- addresses.append(Address(xml=addrXML, parent=None))
- return addresses
-
- def setAddresses(self, addresses, set_type=None):
- self.delAddresses(set_type)
- for addr in addresses:
- addr = dict(addr)
- # Remap 'type' to 'atype' to match the add method
- if set_type is not None:
- addr['type'] = set_type
- curr_type = addr.get('type', None)
- if curr_type is not None:
- del addr['type']
- addr['atype'] = curr_type
- self.addAddress(**addr)
-
- def delAddresses(self, atype=None):
- if atype is None:
- return
- for addrXML in self.xml.findall('{%s}address' % Address.namespace):
- # ElementTree 1.2.6 does not support [@attr='value'] in findall
- if addrXML.attrib.get('type') == atype:
- self.xml.remove(addrXML)
-
- # --------------------------------------------------------------
-
- def delBcc(self):
- self.delAddresses('bcc')
-
- def delCc(self):
- self.delAddresses('cc')
-
- def delNoreply(self):
- self.delAddresses('noreply')
-
- def delReplyroom(self):
- self.delAddresses('replyroom')
-
- def delReplyto(self):
- self.delAddresses('replyto')
-
- def delTo(self):
- self.delAddresses('to')
-
- # --------------------------------------------------------------
-
- def getBcc(self):
- return self.getAddresses('bcc')
-
- def getCc(self):
- return self.getAddresses('cc')
-
- def getNoreply(self):
- return self.getAddresses('noreply')
-
- def getReplyroom(self):
- return self.getAddresses('replyroom')
-
- def getReplyto(self):
- return self.getAddresses('replyto')
-
- def getTo(self):
- return self.getAddresses('to')
-
- # --------------------------------------------------------------
-
- def setBcc(self, addresses):
- self.setAddresses(addresses, 'bcc')
-
- def setCc(self, addresses):
- self.setAddresses(addresses, 'cc')
-
- def setNoreply(self, addresses):
- self.setAddresses(addresses, 'noreply')
-
- def setReplyroom(self, addresses):
- self.setAddresses(addresses, 'replyroom')
-
- def setReplyto(self, addresses):
- self.setAddresses(addresses, 'replyto')
-
- def setTo(self, addresses):
- self.setAddresses(addresses, 'to')
-
-
-class Address(ElementBase):
- namespace = 'http://jabber.org/protocol/address'
- name = 'address'
- plugin_attrib = 'address'
- interfaces = set(('delivered', 'desc', 'jid', 'node', 'type', 'uri'))
- address_types = set(('bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
-
- def getDelivered(self):
- return self.xml.attrib.get('delivered', False)
-
- def setDelivered(self, delivered):
- if delivered:
- self.xml.attrib['delivered'] = "true"
- else:
- del self['delivered']
-
- def setUri(self, uri):
- if uri:
- del self['jid']
- del self['node']
- self.xml.attrib['uri'] = uri
- elif 'uri' in self.xml.attrib:
- del self.xml.attrib['uri']
-
-
-class XEP_0033(BasePlugin):
-
- """
- XEP-0033: Extended Stanza Addressing
- """
-
- name = 'xep_0033'
- description = 'XEP-0033: Extended Stanza Addressing'
- dependencies = set(['xep_0033'])
-
- def plugin_init(self):
- self.xep = '0033'
-
- register_stanza_plugin(Message, Addresses)
-
- self.xmpp.plugin['xep_0030'].add_feature(Addresses.namespace)
-
-
-xep_0033 = XEP_0033
-register_plugin(XEP_0033)
diff --git a/sleekxmpp/plugins/xep_0033/__init__.py b/sleekxmpp/plugins/xep_0033/__init__.py
new file mode 100644
index 00000000..ba8152c4
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0033/__init__.py
@@ -0,0 +1,20 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 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.xep_0033 import stanza
+from sleekxmpp.plugins.xep_0033.stanza import Addresses, Address
+from sleekxmpp.plugins.xep_0033.addresses import XEP_0033
+
+
+register_plugin(XEP_0033)
+
+# Retain some backwards compatibility
+xep_0033 = XEP_0033
+Addresses.addAddress = Addresses.add_address
diff --git a/sleekxmpp/plugins/xep_0033/addresses.py b/sleekxmpp/plugins/xep_0033/addresses.py
new file mode 100644
index 00000000..78b9fbb5
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0033/addresses.py
@@ -0,0 +1,32 @@
+"""
+ 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 sleekxmpp import Message, Presence
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.xep_0033 import stanza, Addresses
+
+
+class XEP_0033(BasePlugin):
+
+ """
+ XEP-0033: Extended Stanza Addressing
+ """
+
+ name = 'xep_0033'
+ description = 'XEP-0033: Extended Stanza Addressing'
+ dependencies = set(['xep_0030'])
+ stanza = stanza
+
+ def plugin_init(self):
+ self.xmpp['xep_0030'].add_feature(Addresses.namespace)
+
+ register_stanza_plugin(Message, Addresses)
+ register_stanza_plugin(Presence, Addresses)
diff --git a/sleekxmpp/plugins/xep_0033/stanza.py b/sleekxmpp/plugins/xep_0033/stanza.py
new file mode 100644
index 00000000..1ff9fb20
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0033/stanza.py
@@ -0,0 +1,131 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import JID, ElementBase, ET, register_stanza_plugin
+
+
+class Addresses(ElementBase):
+
+ name = 'addresses'
+ namespace = 'http://jabber.org/protocol/address'
+ plugin_attrib = 'addresses'
+ interfaces = set()
+
+ def add_address(self, atype='to', jid='', node='', uri='',
+ desc='', delivered=False):
+ addr = Address(parent=self)
+ addr['type'] = atype
+ addr['jid'] = jid
+ addr['node'] = node
+ addr['uri'] = uri
+ addr['desc'] = desc
+ addr['delivered'] = delivered
+
+ return addr
+
+ # Additional methods for manipulating sets of addresses
+ # based on type are generated below.
+
+
+class Address(ElementBase):
+
+ name = 'address'
+ namespace = 'http://jabber.org/protocol/address'
+ plugin_attrib = 'address'
+ interfaces = set(['type', 'jid', 'node', 'uri', 'desc', 'delivered'])
+
+ address_types = set(('bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
+
+ def get_jid(self):
+ return JID(self._get_attr('jid'))
+
+ def set_jid(self, value):
+ self._set_attr('jid', str(value))
+
+ def get_delivered(self):
+ value = self._get_attr('delivered', False)
+ return value and value.lower() in ('true', '1')
+
+ def set_delivered(self, delivered):
+ if delivered:
+ self._set_attr('delivered', 'true')
+ else:
+ del self['delivered']
+
+ def set_uri(self, uri):
+ if uri:
+ del self['jid']
+ del self['node']
+ self._set_attr('uri', uri)
+ else:
+ self._del_attr('uri')
+
+
+# =====================================================================
+# Auto-generate address type filters for the Addresses class.
+
+def _addr_filter(atype):
+ def _type_filter(addr):
+ if isinstance(addr, Address):
+ if atype == 'all' or addr['type'] == atype:
+ return True
+ return False
+ return _type_filter
+
+
+def _build_methods(atype):
+
+ def get_multi(self):
+ return list(filter(_addr_filter(atype), self))
+
+ def set_multi(self, value):
+ del self[atype]
+ for addr in value:
+
+ # Support assigning dictionary versions of addresses
+ # instead of full Address objects.
+ if not isinstance(addr, Address):
+ if atype != 'all':
+ addr['type'] = atype
+ elif 'atype' in addr and 'type' not in addr:
+ addr['type'] = addr['atype']
+ addrObj = Address()
+ addrObj.values = addr
+ addr = addrObj
+
+ self.append(addr)
+
+ def del_multi(self):
+ res = list(filter(_addr_filter(atype), self))
+ for addr in res:
+ self.iterables.remove(addr)
+ self.xml.remove(addr.xml)
+
+ return get_multi, set_multi, del_multi
+
+
+for atype in ('all', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'):
+ get_multi, set_multi, del_multi = _build_methods(atype)
+
+ Addresses.interfaces.add(atype)
+ setattr(Addresses, "get_%s" % atype, get_multi)
+ setattr(Addresses, "set_%s" % atype, set_multi)
+ setattr(Addresses, "del_%s" % atype, del_multi)
+
+ # To retain backwards compatibility:
+ setattr(Addresses, "get%s" % atype.title(), get_multi)
+ setattr(Addresses, "set%s" % atype.title(), set_multi)
+ setattr(Addresses, "del%s" % atype.title(), del_multi)
+ if atype == 'all':
+ Addresses.interfaces.add('addresses')
+ setattr(Addresses, "getAddresses", get_multi)
+ setattr(Addresses, "setAddresses", set_multi)
+ setattr(Addresses, "delAddresses", del_multi)
+
+
+register_stanza_plugin(Addresses, Address, iterable=True)
diff --git a/sleekxmpp/plugins/xep_0050/stanza.py b/sleekxmpp/plugins/xep_0050/stanza.py
index 31a4a5d5..2367c77b 100644
--- a/sleekxmpp/plugins/xep_0050/stanza.py
+++ b/sleekxmpp/plugins/xep_0050/stanza.py
@@ -110,14 +110,14 @@ class Command(ElementBase):
"""
Return the set of allowable next actions.
"""
- actions = []
+ actions = set()
actions_xml = self.find('{%s}actions' % self.namespace)
if actions_xml is not None:
for action in self.next_actions:
action_xml = actions_xml.find('{%s}%s' % (self.namespace,
action))
if action_xml is not None:
- actions.append(action)
+ actions.add(action)
return actions
def del_actions(self):
diff --git a/sleekxmpp/plugins/xep_0054/stanza.py b/sleekxmpp/plugins/xep_0054/stanza.py
index 13865bb5..75b69d3e 100644
--- a/sleekxmpp/plugins/xep_0054/stanza.py
+++ b/sleekxmpp/plugins/xep_0054/stanza.py
@@ -72,6 +72,7 @@ class Nickname(ElementBase):
name = 'NICKNAME'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'nicknames'
interfaces = set([name])
is_extension = True
@@ -94,6 +95,7 @@ class Email(ElementBase):
name = 'EMAIL'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'emails'
interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400', 'USERID'])
sub_interfaces = set(['USERID'])
bool_interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400'])
@@ -103,8 +105,9 @@ class Address(ElementBase):
name = 'ADR'
namespace = 'vcard-temp'
plugin_attrib = name
- interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INTL',
- 'PREF', 'POBOX', 'EXTADD', 'STREET', 'LOCALITY',
+ plugin_multi_attrib = 'addresses'
+ interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INTL',
+ 'PREF', 'POBOX', 'EXTADD', 'STREET', 'LOCALITY',
'REGION', 'PCODE', 'CTRY'])
sub_interfaces = set(['POBOX', 'EXTADD', 'STREET', 'LOCALITY',
'REGION', 'PCODE', 'CTRY'])
@@ -115,12 +118,13 @@ class Telephone(ElementBase):
name = 'TEL'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'telephone_numbers'
interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER', 'MSG',
'CELL', 'VIDEO', 'BBS', 'MODEM', 'ISDN', 'PCS',
'PREF', 'NUMBER'])
sub_interfaces = set(['NUMBER'])
- bool_interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER',
- 'MSG', 'CELL', 'VIDEO', 'BBS', 'MODEM',
+ bool_interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER',
+ 'MSG', 'CELL', 'VIDEO', 'BBS', 'MODEM',
'ISDN', 'PCS', 'PREF'])
def setup(self, xml=None):
@@ -138,9 +142,10 @@ class Label(ElementBase):
name = 'LABEL'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'labels'
interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INT',
'PREF', 'lines'])
- bool_interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM',
+ bool_interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM',
'INT', 'PREF'])
def add_line(self, value):
@@ -171,6 +176,7 @@ class Geo(ElementBase):
name = 'GEO'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'geolocations'
interfaces = set(['LAT', 'LON'])
sub_interfaces = interfaces
@@ -179,6 +185,7 @@ class Org(ElementBase):
name = 'ORG'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'organizations'
interfaces = set(['ORGNAME', 'ORGUNIT', 'orgunits'])
sub_interfaces = set(['ORGNAME', 'ORGUNIT'])
@@ -210,6 +217,7 @@ class Photo(ElementBase):
name = 'PHOTO'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'photos'
interfaces = set(['TYPE', 'EXTVAL'])
sub_interfaces = interfaces
@@ -218,14 +226,16 @@ class Logo(ElementBase):
name = 'LOGO'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'logos'
interfaces = set(['TYPE', 'EXTVAL'])
sub_interfaces = interfaces
class Sound(ElementBase):
- name = 'LOGO'
+ name = 'SOUND'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'sounds'
interfaces = set(['PHONETC', 'EXTVAL'])
sub_interfaces = interfaces
@@ -264,6 +274,7 @@ class Classification(ElementBase):
name = 'CLASS'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'classifications'
interfaces = set(['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'])
bool_interfaces = interfaces
@@ -272,6 +283,7 @@ class Categories(ElementBase):
name = 'CATEGORIES'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'categories'
interfaces = set([name])
is_extension = True
@@ -301,6 +313,7 @@ class Birthday(ElementBase):
name = 'BDAY'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'birthdays'
interfaces = set([name])
is_extension = True
@@ -319,6 +332,7 @@ class Rev(ElementBase):
name = 'REV'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'revision_dates'
interfaces = set([name])
is_extension = True
@@ -337,6 +351,7 @@ class Title(ElementBase):
name = 'TITLE'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'titles'
interfaces = set([name])
is_extension = True
@@ -351,6 +366,7 @@ class Role(ElementBase):
name = 'ROLE'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'roles'
interfaces = set([name])
is_extension = True
@@ -365,6 +381,7 @@ class Note(ElementBase):
name = 'NOTE'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'notes'
interfaces = set([name])
is_extension = True
@@ -379,6 +396,7 @@ class Desc(ElementBase):
name = 'DESC'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'descriptions'
interfaces = set([name])
is_extension = True
@@ -393,6 +411,7 @@ class URL(ElementBase):
name = 'URL'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'urls'
interfaces = set([name])
is_extension = True
@@ -407,6 +426,7 @@ class UID(ElementBase):
name = 'UID'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'uids'
interfaces = set([name])
is_extension = True
@@ -421,6 +441,7 @@ class ProdID(ElementBase):
name = 'PRODID'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'product_ids'
interfaces = set([name])
is_extension = True
@@ -435,6 +456,7 @@ class Mailer(ElementBase):
name = 'MAILER'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'mailers'
interfaces = set([name])
is_extension = True
@@ -449,6 +471,7 @@ class SortString(ElementBase):
name = 'SORT-STRING'
namespace = 'vcard-temp'
plugin_attrib = 'SORT_STRING'
+ plugin_multi_attrib = 'sort_strings'
interfaces = set([name])
is_extension = True
@@ -463,6 +486,7 @@ class Agent(ElementBase):
name = 'AGENT'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'agents'
interfaces = set(['EXTVAL'])
sub_interfaces = interfaces
@@ -471,6 +495,7 @@ class JabberID(ElementBase):
name = 'JABBERID'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'jids'
interfaces = set([name])
is_extension = True
@@ -485,11 +510,12 @@ class TimeZone(ElementBase):
name = 'TZ'
namespace = 'vcard-temp'
plugin_attrib = name
+ plugin_multi_attrib = 'timezones'
interfaces = set([name])
is_extension = True
def set_tz(self, value):
- time = xep_0082.time(offset=value)
+ time = xep_0082.time(offset=value)
if time[-1] == 'Z':
self.xml.text = 'Z'
else:
diff --git a/sleekxmpp/plugins/xep_0054/vcard_temp.py b/sleekxmpp/plugins/xep_0054/vcard_temp.py
index 2c462037..672f948a 100644
--- a/sleekxmpp/plugins/xep_0054/vcard_temp.py
+++ b/sleekxmpp/plugins/xep_0054/vcard_temp.py
@@ -53,7 +53,7 @@ class XEP_0054(BasePlugin):
def make_vcard(self):
return VCardTemp()
- def get_vcard(self, jid=None, ifrom=None, local=False, cached=False,
+ def get_vcard(self, jid=None, ifrom=None, local=False, cached=False,
block=True, callback=None, timeout=None):
if self.xmpp.is_component and jid.domain == self.xmpp.boundjid.domain:
local = True
@@ -84,12 +84,12 @@ class XEP_0054(BasePlugin):
iq.enable('vcard_temp')
vcard = iq.send(block=block, callback=callback, timeout=timeout)
-
+
if block:
self.api['set_vcard'](vcard['from'], args=vcard['vcard_temp'])
return vcard
- def publish_vcard(self, vcard=None, jid=None, block=True, ifrom=None,
+ def publish_vcard(self, vcard=None, jid=None, block=True, ifrom=None,
callback=None, timeout=None):
if self.xmpp.is_component:
self.api['set_vcard'](jid, None, ifrom, vcard)
@@ -107,7 +107,7 @@ class XEP_0054(BasePlugin):
self.api['set_vcard'](jid=iq['from'], args=iq['vcard_temp'])
return
elif iq['type'] == 'get':
- vcard = self.api['get_vard'](iq['from'].bare)
+ vcard = self.api['get_vcard'](iq['from'].bare)
if isinstance(vcard, Iq):
vcard.send()
else:
diff --git a/sleekxmpp/plugins/xep_0059/stanza.py b/sleekxmpp/plugins/xep_0059/stanza.py
index 7c637d0b..48f5c8a0 100644
--- a/sleekxmpp/plugins/xep_0059/stanza.py
+++ b/sleekxmpp/plugins/xep_0059/stanza.py
@@ -74,7 +74,7 @@ class Set(ElementBase):
if fi is not None:
if val:
fi.attrib['index'] = val
- else:
+ elif 'index' in fi.attrib:
del fi.attrib['index']
elif val:
fi = ET.Element("{%s}first" % (self.namespace))
@@ -93,7 +93,7 @@ class Set(ElementBase):
def set_before(self, val):
b = self.xml.find("{%s}before" % (self.namespace))
- if b is None and val == True:
+ if b is None and val is True:
self._set_sub_text('{%s}before' % self.namespace, '', True)
else:
self._set_sub_text('{%s}before' % self.namespace, val)
diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub.py
index 004f0a02..b2fe3010 100644
--- a/sleekxmpp/plugins/xep_0060/stanza/pubsub.py
+++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub.py
@@ -77,12 +77,12 @@ class Item(ElementBase):
self.append(value)
def get_payload(self):
- childs = self.xml.getchildren()
+ childs = list(self.xml)
if len(childs) > 0:
return childs[0]
def del_payload(self):
- for child in self.xml.getchildren():
+ for child in self.xml:
self.xml.remove(child)
@@ -254,12 +254,12 @@ class PubsubState(ElementBase):
self.xml.append(value)
def get_payload(self):
- childs = self.xml.getchildren()
+ childs = list(self.xml)
if len(childs) > 0:
return childs[0]
def del_payload(self):
- for child in self.xml.getchildren():
+ for child in self.xml:
self.xml.remove(child)
diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub_errors.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub_errors.py
index aeaeefe0..59cf1a50 100644
--- a/sleekxmpp/plugins/xep_0060/stanza/pubsub_errors.py
+++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub_errors.py
@@ -33,7 +33,7 @@ class PubsubErrorCondition(ElementBase):
def get_condition(self):
"""Return the condition element's name."""
- for child in self.parent().xml.getchildren():
+ for child in self.parent().xml:
if "{%s}" % self.condition_ns in child.tag:
cond = child.tag.split('}', 1)[-1]
if cond in self.conditions:
@@ -55,7 +55,7 @@ class PubsubErrorCondition(ElementBase):
def del_condition(self):
"""Remove the condition element."""
- for child in self.parent().xml.getchildren():
+ for child in self.parent().xml:
if "{%s}" % self.condition_ns in child.tag:
tag = child.tag.split('}', 1)[-1]
if tag in self.conditions:
diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py
index c0d4929e..32f217fa 100644
--- a/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py
+++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py
@@ -31,12 +31,12 @@ class EventItem(ElementBase):
self.xml.append(value)
def get_payload(self):
- childs = self.xml.getchildren()
+ childs = list(self.xml)
if len(childs) > 0:
return childs[0]
def del_payload(self):
- for child in self.xml.getchildren():
+ for child in self.xml:
self.xml.remove(child)
diff --git a/sleekxmpp/plugins/xep_0078/stanza.py b/sleekxmpp/plugins/xep_0078/stanza.py
index 86ba09ad..c8b26071 100644
--- a/sleekxmpp/plugins/xep_0078/stanza.py
+++ b/sleekxmpp/plugins/xep_0078/stanza.py
@@ -39,5 +39,3 @@ class AuthFeature(ElementBase):
interfaces = set()
plugin_tag_map = {}
plugin_attrib_map = {}
-
-
diff --git a/sleekxmpp/plugins/xep_0080/geoloc.py b/sleekxmpp/plugins/xep_0080/geoloc.py
index 20dde4dd..28c69a2d 100644
--- a/sleekxmpp/plugins/xep_0080/geoloc.py
+++ b/sleekxmpp/plugins/xep_0080/geoloc.py
@@ -40,33 +40,33 @@ class XEP_0080(BasePlugin):
accuracy -- Horizontal GPS error in meters.
alt -- Altitude in meters above or below sea level.
area -- A named area such as a campus or neighborhood.
- bearing -- GPS bearing (direction in which the entity is
- heading to reach its next waypoint), measured in
+ bearing -- GPS bearing (direction in which the entity is
+ heading to reach its next waypoint), measured in
decimal degrees relative to true north.
building -- A specific building on a street or in an area.
country -- The nation where the user is located.
countrycode -- The ISO 3166 two-letter country code.
datum -- GPS datum.
- description -- A natural-language name for or description of
+ description -- A natural-language name for or description of
the location.
error -- Horizontal GPS error in arc minutes. Obsoleted by
the accuracy parameter.
floor -- A particular floor in a building.
lat -- Latitude in decimal degrees North.
- locality -- A locality within the administrative region, such
+ locality -- A locality within the administrative region, such
as a town or city.
lon -- Longitude in decimal degrees East.
postalcode -- A code used for postal delivery.
- region -- An administrative region of the nation, such
+ region -- An administrative region of the nation, such
as a state or province.
room -- A particular room in a building.
- speed -- The speed at which the entity is moving,
+ speed -- The speed at which the entity is moving,
in meters per second.
street -- A thoroughfare within the locality, or a crossing
of two thoroughfares.
- text -- A catch-all element that captures any other
+ text -- A catch-all element that captures any other
information about the location.
- timestamp -- UTC timestamp specifying the moment when the
+ timestamp -- UTC timestamp specifying the moment when the
reading was taken.
uri -- A URI or URL pointing to information about
the location.
@@ -115,7 +115,7 @@ class XEP_0080(BasePlugin):
be executed when a reply stanza is received.
"""
geoloc = Geoloc()
- return self.xmpp['xep_0163'].publish(geoloc,
+ return self.xmpp['xep_0163'].publish(geoloc,
ifrom=ifrom,
block=block,
callback=callback,
diff --git a/sleekxmpp/plugins/xep_0080/stanza.py b/sleekxmpp/plugins/xep_0080/stanza.py
index a83a8b1b..8f466516 100644
--- a/sleekxmpp/plugins/xep_0080/stanza.py
+++ b/sleekxmpp/plugins/xep_0080/stanza.py
@@ -31,33 +31,33 @@ class Geoloc(ElementBase):
accuracy -- Horizontal GPS error in meters.
alt -- Altitude in meters above or below sea level.
area -- A named area such as a campus or neighborhood.
- bearing -- GPS bearing (direction in which the entity is
- heading to reach its next waypoint), measured in
+ bearing -- GPS bearing (direction in which the entity is
+ heading to reach its next waypoint), measured in
decimal degrees relative to true north.
building -- A specific building on a street or in an area.
country -- The nation where the user is located.
countrycode -- The ISO 3166 two-letter country code.
datum -- GPS datum.
- description -- A natural-language name for or description of
+ description -- A natural-language name for or description of
the location.
error -- Horizontal GPS error in arc minutes. Obsoleted by
the accuracy parameter.
floor -- A particular floor in a building.
lat -- Latitude in decimal degrees North.
- locality -- A locality within the administrative region, such
+ locality -- A locality within the administrative region, such
as a town or city.
lon -- Longitude in decimal degrees East.
postalcode -- A code used for postal delivery.
- region -- An administrative region of the nation, such
+ region -- An administrative region of the nation, such
as a state or province.
room -- A particular room in a building.
- speed -- The speed at which the entity is moving,
+ speed -- The speed at which the entity is moving,
in meters per second.
street -- A thoroughfare within the locality, or a crossing
of two thoroughfares.
- text -- A catch-all element that captures any other
+ text -- A catch-all element that captures any other
information about the location.
- timestamp -- UTC timestamp specifying the moment when the
+ timestamp -- UTC timestamp specifying the moment when the
reading was taken.
uri -- A URI or URL pointing to information about
the location.
@@ -65,10 +65,10 @@ class Geoloc(ElementBase):
namespace = 'http://jabber.org/protocol/geoloc'
name = 'geoloc'
- interfaces = set(('accuracy', 'alt', 'area', 'bearing', 'building',
- 'country', 'countrycode', 'datum', 'dscription',
- 'error', 'floor', 'lat', 'locality', 'lon',
- 'postalcode', 'region', 'room', 'speed', 'street',
+ interfaces = set(('accuracy', 'alt', 'area', 'bearing', 'building',
+ 'country', 'countrycode', 'datum', 'dscription',
+ 'error', 'floor', 'lat', 'locality', 'lon',
+ 'postalcode', 'region', 'room', 'speed', 'street',
'text', 'timestamp', 'uri'))
sub_interfaces = interfaces
plugin_attrib = name
@@ -88,7 +88,7 @@ class Geoloc(ElementBase):
"""
self._set_sub_text('accuracy', text=str(accuracy))
return self
-
+
def get_accuracy(self):
"""
Return the value of the <accuracy> element as an integer.
@@ -111,7 +111,7 @@ class Geoloc(ElementBase):
"""
self._set_sub_text('alt', text=str(alt))
return self
-
+
def get_alt(self):
"""
Return the value of the <alt> element as an integer.
@@ -130,8 +130,8 @@ class Geoloc(ElementBase):
Set the value of the <bearing> element.
Arguments:
- bearing -- GPS bearing (direction in which the entity is heading
- to reach its next waypoint), measured in decimal
+ bearing -- GPS bearing (direction in which the entity is heading
+ to reach its next waypoint), measured in decimal
degrees relative to true north
"""
self._set_sub_text('bearing', text=str(bearing))
@@ -155,7 +155,7 @@ class Geoloc(ElementBase):
Set the value of the <error> element.
Arguments:
- error -- Horizontal GPS error in arc minutes; this
+ error -- Horizontal GPS error in arc minutes; this
element is deprecated in favor of <accuracy/>
"""
self._set_sub_text('error', text=str(error))
@@ -183,7 +183,7 @@ class Geoloc(ElementBase):
"""
self._set_sub_text('lat', text=str(lat))
return self
-
+
def get_lat(self):
"""
Return the value of the <lat> element as a float.
@@ -196,7 +196,7 @@ class Geoloc(ElementBase):
return float(p)
except ValueError:
return None
-
+
def set_lon(self, lon):
"""
Set the value of the <lon> element.
@@ -225,12 +225,12 @@ class Geoloc(ElementBase):
Set the value of the <speed> element.
Arguments:
- speed -- The speed at which the entity is moving,
+ speed -- The speed at which the entity is moving,
in meters per second
"""
self._set_sub_text('speed', text=str(speed))
return self
-
+
def get_speed(self):
"""
Return the value of the <speed> element as a float.
diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py
index 96eb331a..02571fa7 100644
--- a/sleekxmpp/plugins/xep_0082.py
+++ b/sleekxmpp/plugins/xep_0082.py
@@ -42,6 +42,7 @@ def format_date(time_obj):
time_obj = time_obj.date()
return time_obj.isoformat()
+
def format_time(time_obj):
"""
Return a formatted string version of a time object.
@@ -60,6 +61,7 @@ def format_time(time_obj):
return '%sZ' % timestamp
return timestamp
+
def format_datetime(time_obj):
"""
Return a formatted string version of a datetime object.
@@ -76,6 +78,7 @@ def format_datetime(time_obj):
return '%sZ' % timestamp
return timestamp
+
def date(year=None, month=None, day=None, obj=False):
"""
Create a date only timestamp for the given instant.
@@ -98,9 +101,10 @@ def date(year=None, month=None, day=None, obj=False):
day = today.day
value = dt.date(year, month, day)
if obj:
- return value
+ return value
return format_date(value)
+
def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
"""
Create a time only timestamp for the given instant.
@@ -136,6 +140,7 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
return value
return format_time(value)
+
def datetime(year=None, month=None, day=None, hour=None,
min=None, sec=None, micro=None, offset=None,
separators=True, obj=False):
@@ -181,7 +186,7 @@ def datetime(year=None, month=None, day=None, hour=None,
value = dt.datetime(year, month, day, hour,
min, sec, micro, offset)
if obj:
- return value
+ return value
return format_datetime(value)
diff --git a/sleekxmpp/plugins/xep_0084/__init__.py b/sleekxmpp/plugins/xep_0084/__init__.py
new file mode 100644
index 00000000..6b87573f
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0084/__init__.py
@@ -0,0 +1,16 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 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.xep_0084 import stanza
+from sleekxmpp.plugins.xep_0084.stanza import Data, MetaData
+from sleekxmpp.plugins.xep_0084.avatar import XEP_0084
+
+
+register_plugin(XEP_0084)
diff --git a/sleekxmpp/plugins/xep_0084/avatar.py b/sleekxmpp/plugins/xep_0084/avatar.py
new file mode 100644
index 00000000..14ab7d97
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0084/avatar.py
@@ -0,0 +1,101 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import hashlib
+import logging
+
+from sleekxmpp import Iq
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.xmlstream import register_stanza_plugin, JID
+from sleekxmpp.plugins.xep_0084 import stanza, Data, MetaData
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0084(BasePlugin):
+
+ name = 'xep_0084'
+ description = 'XEP-0084: User Avatar'
+ dependencies = set(['xep_0163', 'xep_0060'])
+ stanza = stanza
+
+ def plugin_init(self):
+ self.xmpp['xep_0163'].register_pep('avatar_metadata', MetaData)
+
+ pubsub_stanza = self.xmpp['xep_0060'].stanza
+ register_stanza_plugin(pubsub_stanza.Item, Data)
+ register_stanza_plugin(pubsub_stanza.EventItem, Data)
+
+ self.xmpp['xep_0060'].map_node_event(Data.namespace, 'avatar_data')
+
+ def retrieve_avatar(self, jid, id, url=None, ifrom=None, block=True,
+ callback=None, timeout=None):
+ return self.xmpp['xep_0060'].get_item(jid, Data.namespace, id,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def publish_avatar(self, data, ifrom=None, block=True, callback=None,
+ timeout=None):
+ payload = Data()
+ payload['value'] = data
+ return self.xmpp['xep_0163'].publish(payload,
+ node=Data.namespace,
+ id=hashlib.sha1(data).hexdigest(),
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def publish_avatar_metadata(self, items=None, pointers=None,
+ ifrom=None, block=True,
+ callback=None, timeout=None):
+ metadata = MetaData()
+ if items is None:
+ items = []
+ for info in items:
+ metadata.add_info(info['id'], info['type'], info['bytes'],
+ height=info.get('height', ''),
+ width=info.get('width', ''),
+ url=info.get('url', ''))
+ for pointer in pointers:
+ metadata.add_pointer(pointer)
+
+ return self.xmpp['xep_0163'].publish(payload,
+ node=Data.namespace,
+ id=hashlib.sha1(data).hexdigest(),
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def stop(self, ifrom=None, block=True, callback=None, timeout=None):
+ """
+ Clear existing avatar metadata information to stop notifications.
+
+ Arguments:
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ metadata = MetaData()
+ return self.xmpp['xep_0163'].publish(metadata,
+ node=MetaData.namespace,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
diff --git a/sleekxmpp/plugins/xep_0084/stanza.py b/sleekxmpp/plugins/xep_0084/stanza.py
new file mode 100644
index 00000000..1b204471
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0084/stanza.py
@@ -0,0 +1,78 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from base64 import b64encode, b64decode
+from sleekxmpp.thirdparty.suelta.util import bytes
+
+from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
+
+
+class Data(ElementBase):
+ name = 'data'
+ namespace = 'urn:xmpp:avatar:data'
+ plugin_attrib = 'avatar_data'
+ interfaces = set(['value'])
+
+ def get_value(self):
+ if self.xml.text:
+ return b64decode(bytes(self.xml.text))
+ return ''
+
+ def set_value(self, value):
+ if value:
+ self.xml.text = b64encode(bytes(value))
+ else:
+ self.xml.text = ''
+
+ def del_value(self):
+ self.xml.text = ''
+
+
+class MetaData(ElementBase):
+ name = 'metadata'
+ namespace = 'urn:xmpp:avatar:metadata'
+ plugin_attrib = 'avatar_metadata'
+ interfaces = set()
+
+ def add_info(self, id, itype, ibytes, height=None, width=None, url=None):
+ info = Info()
+ info.values = {'id': id,
+ 'type': itype,
+ 'bytes': ibytes,
+ 'height': height,
+ 'width': width,
+ 'url': url}
+ self.append(info)
+
+ def add_pointer(self, xml):
+ if not isinstance(xml, Pointer):
+ pointer = Pointer()
+ pointer.append(xml)
+ self.append(pointer)
+ else:
+ self.append(xml)
+
+
+class Info(ElementBase):
+ name = 'info'
+ namespace = 'urn:xmpp:avatar:metadata'
+ plugin_attrib = 'info'
+ plugin_multi_attrib = 'items'
+ interfaces = set(['bytes', 'height', 'id', 'type', 'url', 'width'])
+
+
+class Pointer(ElementBase):
+ name = 'pointer'
+ namespace = 'urn:xmpp:avatar:metadata'
+ plugin_attrib = 'pointer'
+ plugin_multi_attrib = 'pointers'
+ interfaces = set()
+
+
+register_stanza_plugin(MetaData, Info, iterable=True)
+register_stanza_plugin(MetaData, Pointer, iterable=True)
diff --git a/sleekxmpp/plugins/xep_0086/stanza.py b/sleekxmpp/plugins/xep_0086/stanza.py
index 6554d249..d4909806 100644
--- a/sleekxmpp/plugins/xep_0086/stanza.py
+++ b/sleekxmpp/plugins/xep_0086/stanza.py
@@ -47,28 +47,28 @@ class LegacyError(ElementBase):
interfaces = set(('condition',))
overrides = ['set_condition']
- error_map = {'bad-request': ('modify','400'),
- 'conflict': ('cancel','409'),
- 'feature-not-implemented': ('cancel','501'),
- 'forbidden': ('auth','403'),
- 'gone': ('modify','302'),
- 'internal-server-error': ('wait','500'),
- 'item-not-found': ('cancel','404'),
- 'jid-malformed': ('modify','400'),
- 'not-acceptable': ('modify','406'),
- 'not-allowed': ('cancel','405'),
- 'not-authorized': ('auth','401'),
- 'payment-required': ('auth','402'),
- 'recipient-unavailable': ('wait','404'),
- 'redirect': ('modify','302'),
- 'registration-required': ('auth','407'),
- 'remote-server-not-found': ('cancel','404'),
- 'remote-server-timeout': ('wait','504'),
- 'resource-constraint': ('wait','500'),
- 'service-unavailable': ('cancel','503'),
- 'subscription-required': ('auth','407'),
- 'undefined-condition': (None,'500'),
- 'unexpected-request': ('wait','400')}
+ error_map = {'bad-request': ('modify', '400'),
+ 'conflict': ('cancel', '409'),
+ 'feature-not-implemented': ('cancel', '501'),
+ 'forbidden': ('auth', '403'),
+ 'gone': ('modify', '302'),
+ 'internal-server-error': ('wait', '500'),
+ 'item-not-found': ('cancel', '404'),
+ 'jid-malformed': ('modify', '400'),
+ 'not-acceptable': ('modify', '406'),
+ 'not-allowed': ('cancel', '405'),
+ 'not-authorized': ('auth', '401'),
+ 'payment-required': ('auth', '402'),
+ 'recipient-unavailable': ('wait', '404'),
+ 'redirect': ('modify', '302'),
+ 'registration-required': ('auth', '407'),
+ 'remote-server-not-found': ('cancel', '404'),
+ 'remote-server-timeout': ('wait', '504'),
+ 'resource-constraint': ('wait', '500'),
+ 'service-unavailable': ('cancel', '503'),
+ 'subscription-required': ('auth', '407'),
+ 'undefined-condition': (None, '500'),
+ 'unexpected-request': ('wait', '400')}
def setup(self, xml):
"""Don't create XML for the plugin."""
diff --git a/sleekxmpp/plugins/xep_0092/version.py b/sleekxmpp/plugins/xep_0092/version.py
index c6223c10..5e84b2ff 100644
--- a/sleekxmpp/plugins/xep_0092/version.py
+++ b/sleekxmpp/plugins/xep_0092/version.py
@@ -79,5 +79,7 @@ class XEP_0092(BasePlugin):
result = iq.send()
if result and result['type'] != 'error':
- return result['software_version'].values
+ values = result['software_version'].values
+ del values['lang']
+ return values
return False
diff --git a/sleekxmpp/plugins/xep_0107/user_mood.py b/sleekxmpp/plugins/xep_0107/user_mood.py
index 11aaace4..95e17d45 100644
--- a/sleekxmpp/plugins/xep_0107/user_mood.py
+++ b/sleekxmpp/plugins/xep_0107/user_mood.py
@@ -34,7 +34,7 @@ class XEP_0107(BasePlugin):
register_stanza_plugin(Message, UserMood)
self.xmpp['xep_0163'].register_pep('user_mood', UserMood)
- def publish_mood(self, value=None, text=None, options=None,
+ def publish_mood(self, value=None, text=None, options=None,
ifrom=None, block=True, callback=None, timeout=None):
"""
Publish the user's current mood.
@@ -79,7 +79,7 @@ class XEP_0107(BasePlugin):
be executed when a reply stanza is received.
"""
mood = UserMood()
- return self.xmpp['xep_0163'].publish(mood,
+ return self.xmpp['xep_0163'].publish(mood,
node=UserMood.namespace,
ifrom=ifrom,
block=block,
diff --git a/sleekxmpp/plugins/xep_0108/stanza.py b/sleekxmpp/plugins/xep_0108/stanza.py
index 4dc18f43..4650160a 100644
--- a/sleekxmpp/plugins/xep_0108/stanza.py
+++ b/sleekxmpp/plugins/xep_0108/stanza.py
@@ -21,7 +21,7 @@ class UserActivity(ElementBase):
'talking', 'traveling', 'undefined', 'working'])
specific = set(['at_the_spa', 'brushing_teeth', 'buying_groceries',
'cleaning', 'coding', 'commuting', 'cooking', 'cycling',
- 'dancing', 'day_off', 'doing_maintenance',
+ 'dancing', 'day_off', 'doing_maintenance',
'doing_the_dishes', 'doing_the_laundry', 'driving',
'fishing', 'gaming', 'gardening', 'getting_a_haircut',
'going_out', 'hanging_out', 'having_a_beer',
@@ -31,11 +31,11 @@ class UserActivity(ElementBase):
'jogging', 'on_a_bus', 'on_a_plane', 'on_a_train',
'on_a_trip', 'on_the_phone', 'on_vacation',
'on_video_phone', 'other', 'partying', 'playing_sports',
- 'praying', 'reading', 'rehearsing', 'running',
+ 'praying', 'reading', 'rehearsing', 'running',
'running_an_errand', 'scheduled_holiday', 'shaving',
'shopping', 'skiing', 'sleeping', 'smoking',
'socializing', 'studying', 'sunbathing', 'swimming',
- 'taking_a_bath', 'taking_a_shower', 'thinking',
+ 'taking_a_bath', 'taking_a_shower', 'thinking',
'walking', 'walking_the_dog', 'watching_a_movie',
'watching_tv', 'working_out', 'writing'])
@@ -46,7 +46,7 @@ class UserActivity(ElementBase):
if isinstance(value, tuple) or isinstance(value, list):
general = value[0]
specific = value[1]
-
+
if general in self.general:
gen_xml = ET.Element('{%s}%s' % (self.namespace, general))
if specific:
diff --git a/sleekxmpp/plugins/xep_0108/user_activity.py b/sleekxmpp/plugins/xep_0108/user_activity.py
index 43270486..cd4f48d1 100644
--- a/sleekxmpp/plugins/xep_0108/user_activity.py
+++ b/sleekxmpp/plugins/xep_0108/user_activity.py
@@ -29,7 +29,7 @@ class XEP_0108(BasePlugin):
def plugin_init(self):
self.xmpp['xep_0163'].register_pep('user_activity', UserActivity)
- def publish_activity(self, general, specific=None, text=None, options=None,
+ def publish_activity(self, general, specific=None, text=None, options=None,
ifrom=None, block=True, callback=None, timeout=None):
"""
Publish the user's current activity.
@@ -76,7 +76,7 @@ class XEP_0108(BasePlugin):
be executed when a reply stanza is received.
"""
activity = UserActivity()
- return self.xmpp['xep_0163'].publish(activity,
+ return self.xmpp['xep_0163'].publish(activity,
node=UserActivity.namespace,
ifrom=ifrom,
block=block,
diff --git a/sleekxmpp/plugins/xep_0115/caps.py b/sleekxmpp/plugins/xep_0115/caps.py
index 5e5d2320..b0cba42d 100644
--- a/sleekxmpp/plugins/xep_0115/caps.py
+++ b/sleekxmpp/plugins/xep_0115/caps.py
@@ -35,7 +35,7 @@ class XEP_0115(BasePlugin):
stanza = stanza
def plugin_init(self):
- self.hashes = {'sha-1': hashlib.sha1,
+ self.hashes = {'sha-1': hashlib.sha1,
'sha1': hashlib.sha1,
'md5': hashlib.md5}
@@ -124,7 +124,7 @@ class XEP_0115(BasePlugin):
existing_verstring = self.get_verstring(pres['from'].full)
if str(existing_verstring) == str(pres['caps']['ver']):
return
-
+
if pres['caps']['hash'] not in self.hashes:
try:
log.debug("Unknown caps hash: %s", pres['caps']['hash'])
@@ -132,7 +132,7 @@ class XEP_0115(BasePlugin):
return
except XMPPError:
return
-
+
log.debug("New caps verification string: %s", pres['caps']['ver'])
try:
node = '%s#%s' % (pres['caps']['node'], pres['caps']['ver'])
@@ -140,7 +140,7 @@ class XEP_0115(BasePlugin):
if isinstance(caps, Iq):
caps = caps['disco_info']
-
+
if self._validate_caps(caps, pres['caps']['hash'],
pres['caps']['ver']):
self.assign_verstring(pres['from'], pres['caps']['ver'])
@@ -173,7 +173,8 @@ class XEP_0115(BasePlugin):
form_types.append(f_type)
deduped_form_types.add(f_type)
if len(form_types) != len(deduped_form_types):
- log.debug("Duplicated FORM_TYPE values, invalid for caps")
+ log.debug("Duplicated FORM_TYPE values, " + \
+ "invalid for caps")
return False
if len(f_type) > 1:
@@ -183,7 +184,8 @@ class XEP_0115(BasePlugin):
return False
if stanza['fields']['FORM_TYPE']['type'] != 'hidden':
- log.debug("Field FORM_TYPE type not 'hidden', ignoring form for caps")
+ log.debug("Field FORM_TYPE type not 'hidden', " + \
+ "ignoring form for caps")
caps.xml.remove(stanza.xml)
else:
log.debug("No FORM_TYPE found, ignoring form for caps")
@@ -212,7 +214,7 @@ class XEP_0115(BasePlugin):
identities = sorted(('/'.join(i) for i in identities))
features = sorted(info['features'])
-
+
S += '<'.join(identities) + '<'
S += '<'.join(features) + '<'
@@ -254,7 +256,7 @@ class XEP_0115(BasePlugin):
info = info['disco_info']
ver = self.generate_verstring(info, self.hash)
self.xmpp['xep_0030'].set_info(
- jid=jid,
+ jid=jid,
node='%s#%s' % (self.caps_node, ver),
info=info)
self.cache_caps(ver, info)
diff --git a/sleekxmpp/plugins/xep_0115/static.py b/sleekxmpp/plugins/xep_0115/static.py
index a0a8fb23..f83c244c 100644
--- a/sleekxmpp/plugins/xep_0115/static.py
+++ b/sleekxmpp/plugins/xep_0115/static.py
@@ -69,7 +69,7 @@ class StaticCaps(object):
return True
try:
- info = self.disco.get_info(jid=jid, node=node,
+ info = self.disco.get_info(jid=jid, node=node,
ifrom=ifrom, **data)
info = self.disco._wrap(ifrom, jid, info, True)
return feature in info['disco_info']['features']
@@ -99,7 +99,7 @@ class StaticCaps(object):
be skipped, even if a result has already been
cached. Defaults to false.
"""
- identity = (data.get('category', None),
+ identity = (data.get('category', None),
data.get('itype', None),
data.get('lang', None))
@@ -114,7 +114,7 @@ class StaticCaps(object):
return True
try:
- info = self.disco.get_info(jid=jid, node=node,
+ info = self.disco.get_info(jid=jid, node=node,
ifrom=ifrom, **data)
info = self.disco._wrap(ifrom, jid, info, True)
return identity in map(trunc, info['disco_info']['identities'])
diff --git a/sleekxmpp/plugins/xep_0118/stanza.py b/sleekxmpp/plugins/xep_0118/stanza.py
index 80e0358a..3fdab284 100644
--- a/sleekxmpp/plugins/xep_0118/stanza.py
+++ b/sleekxmpp/plugins/xep_0118/stanza.py
@@ -14,7 +14,7 @@ class UserTune(ElementBase):
name = 'tune'
namespace = 'http://jabber.org/protocol/tune'
plugin_attrib = 'tune'
- interfaces = set(['artist', 'length', 'rating', 'source',
+ interfaces = set(['artist', 'length', 'rating', 'source',
'title', 'track', 'uri'])
sub_interfaces = interfaces
diff --git a/sleekxmpp/plugins/xep_0118/user_tune.py b/sleekxmpp/plugins/xep_0118/user_tune.py
index c848eaa8..53a4f51a 100644
--- a/sleekxmpp/plugins/xep_0118/user_tune.py
+++ b/sleekxmpp/plugins/xep_0118/user_tune.py
@@ -30,7 +30,7 @@ class XEP_0118(BasePlugin):
self.xmpp['xep_0163'].register_pep('user_tune', UserTune)
def publish_tune(self, artist=None, length=None, rating=None, source=None,
- title=None, track=None, uri=None, options=None,
+ title=None, track=None, uri=None, options=None,
ifrom=None, block=True, callback=None, timeout=None):
"""
Publish the user's current tune.
@@ -61,7 +61,7 @@ class XEP_0118(BasePlugin):
tune['title'] = title
tune['track'] = track
tune['uri'] = uri
- return self.xmpp['xep_0163'].publish(tune,
+ return self.xmpp['xep_0163'].publish(tune,
node=UserTune.namespace,
options=options,
ifrom=ifrom,
@@ -84,7 +84,7 @@ class XEP_0118(BasePlugin):
be executed when a reply stanza is received.
"""
tune = UserTune()
- return self.xmpp['xep_0163'].publish(tune,
+ return self.xmpp['xep_0163'].publish(tune,
node=UserTune.namespace,
ifrom=ifrom,
block=block,
diff --git a/sleekxmpp/plugins/xep_0153/vcard_avatar.py b/sleekxmpp/plugins/xep_0153/vcard_avatar.py
index 2cc2f15a..3f36d135 100644
--- a/sleekxmpp/plugins/xep_0153/vcard_avatar.py
+++ b/sleekxmpp/plugins/xep_0153/vcard_avatar.py
@@ -45,7 +45,7 @@ class XEP_0153(BasePlugin):
self.api.register(self._set_hash, 'set_hash', default=True)
self.api.register(self._get_hash, 'get_hash', default=True)
- def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
+ def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
timeout=None, callback=None):
vcard = self.xmpp['xep_0054'].get_vcard(jid, cached=True)
vcard = vcard['vcard_temp']
@@ -69,7 +69,7 @@ class XEP_0153(BasePlugin):
own_jid = (jid.bare == self.xmpp.boundjid.bare)
if self.xmpp.is_component:
own_jid = (jid.domain == self.xmpp.boundjid.domain)
-
+
if jid is not None:
jid = jid.bare
self.api['set_hash'](jid, args=None)
@@ -77,7 +77,7 @@ class XEP_0153(BasePlugin):
self.xmpp.roster[jid].send_last_presence()
iq = self.xmpp['xep_0054'].get_vcard(
- jid=jid,
+ jid=jid,
ifrom=self.xmpp.boundjid)
data = iq['vcard_temp']['PHOTO']['BINVAL']
if not data:
diff --git a/sleekxmpp/plugins/xep_0163.py b/sleekxmpp/plugins/xep_0163.py
index 5a6df1c8..43d3ad3a 100644
--- a/sleekxmpp/plugins/xep_0163.py
+++ b/sleekxmpp/plugins/xep_0163.py
@@ -56,7 +56,7 @@ class XEP_0163(BasePlugin):
jid -- Optionally specify the JID.
"""
if not isinstance(namespace, set) and not isinstance(namespace, list):
- namespace = [namespace]
+ namespace = [namespace]
for ns in namespace:
self.xmpp['xep_0030'].add_feature('%s+notify' % ns,
@@ -75,7 +75,7 @@ class XEP_0163(BasePlugin):
jid -- Optionally specify the JID.
"""
if not isinstance(namespace, set) and not isinstance(namespace, list):
- namespace = [namespace]
+ namespace = [namespace]
for ns in namespace:
self.xmpp['xep_0030'].del_feature(jid=jid,
@@ -109,6 +109,7 @@ class XEP_0163(BasePlugin):
node = stanza.namespace
return self.xmpp['xep_0060'].publish(ifrom, node,
+ id=id,
payload=stanza.xml,
options=options,
ifrom=ifrom,
diff --git a/sleekxmpp/plugins/xep_0172/user_nick.py b/sleekxmpp/plugins/xep_0172/user_nick.py
index c20c3583..324407c3 100644
--- a/sleekxmpp/plugins/xep_0172/user_nick.py
+++ b/sleekxmpp/plugins/xep_0172/user_nick.py
@@ -78,7 +78,7 @@ class XEP_0172(BasePlugin):
be executed when a reply stanza is received.
"""
nick = UserNick()
- return self.xmpp['xep_0163'].publish(nick,
+ return self.xmpp['xep_0163'].publish(nick,
node=UserNick.namespace,
ifrom=ifrom,
block=block,
diff --git a/sleekxmpp/plugins/xep_0184/receipt.py b/sleekxmpp/plugins/xep_0184/receipt.py
index c0086b03..83d89269 100644
--- a/sleekxmpp/plugins/xep_0184/receipt.py
+++ b/sleekxmpp/plugins/xep_0184/receipt.py
@@ -100,13 +100,13 @@ class XEP_0184(BasePlugin):
if not isinstance(stanza, Message):
return stanza
-
+
if stanza['request_receipt']:
return stanza
if not stanza['type'] in self.ack_types:
return stanza
-
+
if stanza['receipt']:
return stanza
diff --git a/sleekxmpp/plugins/xep_0198/stanza.py b/sleekxmpp/plugins/xep_0198/stanza.py
index 5cf93436..6461d766 100644
--- a/sleekxmpp/plugins/xep_0198/stanza.py
+++ b/sleekxmpp/plugins/xep_0198/stanza.py
@@ -82,7 +82,6 @@ class Resumed(StanzaBase):
self._set_attr('h', str(val))
-
class Failed(StanzaBase, Error):
name = 'failed'
namespace = 'urn:xmpp:sm:3'
@@ -106,7 +105,7 @@ class StreamManagement(ElementBase):
self.del_required()
if val:
self._set_sub_text('required', '', keep=True)
-
+
def del_required(self):
self._del_sub('required')
@@ -117,7 +116,7 @@ class StreamManagement(ElementBase):
self.del_optional()
if val:
self._set_sub_text('optional', '', keep=True)
-
+
def del_optional(self):
self._del_sub('optional')
diff --git a/sleekxmpp/plugins/xep_0198/stream_management.py b/sleekxmpp/plugins/xep_0198/stream_management.py
index 7045ad21..05d5856f 100644
--- a/sleekxmpp/plugins/xep_0198/stream_management.py
+++ b/sleekxmpp/plugins/xep_0198/stream_management.py
@@ -21,7 +21,7 @@ from sleekxmpp.plugins.xep_0198 import stanza
log = logging.getLogger(__name__)
-MAX_SEQ = 2**32
+MAX_SEQ = 2 ** 32
class XEP_0198(BasePlugin):
@@ -69,7 +69,7 @@ class XEP_0198(BasePlugin):
self.enabled = threading.Event()
self.unacked_queue = collections.deque()
-
+
self.seq_lock = threading.Lock()
self.handled_lock = threading.Lock()
self.ack_lock = threading.Lock()
@@ -197,7 +197,7 @@ class XEP_0198(BasePlugin):
def _handle_enabled(self, stanza):
"""Save the SM-ID, if provided.
-
+
Raises an :term:`sm_enabled` event.
"""
self.xmpp.features.add('stream_management')
@@ -231,7 +231,7 @@ class XEP_0198(BasePlugin):
def _handle_ack(self, ack):
"""Process a server ack by freeing acked stanzas from the queue.
-
+
Raises a :term:`stanza_acked` event for each acked stanza.
"""
if ack['h'] == self.last_ack:
@@ -243,10 +243,10 @@ class XEP_0198(BasePlugin):
log.debug("Ack: %s, Last Ack: %s, " + \
"Unacked: %s, Num Acked: %s, " + \
"Remaining: %s",
- ack['h'],
- self.last_ack,
+ ack['h'],
+ self.last_ack,
num_unacked,
- num_acked,
+ num_acked,
num_unacked - num_acked)
for x in range(num_acked):
seq, stanza = self.unacked_queue.popleft()
diff --git a/sleekxmpp/plugins/xep_0202/time.py b/sleekxmpp/plugins/xep_0202/time.py
index ca388c5b..319a9bc5 100644
--- a/sleekxmpp/plugins/xep_0202/time.py
+++ b/sleekxmpp/plugins/xep_0202/time.py
@@ -40,8 +40,12 @@ class XEP_0202(BasePlugin):
# custom function can be supplied which accepts
# the JID of the entity to query for the time.
self.local_time = self.config.get('local_time', None)
+
+ def default_local_time(jid):
+ return xep_0082.datetime(offset=self.tz_offset)
+
if not self.local_time:
- self.local_time = lambda x: xep_0082.datetime(offset=self.tz_offset)
+ self.local_time = default_local_time
self.xmpp.registerHandler(
Callback('Entity Time',
diff --git a/sleekxmpp/plugins/xep_0203/__init__.py b/sleekxmpp/plugins/xep_0203/__init__.py
index d4d99a6c..a95ead7e 100644
--- a/sleekxmpp/plugins/xep_0203/__init__.py
+++ b/sleekxmpp/plugins/xep_0203/__init__.py
@@ -13,9 +13,7 @@ from sleekxmpp.plugins.xep_0203.stanza import Delay
from sleekxmpp.plugins.xep_0203.delay import XEP_0203
-
register_plugin(XEP_0203)
-
# Retain some backwards compatibility
xep_0203 = XEP_0203
diff --git a/sleekxmpp/plugins/xep_0222.py b/sleekxmpp/plugins/xep_0222.py
new file mode 100644
index 00000000..724ef968
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0222.py
@@ -0,0 +1,126 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins.base import BasePlugin, register_plugin
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0222(BasePlugin):
+
+ """
+ XEP-0222: Persistent Storage of Public Data via PubSub
+ """
+
+ name = 'xep_0222'
+ description = 'XEP-0222: Persistent Storage of Private Data via PubSub'
+ dependencies = set(['xep_0163', 'xep_0060', 'xep_0004'])
+
+ profile = {'pubsub#persist_items': True,
+ 'pubsub#send_last_published_item': 'never'}
+
+ def configure(self, node):
+ """
+ Update a node's configuration to match the public storage profile.
+ """
+ config = self.xmpp['xep_0004'].Form()
+ config['type'] = 'submit'
+
+ for field, value in self.profile.items():
+ config.add_field(var=field, value=value)
+
+ return self.xmpp['xep_0060'].set_node_config(None, node, config,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def store(self, stanza, node=None, id=None, ifrom=None, options=None,
+ block=True, callback=None, timeout=None):
+ """
+ Store public data via PEP.
+
+ This is just a (very) thin wrapper around the XEP-0060 publish()
+ method to set the defaults expected by PEP.
+
+ Arguments:
+ stanza -- The private content to store.
+ node -- The node to publish the content to. If not specified,
+ the stanza's namespace will be used.
+ id -- Optionally specify the ID of the item.
+ options -- Publish options to use, which will be modified to
+ fit the persistent storage option profile.
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ if not options:
+ options = self.xmpp['xep_0004'].stanza.Form()
+ options['type'] = 'submit'
+ options.add_field(
+ var='FORM_TYPE',
+ ftype='hidden',
+ value='http://jabber.org/protocol/pubsub#publish-options')
+
+ for field, value in self.profile.items():
+ if field not in options.fields:
+ options.add_field(var=field)
+ options.fields[field]['value'] = value
+
+ return self.xmpp['xep_0163'].publish(stanza, node,
+ options=options,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def retrieve(self, node, id=None, item_ids=None, ifrom=None,
+ block=True, callback=None, timeout=None):
+ """
+ Retrieve public data via PEP.
+
+ This is just a (very) thin wrapper around the XEP-0060 publish()
+ method to set the defaults expected by PEP.
+
+ Arguments:
+ node -- The node to retrieve content from.
+ id -- Optionally specify the ID of the item.
+ item_ids -- Specify a group of IDs. If id is also specified, it
+ will be included in item_ids.
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ if item_ids is None:
+ item_ids = []
+ if id is not None:
+ item_ids.append(id)
+
+ return self.xmpp['xep_0060'].get_items(None, node,
+ item_ids=item_ids,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+
+register_plugin(XEP_0222)
diff --git a/sleekxmpp/plugins/xep_0223.py b/sleekxmpp/plugins/xep_0223.py
new file mode 100644
index 00000000..ab99f277
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0223.py
@@ -0,0 +1,126 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins.base import BasePlugin, register_plugin
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0223(BasePlugin):
+
+ """
+ XEP-0223: Persistent Storage of Private Data via PubSub
+ """
+
+ name = 'xep_0223'
+ description = 'XEP-0223: Persistent Storage of Private Data via PubSub'
+ dependencies = set(['xep_0163', 'xep_0060', 'xep_0004'])
+
+ profile = {'pubsub#persist_items': True,
+ 'pubsub#send_last_published_item': 'never'}
+
+ def configure(self, node):
+ """
+ Update a node's configuration to match the public storage profile.
+ """
+ config = self.xmpp['xep_0004'].Form()
+ config['type'] = 'submit'
+
+ for field, value in self.profile.items():
+ config.add_field(var=field, value=value)
+
+ return self.xmpp['xep_0060'].set_node_config(None, node, config,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def store(self, stanza, node=None, id=None, ifrom=None, options=None,
+ block=True, callback=None, timeout=None):
+ """
+ Store private data via PEP.
+
+ This is just a (very) thin wrapper around the XEP-0060 publish()
+ method to set the defaults expected by PEP.
+
+ Arguments:
+ stanza -- The private content to store.
+ node -- The node to publish the content to. If not specified,
+ the stanza's namespace will be used.
+ id -- Optionally specify the ID of the item.
+ options -- Publish options to use, which will be modified to
+ fit the persistent storage option profile.
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ if not options:
+ options = self.xmpp['xep_0004'].stanza.Form()
+ options['type'] = 'submit'
+ options.add_field(
+ var='FORM_TYPE',
+ ftype='hidden',
+ value='http://jabber.org/protocol/pubsub#publish-options')
+
+ for field, value in self.profile.items():
+ if field not in options.fields:
+ options.add_field(var=field)
+ options.fields[field]['value'] = value
+
+ return self.xmpp['xep_0163'].publish(stanza, node,
+ options=options,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def retrieve(self, node, id=None, item_ids=None, ifrom=None,
+ block=True, callback=None, timeout=None):
+ """
+ Retrieve private data via PEP.
+
+ This is just a (very) thin wrapper around the XEP-0060 publish()
+ method to set the defaults expected by PEP.
+
+ Arguments:
+ node -- The node to retrieve content from.
+ id -- Optionally specify the ID of the item.
+ item_ids -- Specify a group of IDs. If id is also specified, it
+ will be included in item_ids.
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ if item_ids is None:
+ item_ids = []
+ if id is not None:
+ item_ids.append(id)
+
+ return self.xmpp['xep_0060'].get_items(None, node,
+ item_ids=item_ids,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+
+register_plugin(XEP_0223)
diff --git a/sleekxmpp/plugins/xep_0231/__init__.py b/sleekxmpp/plugins/xep_0231/__init__.py
index 6a70cc07..2861d67b 100644
--- a/sleekxmpp/plugins/xep_0231/__init__.py
+++ b/sleekxmpp/plugins/xep_0231/__init__.py
@@ -1,6 +1,6 @@
"""
SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2012 Nathanael C. Fritz,
+ Copyright (C) 2012 Nathanael C. Fritz,
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
This file is part of SleekXMPP.
diff --git a/sleekxmpp/plugins/xep_0231/bob.py b/sleekxmpp/plugins/xep_0231/bob.py
index 011a1952..f411a8f7 100644
--- a/sleekxmpp/plugins/xep_0231/bob.py
+++ b/sleekxmpp/plugins/xep_0231/bob.py
@@ -1,6 +1,6 @@
"""
SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2012 Nathanael C. Fritz,
+ Copyright (C) 2012 Nathanael C. Fritz,
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
This file is part of SleekXMPP.
@@ -58,7 +58,6 @@ class XEP_0231(BasePlugin):
self.api.register(self._set_bob, 'set_bob', default=True)
self.api.register(self._del_bob, 'del_bob', default=True)
-
def set_bob(self, data, mtype, cid=None, max_age=None):
if cid is None:
cid = 'sha1+%s@bob.xmpp.org' % hashlib.sha1(data).hexdigest()
@@ -73,7 +72,7 @@ class XEP_0231(BasePlugin):
return cid
- def get_bob(self, jid=None, cid=None, cached=True, ifrom=None,
+ def get_bob(self, jid=None, cid=None, cached=True, ifrom=None,
block=True, timeout=None, callback=None):
if cached:
data = self.api['get_bob'](None, None, ifrom, args=cid)
@@ -112,7 +111,7 @@ class XEP_0231(BasePlugin):
iq.send()
def _handle_bob(self, stanza):
- self.api['set_bob'](stanza['from'], None,
+ self.api['set_bob'](stanza['from'], None,
stanza['to'], args=stanza['bob'])
self.xmpp.event('bob', stanza)
diff --git a/sleekxmpp/plugins/xep_0231/stanza.py b/sleekxmpp/plugins/xep_0231/stanza.py
index 13d7a5db..a51f5a03 100644
--- a/sleekxmpp/plugins/xep_0231/stanza.py
+++ b/sleekxmpp/plugins/xep_0231/stanza.py
@@ -1,6 +1,6 @@
"""
SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2012 Nathanael C. Fritz,
+ Copyright (C) 2012 Nathanael C. Fritz,
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
This file is part of SleekXMPP.
diff --git a/sleekxmpp/plugins/xep_0258/__init__.py b/sleekxmpp/plugins/xep_0258/__init__.py
new file mode 100644
index 00000000..516a3706
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0258/__init__.py
@@ -0,0 +1,18 @@
+"""
+ 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.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.xep_0258 import stanza
+from sleekxmpp.plugins.xep_0258.stanza import SecurityLabel, Label
+from sleekxmpp.plugins.xep_0258.stanza import DisplayMarking, EquivalentLabel
+from sleekxmpp.plugins.xep_0258.stanza import ESSLabel, Catalog, CatalogItem
+from sleekxmpp.plugins.xep_0258.security_labels import XEP_0258
+
+
+register_plugin(XEP_0258)
diff --git a/sleekxmpp/plugins/xep_0258/security_labels.py b/sleekxmpp/plugins/xep_0258/security_labels.py
new file mode 100644
index 00000000..e0426f32
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0258/security_labels.py
@@ -0,0 +1,40 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp import Iq, Message
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins.xep_0258 import stanza, SecurityLabel, Catalog
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0258(BasePlugin):
+
+ name = 'xep_0258'
+ description = 'XEP-0258: Security Labels in XMPP'
+ dependencies = set(['xep_0030'])
+ stanza = stanza
+
+ def plugin_init(self):
+ self.xmpp['xep_0030'].add_feature(SecurityLabel.namespace)
+
+ register_stanza_plugin(Message, SecurityLabel)
+ register_stanza_plugin(Iq, Catalog)
+
+ def get_catalog(self, jid, ifrom=None, block=True,
+ callback=None, timeout=None):
+ iq = self.xmpp.Iq()
+ iq['to'] = jid
+ iq['from'] = ifrom
+ iq['type'] = 'get'
+ iq.enable('security_label_catalog')
+ return iq.send(block=block, callback=callback, timeout=timeout)
diff --git a/sleekxmpp/plugins/xep_0258/stanza.py b/sleekxmpp/plugins/xep_0258/stanza.py
new file mode 100644
index 00000000..4d828a46
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0258/stanza.py
@@ -0,0 +1,142 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from base64 import b64encode, b64decode
+
+from sleekxmpp.thirdparty.suelta.util import bytes
+
+from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
+
+
+class SecurityLabel(ElementBase):
+ name = 'securitylabel'
+ namespace = 'urn:xmpp:sec-label:0'
+ plugin_attrib = 'security_label'
+
+ def add_equivalent(self, label):
+ equiv = EquivalentLabel(parent=self)
+ equiv.append(label)
+ return equiv
+
+
+class Label(ElementBase):
+ name = 'label'
+ namespace = 'urn:xmpp:sec-label:0'
+ plugin_attrib = 'label'
+
+
+class DisplayMarking(ElementBase):
+ name = 'displaymarking'
+ namespace = 'urn:xmpp:sec-label:0'
+ plugin_attrib = 'display_marking'
+ interfaces = set(['fgcolor', 'bgcolor', 'value'])
+
+ def get_fgcolor(self):
+ return self._get_attr('fgcolor', 'black')
+
+ def get_bgcolor(self):
+ return self._get_attr('fgcolor', 'white')
+
+ def get_value(self):
+ return self.xml.text
+
+ def set_value(self, value):
+ self.xml.text = value
+
+ def del_value(self):
+ self.xml.text = ''
+
+
+class EquivalentLabel(ElementBase):
+ name = 'equivalentlabel'
+ namespace = 'urn:xmpp:sec-label:0'
+ plugin_attrib = 'equivalent_label'
+ plugin_multi_attrib = 'equivalent_labels'
+
+
+class Catalog(ElementBase):
+ name = 'catalog'
+ namespace = 'urn:xmpp:sec-label:catalog:2'
+ plugin_attrib = 'security_label_catalog'
+ interfaces = set(['to', 'from', 'name', 'desc', 'id', 'size', 'restrict'])
+
+ def get_to(self):
+ return JID(self._get_attr('to'))
+ pass
+
+ def set_to(self, value):
+ return self._set_attr('to', str(value))
+
+ def get_from(self):
+ return JID(self._get_attr('from'))
+
+ def set_from(self, value):
+ return self._set_attr('from', str(value))
+
+ def get_restrict(self):
+ value = self._get_attr('restrict', '')
+ if value and value.lower() in ('true', '1'):
+ return True
+ return False
+
+ def set_restrict(self, value):
+ self._del_attr('restrict')
+ if value:
+ self._set_attr('restrict', 'true')
+ elif value is False:
+ self._set_attr('restrict', 'false')
+
+
+class CatalogItem(ElementBase):
+ name = 'catalog'
+ namespace = 'urn:xmpp:sec-label:catalog:2'
+ plugin_attrib = 'item'
+ plugin_multi_attrib = 'items'
+ interfaces = set(['selector', 'default'])
+
+ def get_default(self):
+ value = self._get_attr('default', '')
+ if value.lower() in ('true', '1'):
+ return True
+ return False
+
+ def set_default(self, value):
+ self._del_attr('default')
+ if value:
+ self._set_attr('default', 'true')
+ elif value is False:
+ self._set_attr('default', 'false')
+
+
+class ESSLabel(ElementBase):
+ name = 'esssecuritylabel'
+ namespace = 'urn:xmpp:sec-label:ess:0'
+ plugin_attrib = 'ess'
+ interfaces = set(['value'])
+
+ def get_value(self):
+ if self.xml.text:
+ return b64decode(bytes(self.xml.text))
+ return ''
+
+ def set_value(self, value):
+ self.xml.text = ''
+ if value:
+ self.xml.text = b64encode(bytes(value))
+
+ def del_value(self):
+ self.xml.text = ''
+
+
+register_stanza_plugin(Catalog, CatalogItem, iterable=True)
+register_stanza_plugin(CatalogItem, SecurityLabel)
+register_stanza_plugin(EquivalentLabel, ESSLabel)
+register_stanza_plugin(Label, ESSLabel)
+register_stanza_plugin(SecurityLabel, DisplayMarking)
+register_stanza_plugin(SecurityLabel, EquivalentLabel, iterable=True)
+register_stanza_plugin(SecurityLabel, Label)