Compare commits
30 Commits
1.1.7
...
sleek-1.1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
acd9c32a9f | ||
![]() |
b8581b0278 | ||
![]() |
917faecdcb | ||
![]() |
f6edaa56a6 | ||
![]() |
51fee28bf4 | ||
![]() |
e8a3e92ceb | ||
![]() |
5df3839b7a | ||
![]() |
8dcb441f44 | ||
![]() |
a347cf625a | ||
![]() |
46f49c7a12 | ||
![]() |
99701c947e | ||
![]() |
1baae1b81e | ||
![]() |
7d20f0e9a6 | ||
![]() |
fbad22a1cd | ||
![]() |
5af2f62c04 | ||
![]() |
4a4a03858e | ||
![]() |
6819b57353 | ||
![]() |
88b5e60807 | ||
![]() |
a26a8bd79c | ||
![]() |
9307a6915f | ||
![]() |
85ef2d8d0b | ||
![]() |
c2c7cc032b | ||
![]() |
e4911e9391 | ||
![]() |
b11e1ee92d | ||
![]() |
5027d00c10 | ||
![]() |
69ddeceb49 | ||
![]() |
82698672bb | ||
![]() |
9cec284947 | ||
![]() |
dc501d1902 | ||
![]() |
100e504b7f |
@@ -45,7 +45,7 @@ The latest source code for SleekXMPP may be found on `Github
|
||||
``develop`` branch.
|
||||
|
||||
**Latest Release**
|
||||
- `1.1.7 <http://github.com/fritzy/SleekXMPP/zipball/1.1.7>`_
|
||||
- `1.1.9 <http://github.com/fritzy/SleekXMPP/zipball/1.1.9>`_
|
||||
|
||||
**Develop Releases**
|
||||
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
|
||||
@@ -74,6 +74,7 @@ help with SleekXMPP.
|
||||
**Chat**
|
||||
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
|
||||
|
||||
|
||||
Documentation and Testing
|
||||
-------------------------
|
||||
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
|
||||
|
@@ -122,6 +122,19 @@ if __name__ == '__main__':
|
||||
xmpp.register_plugin('xep_0060') # PubSub
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
|
||||
# If you are connecting to Facebook and wish to use the
|
||||
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
|
||||
# your API key and an access token. Then you'll set:
|
||||
# xmpp.credentials['api_key'] = 'THE_API_KEY'
|
||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||
|
||||
# If you are connecting to MSN, then you will need an
|
||||
# access token, and it does not matter what JID you
|
||||
# specify other than that the domain is 'messenger.live.com',
|
||||
# so '_@messenger.live.com' will work. You can specify
|
||||
# the access token as so:
|
||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||
|
||||
# If you are working with an OpenFire server, you may need
|
||||
# to adjust the SSL version used:
|
||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
||||
|
1
setup.py
1
setup.py
@@ -85,6 +85,7 @@ packages = [ 'sleekxmpp',
|
||||
'sleekxmpp/plugins/xep_0172',
|
||||
'sleekxmpp/plugins/xep_0184',
|
||||
'sleekxmpp/plugins/xep_0186',
|
||||
'sleekxmpp/plugins/xep_0191',
|
||||
'sleekxmpp/plugins/xep_0198',
|
||||
'sleekxmpp/plugins/xep_0199',
|
||||
'sleekxmpp/plugins/xep_0202',
|
||||
|
@@ -99,7 +99,7 @@ class APIRegistry(object):
|
||||
"""
|
||||
self._setup(ctype, op)
|
||||
|
||||
if jid in (None, ''):
|
||||
if not jid:
|
||||
jid = self.xmpp.boundjid
|
||||
if jid and not isinstance(jid, JID):
|
||||
jid = JID(jid)
|
||||
@@ -113,7 +113,7 @@ class APIRegistry(object):
|
||||
else:
|
||||
jid = jid.full
|
||||
else:
|
||||
if self.settings[ctype].get('client_bare', True):
|
||||
if self.settings[ctype].get('client_bare', False):
|
||||
jid = jid.bare
|
||||
else:
|
||||
jid = jid.full
|
||||
|
@@ -16,6 +16,7 @@ from __future__ import with_statement, unicode_literals
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import threading
|
||||
|
||||
import sleekxmpp
|
||||
from sleekxmpp import plugins, features, roster
|
||||
@@ -69,8 +70,11 @@ class BaseXMPP(XMLStream):
|
||||
|
||||
#: The JabberID (JID) used by this connection.
|
||||
self.boundjid = JID(jid)
|
||||
|
||||
self._expected_server_name = self.boundjid.host
|
||||
|
||||
self.session_bind_event = threading.Event()
|
||||
|
||||
#: A dictionary mapping plugin names to plugins.
|
||||
self.plugin = PluginManager(self)
|
||||
|
||||
@@ -87,13 +91,13 @@ class BaseXMPP(XMLStream):
|
||||
#: owner JIDs, as in the case for components. For clients
|
||||
#: which only have a single JID, see :attr:`client_roster`.
|
||||
self.roster = roster.Roster(self)
|
||||
self.roster.add(self.boundjid.bare)
|
||||
self.roster.add(self.boundjid)
|
||||
|
||||
#: The single roster for the bound JID. This is the
|
||||
#: equivalent of::
|
||||
#:
|
||||
#: self.roster[self.boundjid.bare]
|
||||
self.client_roster = self.roster[self.boundjid.bare]
|
||||
self.client_roster = self.roster[self.boundjid]
|
||||
|
||||
#: The distinction between clients and components can be
|
||||
#: important, primarily for choosing how to handle the
|
||||
@@ -134,6 +138,7 @@ class BaseXMPP(XMLStream):
|
||||
Callback('Presence',
|
||||
MatchXPath("{%s}presence" % self.default_ns),
|
||||
self._handle_presence))
|
||||
|
||||
self.register_handler(
|
||||
Callback('Stream Error',
|
||||
MatchXPath("{%s}error" % self.stream_ns),
|
||||
@@ -654,27 +659,45 @@ class BaseXMPP(XMLStream):
|
||||
def _handle_disconnected(self, event):
|
||||
"""When disconnected, reset the roster"""
|
||||
self.roster.reset()
|
||||
self.session_bind_event.clear()
|
||||
|
||||
def _handle_stream_error(self, error):
|
||||
self.event('stream_error', error)
|
||||
|
||||
if error['condition'] == 'see-other-host':
|
||||
other_host = error['see_other_host']
|
||||
|
||||
host = other_host
|
||||
port = 5222
|
||||
|
||||
if '[' in other_host and ']' in other_host:
|
||||
host = other_host.split(']')[0][1:]
|
||||
elif ':' in other_host:
|
||||
host = other_host.split(':')[0]
|
||||
|
||||
port_sec = other_host.split(']')[-1]
|
||||
if ':' in port_sec:
|
||||
port = int(port_sec.split(':')[1])
|
||||
|
||||
self.address = (host, port)
|
||||
self.default_domain = host
|
||||
self.dns_records = None
|
||||
self.reconnect_delay = None
|
||||
self.reconnect()
|
||||
|
||||
def _handle_message(self, msg):
|
||||
"""Process incoming message stanzas."""
|
||||
if not self.is_component and not msg['to'].bare:
|
||||
msg['to'] = self.boundjid
|
||||
self.event('message', msg)
|
||||
|
||||
def _handle_available(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_available(presence)
|
||||
def _handle_available(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_available(pres)
|
||||
|
||||
def _handle_unavailable(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_unavailable(presence)
|
||||
def _handle_unavailable(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_unavailable(pres)
|
||||
|
||||
def _handle_new_subscription(self, stanza):
|
||||
def _handle_new_subscription(self, pres):
|
||||
"""Attempt to automatically handle subscription requests.
|
||||
|
||||
Subscriptions will be approved if the request is from
|
||||
@@ -686,8 +709,8 @@ class BaseXMPP(XMLStream):
|
||||
If a subscription is accepted, a request for a mutual
|
||||
subscription will be sent if :attr:`auto_subscribe` is ``True``.
|
||||
"""
|
||||
roster = self.roster[stanza['to'].bare]
|
||||
item = self.roster[stanza['to'].bare][stanza['from'].bare]
|
||||
roster = self.roster[pres['to']]
|
||||
item = self.roster[pres['to']][pres['from']]
|
||||
if item['whitelisted']:
|
||||
item.authorize()
|
||||
elif roster.auto_authorize:
|
||||
@@ -697,30 +720,20 @@ class BaseXMPP(XMLStream):
|
||||
elif roster.auto_authorize == False:
|
||||
item.unauthorize()
|
||||
|
||||
def _handle_removed_subscription(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].unauthorize()
|
||||
def _handle_removed_subscription(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_unauthorize(pres)
|
||||
|
||||
def _handle_subscribe(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_subscribe(presence)
|
||||
def _handle_subscribe(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_subscribe(pres)
|
||||
|
||||
def _handle_subscribed(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_subscribed(presence)
|
||||
def _handle_subscribed(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_subscribed(pres)
|
||||
|
||||
def _handle_unsubscribe(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_unsubscribe(presence)
|
||||
def _handle_unsubscribe(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_unsubscribe(pres)
|
||||
|
||||
def _handle_unsubscribed(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_unsubscribed(presence)
|
||||
def _handle_unsubscribed(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_unsubscribed(pres)
|
||||
|
||||
def _handle_presence(self, presence):
|
||||
"""Process incoming presence stanzas.
|
||||
|
@@ -173,6 +173,12 @@ class ClientXMPP(BaseXMPP):
|
||||
self._stream_feature_order.append((order, name))
|
||||
self._stream_feature_order.sort()
|
||||
|
||||
def unregister_feature(self, name, order):
|
||||
if name in self._stream_feature_handlers:
|
||||
del self._stream_feature_handlers[name]
|
||||
self._stream_feature_order.remove((order, name))
|
||||
self._stream_feature_order.sort()
|
||||
|
||||
def update_roster(self, jid, name=None, subscription=None, groups=[],
|
||||
block=True, timeout=None, callback=None):
|
||||
"""Add or change a roster item.
|
||||
@@ -270,8 +276,9 @@ class ClientXMPP(BaseXMPP):
|
||||
roster = self.client_roster
|
||||
if iq['roster']['ver']:
|
||||
roster.version = iq['roster']['ver']
|
||||
for jid in iq['roster']['items']:
|
||||
item = iq['roster']['items'][jid]
|
||||
items = iq['roster']['items']
|
||||
for jid in items:
|
||||
item = items[jid]
|
||||
roster[jid]['name'] = item['name']
|
||||
roster[jid]['groups'] = item['groups']
|
||||
roster[jid]['from'] = item['subscription'] in ['from', 'both']
|
||||
|
@@ -156,10 +156,10 @@ class ComponentXMPP(BaseXMPP):
|
||||
|
||||
:param xml: The reply handshake stanza.
|
||||
"""
|
||||
self.session_bind_event.set()
|
||||
self.session_started_event.set()
|
||||
self.event("session_bind", self.xmpp.boundjid, direct=True)
|
||||
self.event("session_start")
|
||||
|
||||
def _handle_probe(self, presence):
|
||||
pto = presence['to'].bare
|
||||
pfrom = presence['from'].bare
|
||||
self.roster[pto][pfrom].handle_probe(presence)
|
||||
def _handle_probe(self, pres):
|
||||
self.roster[pres['to']][pres['from']].handle_probe(pres)
|
||||
|
@@ -51,6 +51,7 @@ class FeatureBind(BasePlugin):
|
||||
self.xmpp.set_jid(response['bind']['jid'])
|
||||
self.xmpp.bound = True
|
||||
self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
|
||||
self.xmpp.session_bind_event.set()
|
||||
|
||||
self.xmpp.features.add('bind')
|
||||
|
||||
|
@@ -46,6 +46,7 @@ __all__ = [
|
||||
'xep_0172', # User Nickname
|
||||
'xep_0184', # Message Receipts
|
||||
'xep_0186', # Invisible Command
|
||||
'xep_0191', # Blocking Command
|
||||
'xep_0198', # Stream Management
|
||||
'xep_0199', # Ping
|
||||
'xep_0202', # Entity Time
|
||||
@@ -56,5 +57,8 @@ __all__ = [
|
||||
'xep_0224', # Attention
|
||||
'xep_0231', # Bits of Binary
|
||||
'xep_0249', # Direct MUC Invitations
|
||||
'xep_0256', # Last Activity in Presence
|
||||
'xep_0258', # Security Labels in XMPP
|
||||
'xep_0270', # XMPP Compliance Suites 2010
|
||||
'xep_0302', # XMPP Compliance Suites 2012
|
||||
]
|
||||
|
@@ -167,8 +167,7 @@ class PluginManager(object):
|
||||
self._plugins[name] = plugin
|
||||
for dep in plugin.dependencies:
|
||||
self.enable(dep, enabled=enabled)
|
||||
plugin.plugin_init()
|
||||
log.debug("Loaded Plugin: %s", plugin.description)
|
||||
plugin._init()
|
||||
|
||||
if top_level:
|
||||
for name in enabled:
|
||||
@@ -229,7 +228,7 @@ class PluginManager(object):
|
||||
raise PluginNotFound(name)
|
||||
for dep in PLUGIN_DEPENDENTS[name]:
|
||||
self.disable(dep, _disabled)
|
||||
plugin.plugin_end()
|
||||
plugin._end()
|
||||
if name in self._enabled:
|
||||
self._enabled.remove(name)
|
||||
del self._plugins[name]
|
||||
@@ -282,6 +281,28 @@ class BasePlugin(object):
|
||||
#: configuration settings will be provided as a dictionary.
|
||||
self.config = config if config is not None else {}
|
||||
|
||||
def _init(self):
|
||||
"""Initialize plugin state, such as registering event handlers.
|
||||
|
||||
Also sets up required event handlers.
|
||||
"""
|
||||
if self.xmpp is not None:
|
||||
self.xmpp.add_event_handler('session_bind', self.session_bind)
|
||||
if self.xmpp.session_bind_event.is_set():
|
||||
self.session_bind(self.xmpp.boundjid.full)
|
||||
self.plugin_init()
|
||||
log.debug('Loaded Plugin: %s', self.description)
|
||||
|
||||
def _end(self):
|
||||
"""Cleanup plugin state, and prepare for plugin removal.
|
||||
|
||||
Also removes required event handlers.
|
||||
"""
|
||||
if self.xmpp is not None:
|
||||
self.xmpp.del_event_handler('session_bind', self.session_bind)
|
||||
self.plugin_end()
|
||||
log.debug('Disabled Plugin: %s' % self.description)
|
||||
|
||||
def plugin_init(self):
|
||||
"""Initialize plugin state, such as registering event handlers."""
|
||||
pass
|
||||
@@ -290,6 +311,10 @@ class BasePlugin(object):
|
||||
"""Cleanup plugin state, and prepare for plugin removal."""
|
||||
pass
|
||||
|
||||
def session_bind(self, jid):
|
||||
"""Initialize plugin state based on the bound JID."""
|
||||
pass
|
||||
|
||||
def post_init(self):
|
||||
"""Initialize any cross-plugin state.
|
||||
|
||||
|
@@ -27,7 +27,7 @@ class XEP_0004(BasePlugin):
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
self.xmpp.registerHandler(
|
||||
self.xmpp.register_handler(
|
||||
Callback('Data Form',
|
||||
StanzaPath('message/form'),
|
||||
self.handle_form))
|
||||
@@ -36,6 +36,11 @@ class XEP_0004(BasePlugin):
|
||||
register_stanza_plugin(Form, FormField, iterable=True)
|
||||
register_stanza_plugin(Message, Form)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Data Form')
|
||||
self.xmpp['xep_0030'].del_feature(feature='jabber:x:data')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('jabber:x:data')
|
||||
|
||||
def make_form(self, ftype='form', title='', instructions=''):
|
||||
|
@@ -37,13 +37,11 @@ class XEP_0012(BasePlugin):
|
||||
|
||||
self._last_activities = {}
|
||||
|
||||
self.xmpp.registerHandler(
|
||||
self.xmpp.register_handler(
|
||||
Callback('Last Activity',
|
||||
StanzaPath('iq@type=get/last_activity'),
|
||||
self._handle_get_last_activity))
|
||||
|
||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:last')
|
||||
|
||||
self.api.register(self._default_get_last_activity,
|
||||
'get_last_activity',
|
||||
default=True)
|
||||
@@ -54,6 +52,13 @@ class XEP_0012(BasePlugin):
|
||||
'del_last_activity',
|
||||
default=True)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Last Activity')
|
||||
self.xmpp['xep_0030'].del_feature(feature='jabber:iq:last')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('jabber:iq:last')
|
||||
|
||||
def begin_idle(self, jid=None, status=None):
|
||||
self.set_last_activity(jid, 0, status)
|
||||
|
||||
|
@@ -79,6 +79,13 @@ class XEP_0027(BasePlugin):
|
||||
StanzaPath('message/encrypted'),
|
||||
self._handle_encrypted_message))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Encrypted Message')
|
||||
self.xmpp.remove_handler('Signed Presence')
|
||||
self.xmpp.del_filter('out', self._sign_presence)
|
||||
self.xmpp.del_event_handler('unverified_signed_presence',
|
||||
self._handle_unverified_signed_presence)
|
||||
|
||||
def _sign_presence(self, stanza):
|
||||
if isinstance(stanza, Presence):
|
||||
if stanza['type'] == 'available' or \
|
||||
|
@@ -622,11 +622,7 @@ class XEP_0030(BasePlugin):
|
||||
if iq['type'] == 'get':
|
||||
log.debug("Received disco info query from " + \
|
||||
"<%s> to <%s>.", iq['from'], iq['to'])
|
||||
if self.xmpp.is_component:
|
||||
jid = iq['to'].full
|
||||
else:
|
||||
jid = iq['to'].bare
|
||||
info = self.api['get_info'](jid,
|
||||
info = self.api['get_info'](iq['to'],
|
||||
iq['disco_info']['node'],
|
||||
iq['from'],
|
||||
iq)
|
||||
@@ -649,7 +645,7 @@ class XEP_0030(BasePlugin):
|
||||
ito = iq['to'].full
|
||||
else:
|
||||
ito = None
|
||||
self.api['cache_info'](iq['from'].full,
|
||||
self.api['cache_info'](iq['from'],
|
||||
iq['disco_info']['node'],
|
||||
ito,
|
||||
iq)
|
||||
@@ -667,13 +663,9 @@ class XEP_0030(BasePlugin):
|
||||
if iq['type'] == 'get':
|
||||
log.debug("Received disco items query from " + \
|
||||
"<%s> to <%s>.", iq['from'], iq['to'])
|
||||
if self.xmpp.is_component:
|
||||
jid = iq['to'].full
|
||||
else:
|
||||
jid = iq['to'].bare
|
||||
items = self.api['get_items'](jid,
|
||||
items = self.api['get_items'](iq['to'],
|
||||
iq['disco_items']['node'],
|
||||
iq['from'].full,
|
||||
iq['from'],
|
||||
iq)
|
||||
if isinstance(items, Iq):
|
||||
items.send()
|
||||
|
@@ -237,7 +237,7 @@ class StaticDisco(object):
|
||||
with self.lock:
|
||||
if not self.node_exists(jid, node):
|
||||
if not node:
|
||||
return DiscoInfo()
|
||||
return DiscoItems()
|
||||
else:
|
||||
raise XMPPError(condition='item-not-found')
|
||||
else:
|
||||
@@ -424,9 +424,6 @@ class StaticDisco(object):
|
||||
The data parameter is not used.
|
||||
"""
|
||||
with self.lock:
|
||||
if isinstance(jid, JID):
|
||||
jid = jid.full
|
||||
|
||||
if not self.node_exists(jid, node, ifrom):
|
||||
return None
|
||||
else:
|
||||
|
@@ -26,7 +26,12 @@ class XEP_0033(BasePlugin):
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
self.xmpp['xep_0030'].add_feature(Addresses.namespace)
|
||||
|
||||
register_stanza_plugin(Message, Addresses)
|
||||
register_stanza_plugin(Presence, Addresses)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=Addresses.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(Addresses.namespace)
|
||||
|
||||
|
@@ -51,6 +51,13 @@ class XEP_0047(BasePlugin):
|
||||
StanzaPath('iq@type=set/ibb_data'),
|
||||
self._handle_data))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('IBB Open')
|
||||
self.xmpp.remove_handler('IBB Close')
|
||||
self.xmpp.remove_handler('IBB Data')
|
||||
self.xmpp['xep_0030'].del_feature(feature='http://jabber.org/protocol/ibb')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/ibb')
|
||||
|
||||
def _accept_stream(self, iq):
|
||||
|
@@ -110,6 +110,20 @@ class XEP_0050(BasePlugin):
|
||||
self._handle_command_complete,
|
||||
threaded=self.threaded)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.del_event_handler('command_execute',
|
||||
self._handle_command_start)
|
||||
self.xmpp.del_event_handler('command_next',
|
||||
self._handle_command_next)
|
||||
self.xmpp.del_event_handler('command_cancel',
|
||||
self._handle_command_cancel)
|
||||
self.xmpp.del_event_handler('command_complete',
|
||||
self._handle_command_complete)
|
||||
self.xmpp.remove_handler('Ad-Hoc Execute')
|
||||
self.xmpp['xep_0030'].del_feature(feature=Command.namespace)
|
||||
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(Command.namespace)
|
||||
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
||||
|
||||
|
@@ -37,7 +37,6 @@ class XEP_0054(BasePlugin):
|
||||
"""
|
||||
register_stanza_plugin(Iq, VCardTemp)
|
||||
|
||||
self.xmpp['xep_0030'].add_feature('vcard-temp')
|
||||
|
||||
self.api.register(self._set_vcard, 'set_vcard', default=True)
|
||||
self.api.register(self._get_vcard, 'get_vcard', default=True)
|
||||
@@ -50,6 +49,13 @@ class XEP_0054(BasePlugin):
|
||||
StanzaPath('iq/vcard_temp'),
|
||||
self._handle_get_vcard))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('VCardTemp')
|
||||
self.xmpp['xep_0030'].del_feature(feature='vcard-temp')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('vcard-temp')
|
||||
|
||||
def make_vcard(self):
|
||||
return VCardTemp()
|
||||
|
||||
|
@@ -47,6 +47,7 @@ class ResultIterator():
|
||||
self.start = start
|
||||
self.interface = interface
|
||||
self.reverse = reverse
|
||||
self._stop = False
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
@@ -62,6 +63,8 @@ class ResultIterator():
|
||||
results will be the items before the current page
|
||||
of items.
|
||||
"""
|
||||
if self._stop:
|
||||
raise StopIteration
|
||||
self.query[self.interface]['rsm']['before'] = self.reverse
|
||||
self.query['id'] = self.query.stream.new_id()
|
||||
self.query[self.interface]['rsm']['max'] = str(self.amount)
|
||||
@@ -84,7 +87,7 @@ class ResultIterator():
|
||||
first = int(r[self.interface]['rsm']['first_index'])
|
||||
num_items = len(r[self.interface]['substanzas'])
|
||||
if first + num_items == count:
|
||||
raise StopIteration
|
||||
self._stop = True
|
||||
|
||||
if self.reverse:
|
||||
self.start = r[self.interface]['rsm']['first']
|
||||
@@ -111,10 +114,15 @@ class XEP_0059(BasePlugin):
|
||||
"""
|
||||
Start the XEP-0059 plugin.
|
||||
"""
|
||||
self.xmpp['xep_0030'].add_feature(Set.namespace)
|
||||
register_stanza_plugin(self.xmpp['xep_0030'].stanza.DiscoItems,
|
||||
self.stanza.Set)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=Set.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(Set.namespace)
|
||||
|
||||
def iterate(self, stanza, interface):
|
||||
"""
|
||||
Create a new result set iterator for a given stanza query.
|
||||
|
@@ -53,6 +53,13 @@ class XEP_0060(BasePlugin):
|
||||
StanzaPath('message/pubsub_event/subscription'),
|
||||
self._handle_event_subscription))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Pubsub Event: Items')
|
||||
self.xmpp.remove_handler('Pubsub Event: Purge')
|
||||
self.xmpp.remove_handler('Pubsub Event: Delete')
|
||||
self.xmpp.remove_handler('Pubsub Event: Configuration')
|
||||
self.xmpp.remove_handler('Pubsub Event: Subscription')
|
||||
|
||||
def _handle_event_items(self, msg):
|
||||
"""Raise events for publish and retraction notifications."""
|
||||
node = msg['pubsub_event']['items']['node']
|
||||
|
@@ -62,6 +62,12 @@ class XEP_0066(BasePlugin):
|
||||
StanzaPath('iq@type=set/oob_transfer'),
|
||||
self._handle_transfer))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('OOB Transfer')
|
||||
self.xmpp['xep_0030'].del_feature(feature=stanza.OOBTransfer.namespace)
|
||||
self.xmpp['xep_0030'].del_feature(feature=stanza.OOB.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(stanza.OOBTransfer.namespace)
|
||||
self.xmpp['xep_0030'].add_feature(stanza.OOB.namespace)
|
||||
|
||||
|
@@ -34,9 +34,7 @@ class XEP_0077(BasePlugin):
|
||||
register_stanza_plugin(StreamFeatures, RegisterFeature)
|
||||
register_stanza_plugin(Iq, Register)
|
||||
|
||||
if self.xmpp.is_component:
|
||||
pass
|
||||
else:
|
||||
if not self.xmpp.is_component:
|
||||
self.xmpp.register_feature('register',
|
||||
self._handle_register_feature,
|
||||
restart=False,
|
||||
@@ -45,6 +43,10 @@ class XEP_0077(BasePlugin):
|
||||
register_stanza_plugin(Register, self.xmpp['xep_0004'].stanza.Form)
|
||||
register_stanza_plugin(Register, self.xmpp['xep_0066'].stanza.OOB)
|
||||
|
||||
def plugin_end(self):
|
||||
if not self.xmpp.is_component:
|
||||
self.xmpp.unregister_feature('register', self.config.get('order', 50))
|
||||
|
||||
def _handle_register_feature(self, features):
|
||||
if 'mechanisms' in self.xmpp.features:
|
||||
# We have already logged in with an account
|
||||
|
@@ -44,6 +44,9 @@ class XEP_0078(BasePlugin):
|
||||
register_stanza_plugin(Iq, stanza.IqAuth)
|
||||
register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.unregister_feature('auth', self.config.get('order', 15))
|
||||
|
||||
def _handle_auth(self, features):
|
||||
# If we can or have already authenticated with SASL, do nothing.
|
||||
if 'mechanisms' in features['features']:
|
||||
|
@@ -28,8 +28,11 @@ class XEP_0080(BasePlugin):
|
||||
dependencies = set(['xep_0163'])
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
"""Start the XEP-0080 plugin."""
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0163'].remove_interest(Geoloc.namespace)
|
||||
self.xmpp['xep_0030'].del_feature(feature=Geoloc.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('user_location', Geoloc)
|
||||
|
||||
def publish_location(self, **kwargs):
|
||||
|
@@ -28,14 +28,19 @@ class XEP_0084(BasePlugin):
|
||||
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 plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=MetaData.namespace)
|
||||
self.xmpp['xep_0163'].remove_interest(MetaData.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('avatar_metadata', MetaData)
|
||||
|
||||
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,
|
||||
|
@@ -43,6 +43,10 @@ class XEP_0085(BasePlugin):
|
||||
register_stanza_plugin(Message, stanza.Inactive)
|
||||
register_stanza_plugin(Message, stanza.Paused)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Chat State')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp.plugin['xep_0030'].add_feature(ChatState.namespace)
|
||||
|
||||
def _handle_chat_state(self, msg):
|
||||
|
@@ -48,6 +48,11 @@ class XEP_0092(BasePlugin):
|
||||
|
||||
register_stanza_plugin(Iq, Version)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Software Version')
|
||||
self.xmpp['xep_0030'].del_feature(feature='jabber:iq:version')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:version')
|
||||
|
||||
def _handle_version(self, iq):
|
||||
|
@@ -32,6 +32,12 @@ class XEP_0107(BasePlugin):
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(Message, UserMood)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=UserMood.namespace)
|
||||
self.xmpp['xep_0163'].remove_interest(UserMood.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('user_mood', UserMood)
|
||||
|
||||
def publish_mood(self, value=None, text=None, options=None,
|
||||
|
@@ -26,7 +26,11 @@ class XEP_0108(BasePlugin):
|
||||
dependencies = set(['xep_0163'])
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=UserActivity.namespace)
|
||||
self.xmpp['xep_0163'].remove_interest(UserActivity.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('user_activity', UserActivity)
|
||||
|
||||
def publish_activity(self, general, specific=None, text=None, options=None,
|
||||
|
@@ -73,16 +73,15 @@ class XEP_0115(BasePlugin):
|
||||
restart=False,
|
||||
order=10010)
|
||||
|
||||
self.xmpp['xep_0030'].add_feature(stanza.Capabilities.namespace)
|
||||
|
||||
disco = self.xmpp['xep_0030']
|
||||
self.static = StaticCaps(self.xmpp, disco.static)
|
||||
|
||||
self.api.settings['client_bare'] = False
|
||||
self.api.settings['component_bare'] = False
|
||||
for op in self._disco_ops:
|
||||
self.api.register(getattr(self.static, op), op, default=True)
|
||||
|
||||
for op in ('supports', 'has_identity'):
|
||||
self.xmpp['xep_0030'].api.register(getattr(self.static, op), op)
|
||||
|
||||
self._run_node_handler = disco._run_node_handler
|
||||
|
||||
disco.cache_caps = self.cache_caps
|
||||
@@ -90,6 +89,19 @@ class XEP_0115(BasePlugin):
|
||||
disco.assign_verstring = self.assign_verstring
|
||||
disco.get_verstring = self.get_verstring
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=stanza.Capabilities.namespace)
|
||||
self.xmpp.del_filter('out', self._filter_add_caps)
|
||||
self.xmpp.del_event_handler('entity_caps', self._process_caps)
|
||||
self.xmpp.remove_handler('Entity Capabilities')
|
||||
if not self.xmpp.is_component:
|
||||
self.xmpp.unregister_feature('caps', 10010)
|
||||
for op in ('supports', 'has_identity'):
|
||||
self.xmpp['xep_0030'].restore_defaults(op)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(stanza.Capabilities.namespace)
|
||||
|
||||
def _filter_add_caps(self, stanza):
|
||||
if isinstance(stanza, Presence) and self.broadcast:
|
||||
ver = self.get_verstring(stanza['from'])
|
||||
|
@@ -26,7 +26,11 @@ class XEP_0118(BasePlugin):
|
||||
dependencies = set(['xep_0163'])
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=UserTune.namespace)
|
||||
self.xmpp['xep_0163'].remove_interest(UserTune.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('user_tune', UserTune)
|
||||
|
||||
def publish_tune(self, artist=None, length=None, rating=None, source=None,
|
||||
|
@@ -51,8 +51,6 @@ class XEP_0128(BasePlugin):
|
||||
|
||||
register_stanza_plugin(DiscoInfo, Form, iterable=True)
|
||||
|
||||
def post_init(self):
|
||||
"""Handle cross-plugin dependencies."""
|
||||
self.disco = self.xmpp['xep_0030']
|
||||
self.static = StaticExtendedDisco(self.disco.static)
|
||||
|
||||
|
@@ -45,6 +45,15 @@ 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 plugin_end(self):
|
||||
self.xmpp.del_filter('out', self._update_presence)
|
||||
self.xmpp.del_event_handler('session_start', self._start)
|
||||
self.xmpp.del_event_handler('presence_available', self._recv_presence)
|
||||
self.xmpp.del_event_handler('presence_dnd', self._recv_presence)
|
||||
self.xmpp.del_event_handler('presence_xa', self._recv_presence)
|
||||
self.xmpp.del_event_handler('presence_chat', self._recv_presence)
|
||||
self.xmpp.del_event_handler('presence_away', self._recv_presence)
|
||||
|
||||
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)
|
||||
|
@@ -74,7 +74,7 @@ class XEP_0163(BasePlugin):
|
||||
be a list of such namespaces.
|
||||
jid -- Optionally specify the JID.
|
||||
"""
|
||||
if not isinstance(namespace, set) and not isinstance(namespace, list):
|
||||
if not isinstance(namespace, (set, list)):
|
||||
namespace = [namespace]
|
||||
|
||||
for ns in namespace:
|
||||
|
@@ -34,6 +34,12 @@ class XEP_0172(BasePlugin):
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(Message, UserNick)
|
||||
register_stanza_plugin(Presence, UserNick)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=UserNick.namespace)
|
||||
self.xmpp['xep_0163'].remove_interest(UserNick.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0163'].register_pep('user_nick', UserNick)
|
||||
|
||||
def publish_nick(self, nick=None, options=None, ifrom=None, block=True,
|
||||
|
@@ -48,6 +48,13 @@ class XEP_0184(BasePlugin):
|
||||
StanzaPath('message/request_receipt'),
|
||||
self._handle_receipt_request))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature('urn:xmpp:receipts')
|
||||
self.xmpp.del_filter('out', self._filter_add_receipt_request)
|
||||
self.xmpp.remove_handler('Message Receipt')
|
||||
self.xmpp.remove_handler('Message Receipt Request')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:receipts')
|
||||
|
||||
def ack(self, msg):
|
||||
|
15
sleekxmpp/plugins/xep_0191/__init__.py
Normal file
15
sleekxmpp/plugins/xep_0191/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
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_0191.stanza import Block, Unblock, BlockList
|
||||
from sleekxmpp.plugins.xep_0191.blocking import XEP_0191
|
||||
|
||||
|
||||
register_plugin(XEP_0191)
|
83
sleekxmpp/plugins/xep_0191/blocking.py
Normal file
83
sleekxmpp/plugins/xep_0191/blocking.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
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
|
||||
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_0191 import stanza, Block, Unblock, BlockList
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0191(BasePlugin):
|
||||
|
||||
name = 'xep_0191'
|
||||
description = 'XEP-0191: Blocking Command'
|
||||
dependencies = set(['xep_0030'])
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(Iq, BlockList)
|
||||
register_stanza_plugin(Iq, Block)
|
||||
register_stanza_plugin(Iq, Unblock)
|
||||
|
||||
self.xmpp.register_handler(
|
||||
Callback('Blocked Contact',
|
||||
StanzaPath('iq@type=set/block'),
|
||||
self._handle_blocked))
|
||||
|
||||
self.xmpp.register_handler(
|
||||
Callback('Unblocked Contact',
|
||||
StanzaPath('iq@type=set/unblock'),
|
||||
self._handle_unblocked))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.remove_handler('Blocked Contact')
|
||||
self.xmpp.remove_handler('Unblocked Contact')
|
||||
|
||||
def get_blocked(self, ifrom=None, block=True, timeout=None, callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['from'] = 'ifrom'
|
||||
iq.enable('blocklist')
|
||||
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||
|
||||
def block(self, jids, ifrom=None, block=True, timeout=None, callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['from'] = ifrom
|
||||
|
||||
if not isinstance(jids, (set, list)):
|
||||
jids = [jids]
|
||||
|
||||
iq['block']['items'] = jids
|
||||
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||
|
||||
def unblock(self, jids=None, ifrom=None, block=True, timeout=None, callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['from'] = ifrom
|
||||
|
||||
if jids is None:
|
||||
jids = []
|
||||
if not isinstance(jids, (set, list)):
|
||||
jids = [jids]
|
||||
|
||||
iq['unblock']['items'] = jids
|
||||
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||
|
||||
def _handle_blocked(self, iq):
|
||||
self.xmpp.event('blocked', iq)
|
||||
|
||||
def _handle_unblocked(self, iq):
|
||||
self.xmpp.event('unblocked', iq)
|
50
sleekxmpp/plugins/xep_0191/stanza.py
Normal file
50
sleekxmpp/plugins/xep_0191/stanza.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
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 ET, ElementBase, JID
|
||||
|
||||
|
||||
class BlockList(ElementBase):
|
||||
name = 'blocklist'
|
||||
namespace = 'urn:xmpp:blocking'
|
||||
plugin_attrib = 'blocklist'
|
||||
interfaces = set(['items'])
|
||||
|
||||
def get_items(self):
|
||||
result = set()
|
||||
items = self.xml.findall('{%s}item' % self.namespace)
|
||||
if items is not None:
|
||||
for item in items:
|
||||
jid = JID(item.attrib.get('jid', ''))
|
||||
if jid:
|
||||
result.add(jid)
|
||||
return result
|
||||
|
||||
def set_items(self, values):
|
||||
self.del_items()
|
||||
for jid in values:
|
||||
if jid:
|
||||
item = ET.Element('{%s}item' % self.namespace)
|
||||
item.attrib['jid'] = JID(jid).full
|
||||
self.xml.append(item)
|
||||
|
||||
def del_items(self):
|
||||
items = self.xml.findall('{%s}item' % self.namespace)
|
||||
if items is not None:
|
||||
for item in items:
|
||||
self.xml.remove(item)
|
||||
|
||||
|
||||
class Block(BlockList):
|
||||
name = 'block'
|
||||
plugin_attrib = 'block'
|
||||
|
||||
|
||||
class Unblock(BlockList):
|
||||
name = 'unblock'
|
||||
plugin_attrib = 'unblock'
|
@@ -133,6 +133,27 @@ class XEP_0198(BasePlugin):
|
||||
|
||||
self.xmpp.add_event_handler('session_end', self.session_end)
|
||||
|
||||
def plugin_end(self):
|
||||
if self.xmpp.is_component:
|
||||
return
|
||||
|
||||
self.xmpp.unregister_feature('sm', self.config.get('order', 10100))
|
||||
self.xmpp.unregister_feature('sm', self.config.get('resume_order', 9000))
|
||||
self.xmpp.del_event_handler('session_end', self.session_end)
|
||||
self.xmpp.del_filter('in', self._handle_incoming)
|
||||
self.xmpp.del_filter('out_sync', self._handle_outgoing)
|
||||
self.xmpp.remove_handler('Stream Management Enabled')
|
||||
self.xmpp.remove_handler('Stream Management Resumed')
|
||||
self.xmpp.remove_handler('Stream Management Failed')
|
||||
self.xmpp.remove_handler('Stream Management Ack')
|
||||
self.xmpp.remove_handler('Stream Management Request Ack')
|
||||
self.xmpp.remove_stanza(stanza.Enable)
|
||||
self.xmpp.remove_stanza(stanza.Enabled)
|
||||
self.xmpp.remove_stanza(stanza.Resume)
|
||||
self.xmpp.remove_stanza(stanza.Resumed)
|
||||
self.xmpp.remove_stanza(stanza.Ack)
|
||||
self.xmpp.remove_stanza(stanza.RequestAck)
|
||||
|
||||
def session_end(self, event):
|
||||
"""Reset stream management state."""
|
||||
self.enabled.clear()
|
||||
|
@@ -74,6 +74,16 @@ class XEP_0199(BasePlugin):
|
||||
self.xmpp.add_event_handler('session_end',
|
||||
self._handle_session_end)
|
||||
|
||||
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.xmpp.del_event_handler('session_end',
|
||||
self._handle_session_end)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(Ping.namespace)
|
||||
|
||||
def _handle_keepalive(self, event):
|
||||
|
@@ -53,6 +53,11 @@ class XEP_0202(BasePlugin):
|
||||
self._handle_time_request))
|
||||
register_stanza_plugin(Iq, stanza.EntityTime)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:time')
|
||||
self.xmpp.remove_handler('Entity Time')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:time')
|
||||
|
||||
def _handle_time_request(self, iq):
|
||||
|
@@ -39,6 +39,11 @@ class XEP_0224(BasePlugin):
|
||||
StanzaPath('message/attention'),
|
||||
self._handle_attention))
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=stanza.Attention.namespace)
|
||||
self.xmpp.remove_handler('Attention')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(stanza.Attention.namespace)
|
||||
|
||||
def request_attention(self, to, mfrom=None, mbody=''):
|
||||
|
@@ -35,8 +35,6 @@ class XEP_0231(BasePlugin):
|
||||
def plugin_init(self):
|
||||
self._cids = {}
|
||||
|
||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:bob')
|
||||
|
||||
register_stanza_plugin(Iq, BitsOfBinary)
|
||||
|
||||
self.xmpp.register_handler(
|
||||
@@ -58,6 +56,15 @@ 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 plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:bob')
|
||||
self.xmpp.remove_handler('Bits of Binary - Iq')
|
||||
self.xmpp.remove_handler('Bits of Binary - Message')
|
||||
self.xmpp.remove_handler('Bits of Binary - Presence')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:bob')
|
||||
|
||||
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()
|
||||
|
@@ -39,6 +39,11 @@ class XEP_0249(BasePlugin):
|
||||
|
||||
register_stanza_plugin(Message, Invite)
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=Invite.namespace)
|
||||
self.xmpp.remove_handler('Direct MUC Invitations')
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(Invite.namespace)
|
||||
|
||||
def _handle_invite(self, msg):
|
||||
|
72
sleekxmpp/plugins/xep_0256.py
Normal file
72
sleekxmpp/plugins/xep_0256.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
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 Presence
|
||||
from sleekxmpp.exceptions import XMPPError
|
||||
from sleekxmpp.plugins import BasePlugin, register_plugin
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
from sleekxmpp.plugins.xep_0012 import stanza, LastActivity
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0256(BasePlugin):
|
||||
|
||||
name = 'xep_0256'
|
||||
description = 'XEP-0256: Last Activity in Presence'
|
||||
dependencies = set(['xep_0012'])
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
self.auto_last_activity = self.config.get('auto_last_activity', False)
|
||||
|
||||
register_stanza_plugin(Presence, LastActivity)
|
||||
|
||||
self.xmpp.add_filter('out', self._initial_presence_activity)
|
||||
self.xmpp.add_event_handler('connected', self._reset_presence_activity)
|
||||
|
||||
self._initial_presence = set()
|
||||
|
||||
def plugin_end(self):
|
||||
self.xmpp.del_filter('out', self._initial_presence_activity)
|
||||
self.xmpp.del_event_handler('connected', self._reset_presence_activity)
|
||||
|
||||
def _reset_presence_activity(self, e):
|
||||
self._initial_presence = set()
|
||||
|
||||
def _initial_presence_activity(self, stanza):
|
||||
if isinstance(stanza, Presence):
|
||||
use_last_activity = False
|
||||
|
||||
if self.auto_last_activity and stanza['show'] in ('xa', 'away'):
|
||||
use_last_activity = True
|
||||
|
||||
if stanza['from'] not in self._initial_presence:
|
||||
self._initial_presence.add(stanza['from'])
|
||||
use_last_activity = True
|
||||
|
||||
if use_last_activity:
|
||||
plugin = self.xmpp['xep_0012']
|
||||
try:
|
||||
result = plugin.api['get_last_activity'](stanza['from'],
|
||||
None,
|
||||
stanza['to'])
|
||||
seconds = result['last_activity']['seconds']
|
||||
except XMPPError:
|
||||
seconds = None
|
||||
|
||||
if seconds is not None:
|
||||
stanza['last_activity']['seconds'] = seconds
|
||||
return stanza
|
||||
|
||||
|
||||
register_plugin(XEP_0256)
|
@@ -25,11 +25,15 @@ class XEP_0258(BasePlugin):
|
||||
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 plugin_end(self):
|
||||
self.xmpp['xep_0030'].del_feature(feature=SecurityLabel.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
self.xmpp['xep_0030'].add_feature(SecurityLabel.namespace)
|
||||
|
||||
def get_catalog(self, jid, ifrom=None, block=True,
|
||||
callback=None, timeout=None):
|
||||
iq = self.xmpp.Iq()
|
||||
|
20
sleekxmpp/plugins/xep_0270.py
Normal file
20
sleekxmpp/plugins/xep_0270.py
Normal file
@@ -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 import BasePlugin, register_plugin
|
||||
|
||||
|
||||
class XEP_0270(BasePlugin):
|
||||
|
||||
name = 'xep_0270'
|
||||
description = 'XEP-0270: XMPP Compliance Suites 2010'
|
||||
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
|
||||
'xep_0163', 'xep_0045', 'xep_0085'])
|
||||
|
||||
|
||||
register_plugin(XEP_0270)
|
21
sleekxmpp/plugins/xep_0302.py
Normal file
21
sleekxmpp/plugins/xep_0302.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
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 import BasePlugin, register_plugin
|
||||
|
||||
|
||||
class XEP_0302(BasePlugin):
|
||||
|
||||
name = 'xep_0302'
|
||||
description = 'XEP-0302: XMPP Compliance Suites 2012'
|
||||
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
|
||||
'xep_0163', 'xep_0045', 'xep_0085',
|
||||
'xep_0184', 'xep_0198'])
|
||||
|
||||
|
||||
register_plugin(XEP_0302)
|
@@ -94,10 +94,12 @@ class Roster(object):
|
||||
Arguments:
|
||||
key -- Return the roster for this JID.
|
||||
"""
|
||||
if isinstance(key, JID):
|
||||
key = key.bare
|
||||
if key is None:
|
||||
key = self.xmpp.boundjid.bare
|
||||
key = self.xmpp.boundjid
|
||||
if not isinstance(key, JID):
|
||||
key = JID(key)
|
||||
key = key.bare
|
||||
|
||||
if key not in self._rosters:
|
||||
self.add(key)
|
||||
self._rosters[key].auto_authorize = self.auto_authorize
|
||||
@@ -119,8 +121,10 @@ class Roster(object):
|
||||
Arguments:
|
||||
node -- The JID for the new roster node.
|
||||
"""
|
||||
if isinstance(node, JID):
|
||||
node = node.bare
|
||||
if not isinstance(node, JID):
|
||||
node = JID(node)
|
||||
|
||||
node = node.bare
|
||||
if node not in self._rosters:
|
||||
self._rosters[node] = RosterNode(self.xmpp, node, self.db)
|
||||
|
||||
|
@@ -89,8 +89,11 @@ class RosterNode(object):
|
||||
|
||||
A new item entry will be created if one does not already exist.
|
||||
"""
|
||||
if isinstance(key, JID):
|
||||
key = key.bare
|
||||
if key is None:
|
||||
key = JID('')
|
||||
if not isinstance(key, JID):
|
||||
key = JID(key)
|
||||
key = key.bare
|
||||
if key not in self._jids:
|
||||
self.add(key, save=True)
|
||||
return self._jids[key]
|
||||
@@ -101,8 +104,11 @@ class RosterNode(object):
|
||||
|
||||
To remove an item from the server, use the remove() method.
|
||||
"""
|
||||
if isinstance(key, JID):
|
||||
key = key.bare
|
||||
if key is None:
|
||||
key = JID('')
|
||||
if not isinstance(key, JID):
|
||||
key = JID(key)
|
||||
key = key.bare
|
||||
if key in self._jids:
|
||||
del self._jids[key]
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza.rootstanza import RootStanza
|
||||
from sleekxmpp.xmlstream import StanzaBase
|
||||
from sleekxmpp.xmlstream import StanzaBase, ET
|
||||
|
||||
|
||||
class Message(RootStanza):
|
||||
@@ -54,13 +54,14 @@ class Message(RootStanza):
|
||||
del_mucnick -- Dummy method to prevent deletion.
|
||||
"""
|
||||
|
||||
namespace = 'jabber:client'
|
||||
name = 'message'
|
||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject',
|
||||
'mucroom', 'mucnick'))
|
||||
sub_interfaces = set(('body', 'subject'))
|
||||
namespace = 'jabber:client'
|
||||
plugin_attrib = name
|
||||
types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat'))
|
||||
interfaces = set(['type', 'to', 'from', 'id', 'body', 'subject',
|
||||
'thread', 'parent_thread', 'mucroom', 'mucnick'])
|
||||
sub_interfaces = set(['body', 'subject', 'thread'])
|
||||
lang_interfaces = sub_interfaces
|
||||
types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
|
||||
|
||||
def get_type(self):
|
||||
"""
|
||||
@@ -72,6 +73,31 @@ class Message(RootStanza):
|
||||
"""
|
||||
return self._get_attr('type', 'normal')
|
||||
|
||||
def get_parent_thread(self):
|
||||
"""Return the message thread's parent thread."""
|
||||
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||
if thread is not None:
|
||||
return thread.attrib.get('parent', '')
|
||||
return ''
|
||||
|
||||
def set_parent_thread(self, value):
|
||||
"""Add or change the message thread's parent thread."""
|
||||
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||
if value:
|
||||
if thread is None:
|
||||
thread = ET.Element('{%s}thread' % self.namespace)
|
||||
self.xml.append(thread)
|
||||
thread.attrib['parent'] = value
|
||||
else:
|
||||
if thread is not None and 'parent' in thread.attrib:
|
||||
del thread.attrib['parent']
|
||||
|
||||
def del_parent_thread(self):
|
||||
"""Delete the message thread's parent reference."""
|
||||
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||
if thread is not None and 'parent' in thread.attrib:
|
||||
del thread.attrib['parent']
|
||||
|
||||
def chat(self):
|
||||
"""Set the message type to 'chat'."""
|
||||
self['type'] = 'chat'
|
||||
@@ -96,10 +122,16 @@ class Message(RootStanza):
|
||||
clear -- Indicates if existing content should be removed
|
||||
before replying. Defaults to True.
|
||||
"""
|
||||
thread = self['thread']
|
||||
parent = self['parent_thread']
|
||||
|
||||
StanzaBase.reply(self, clear)
|
||||
if self['type'] == 'groupchat':
|
||||
self['to'] = self['to'].bare
|
||||
|
||||
self['thread'] = thread
|
||||
self['parent_thread'] = parent
|
||||
|
||||
del self['id']
|
||||
|
||||
if body is not None:
|
||||
|
@@ -60,16 +60,17 @@ class Presence(RootStanza):
|
||||
set_priority -- Set the value of the <priority> element.
|
||||
"""
|
||||
|
||||
namespace = 'jabber:client'
|
||||
name = 'presence'
|
||||
interfaces = set(('type', 'to', 'from', 'id', 'show',
|
||||
'status', 'priority'))
|
||||
sub_interfaces = set(('show', 'status', 'priority'))
|
||||
namespace = 'jabber:client'
|
||||
plugin_attrib = name
|
||||
interfaces = set(['type', 'to', 'from', 'id', 'show',
|
||||
'status', 'priority'])
|
||||
sub_interfaces = set(['show', 'status', 'priority'])
|
||||
lang_interfaces = set(['status'])
|
||||
|
||||
types = set(('available', 'unavailable', 'error', 'probe', 'subscribe',
|
||||
'subscribed', 'unsubscribe', 'unsubscribed'))
|
||||
showtypes = set(('dnd', 'chat', 'xa', 'away'))
|
||||
types = set(['available', 'unavailable', 'error', 'probe', 'subscribe',
|
||||
'subscribed', 'unsubscribe', 'unsubscribed'])
|
||||
showtypes = set(['dnd', 'chat', 'xa', 'away'])
|
||||
|
||||
def exception(self, e):
|
||||
"""
|
||||
|
3
sleekxmpp/thirdparty/suelta/util.py
vendored
3
sleekxmpp/thirdparty/suelta/util.py
vendored
@@ -15,6 +15,9 @@ def bytes(text):
|
||||
:param text: Unicode text to convert to bytes
|
||||
:rtype: bytes (Python3), str (Python2.6+)
|
||||
"""
|
||||
if text is None:
|
||||
return b''
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
import __builtin__
|
||||
return __builtin__.bytes(text)
|
||||
|
@@ -9,5 +9,5 @@
|
||||
# We don't want to have to import the entire library
|
||||
# just to get the version info for setup.py
|
||||
|
||||
__version__ = '1.1.7'
|
||||
__version_info__ = (1, 1, 7, '', 0)
|
||||
__version__ = '1.1.9'
|
||||
__version_info__ = (1, 1, 9, '', 0)
|
||||
|
@@ -143,3 +143,6 @@ class JID(object):
|
||||
def __hash__(self):
|
||||
"""Hash a JID based on the string version of its full JID."""
|
||||
return hash(self.full)
|
||||
|
||||
def __copy__(self):
|
||||
return JID(self.jid)
|
||||
|
@@ -254,6 +254,7 @@ def get_SRV(host, port, service, proto='tcp', resolver=None):
|
||||
by SRV priorities and weights.
|
||||
"""
|
||||
if resolver is None:
|
||||
log.warning("DNS: dnspython not found. Can not use SRV lookup.")
|
||||
return [(host, port)]
|
||||
|
||||
log.debug("DNS: Querying SRV records for %s" % host)
|
||||
|
@@ -172,7 +172,8 @@ class Scheduler(object):
|
||||
else:
|
||||
updated = True
|
||||
self.schedule_lock.acquire()
|
||||
self.schedule.append(newtask)
|
||||
if newtask is not None:
|
||||
self.schedule.append(newtask)
|
||||
finally:
|
||||
if updated:
|
||||
self.schedule = sorted(self.schedule,
|
||||
|
@@ -421,12 +421,6 @@ class ElementBase(object):
|
||||
#: ``'{namespace}elementname'``.
|
||||
self.tag = self.tag_name()
|
||||
|
||||
if 'lang' not in self.interfaces:
|
||||
if isinstance(self.interfaces, tuple):
|
||||
self.interfaces += ('lang',)
|
||||
else:
|
||||
self.interfaces.add('lang')
|
||||
|
||||
#: A :class:`weakref.weakref` to the parent stanza, if there is one.
|
||||
#: If not, then :attr:`parent` is ``None``.
|
||||
self.parent = None
|
||||
@@ -574,6 +568,7 @@ class ElementBase(object):
|
||||
.. versionadded:: 1.0-Beta1
|
||||
"""
|
||||
values = {}
|
||||
values['lang'] = self['lang']
|
||||
for interface in self.interfaces:
|
||||
values[interface] = self[interface]
|
||||
if interface in self.lang_interfaces:
|
||||
@@ -629,6 +624,8 @@ class ElementBase(object):
|
||||
sub.values = subdict
|
||||
self.iterables.append(sub)
|
||||
break
|
||||
elif interface == 'lang':
|
||||
self[interface] = value
|
||||
elif interface in self.interfaces:
|
||||
self[full_interface] = value
|
||||
elif interface in self.plugin_attrib_map:
|
||||
@@ -678,7 +675,7 @@ class ElementBase(object):
|
||||
|
||||
if attrib == 'substanzas':
|
||||
return self.iterables
|
||||
elif attrib in self.interfaces:
|
||||
elif attrib in self.interfaces or attrib == 'lang':
|
||||
get_method = "get_%s" % attrib.lower()
|
||||
get_method2 = "get%s" % attrib.title()
|
||||
|
||||
@@ -752,7 +749,7 @@ class ElementBase(object):
|
||||
if lang and attrib in self.lang_interfaces:
|
||||
kwargs['lang'] = lang
|
||||
|
||||
if attrib in self.interfaces:
|
||||
if attrib in self.interfaces or attrib == 'lang':
|
||||
if value is not None:
|
||||
set_method = "set_%s" % attrib.lower()
|
||||
set_method2 = "set%s" % attrib.title()
|
||||
@@ -838,7 +835,7 @@ class ElementBase(object):
|
||||
if lang and attrib in self.lang_interfaces:
|
||||
kwargs['lang'] = lang
|
||||
|
||||
if attrib in self.interfaces:
|
||||
if attrib in self.interfaces or attrib == 'lang':
|
||||
del_method = "del_%s" % attrib.lower()
|
||||
del_method2 = "del%s" % attrib.title()
|
||||
|
||||
@@ -973,10 +970,6 @@ class ElementBase(object):
|
||||
:param keep: Indicates if the element should be kept if its text is
|
||||
removed. Defaults to False.
|
||||
"""
|
||||
path = self._fix_ns(name, split=True)
|
||||
element = self.xml.find(name)
|
||||
parent = self.xml
|
||||
|
||||
default_lang = self.get_lang()
|
||||
if lang is None:
|
||||
lang = default_lang
|
||||
@@ -984,32 +977,51 @@ class ElementBase(object):
|
||||
if not text and not keep:
|
||||
return self._del_sub(name, lang=lang)
|
||||
|
||||
if element is None:
|
||||
# We need to add the element. If the provided name was
|
||||
# an XPath expression, some of the intermediate elements
|
||||
# may already exist. If so, we want to use those instead
|
||||
# of generating new elements.
|
||||
last_xml = self.xml
|
||||
walked = []
|
||||
for ename in path:
|
||||
walked.append(ename)
|
||||
element = self.xml.find("/".join(walked))
|
||||
if element is None:
|
||||
element = ET.Element(ename)
|
||||
if lang:
|
||||
element.attrib['{%s}lang' % XML_NS] = lang
|
||||
last_xml.append(element)
|
||||
parent = last_xml
|
||||
last_xml = element
|
||||
element = last_xml
|
||||
path = self._fix_ns(name, split=True)
|
||||
name = path[-1]
|
||||
parent = self.xml
|
||||
|
||||
if lang:
|
||||
if element.attrib.get('{%s}lang' % XML_NS, default_lang) != lang:
|
||||
element = ET.Element(ename)
|
||||
element.attrib['{%s}lang' % XML_NS] = lang
|
||||
parent.append(element)
|
||||
# The first goal is to find the parent of the subelement, or, if
|
||||
# we can't find that, the closest grandparent element.
|
||||
missing_path = []
|
||||
search_order = path[:-1]
|
||||
while search_order:
|
||||
parent = self.xml.find('/'.join(search_order))
|
||||
ename = search_order.pop()
|
||||
if parent is not None:
|
||||
break
|
||||
else:
|
||||
missing_path.append(ename)
|
||||
missing_path.reverse()
|
||||
|
||||
# Find all existing elements that match the desired
|
||||
# element path (there may be multiples due to different
|
||||
# languages values).
|
||||
if parent is not None:
|
||||
elements = self.xml.findall('/'.join(path))
|
||||
else:
|
||||
parent = self.xml
|
||||
elements = []
|
||||
|
||||
# Insert the remaining grandparent elements that don't exist yet.
|
||||
for ename in missing_path:
|
||||
element = ET.Element(ename)
|
||||
parent.append(element)
|
||||
parent = element
|
||||
|
||||
# Re-use an existing element with the proper language, if one exists.
|
||||
for element in elements:
|
||||
elang = element.attrib.get('{%s}lang' % XML_NS, default_lang)
|
||||
if not lang and elang == default_lang or lang and lang == elang:
|
||||
element.text = text
|
||||
return element
|
||||
|
||||
# No useable element exists, so create a new one.
|
||||
element = ET.Element(name)
|
||||
element.text = text
|
||||
if lang and lang != default_lang:
|
||||
element.attrib['{%s}lang' % XML_NS] = lang
|
||||
parent.append(element)
|
||||
return element
|
||||
|
||||
def _set_all_sub_text(self, name, values, keep=False, lang=None):
|
||||
@@ -1184,6 +1196,7 @@ class ElementBase(object):
|
||||
out = []
|
||||
out += [x for x in self.interfaces]
|
||||
out += [x for x in self.loaded_plugins]
|
||||
out.append('lang')
|
||||
if self.iterables:
|
||||
out.append('substanzas')
|
||||
return out
|
||||
@@ -1263,7 +1276,7 @@ class ElementBase(object):
|
||||
"""
|
||||
return "{%s}%s" % (cls.namespace, cls.name)
|
||||
|
||||
def get_lang(self):
|
||||
def get_lang(self, lang=None):
|
||||
result = self.xml.attrib.get('{%s}lang' % XML_NS, '')
|
||||
if not result and self.parent and self.parent():
|
||||
return self.parent()['lang']
|
||||
|
@@ -537,8 +537,8 @@ class XMLStream(object):
|
||||
try:
|
||||
cert.verify(self._expected_server_name, self._der_cert)
|
||||
except cert.CertificateError as err:
|
||||
log.error(err.message)
|
||||
if not self.event_handled('ssl_invalid_cert'):
|
||||
log.error(err.message)
|
||||
self.disconnect(send_close=False)
|
||||
else:
|
||||
self.event('ssl_invalid_cert',
|
||||
@@ -828,8 +828,8 @@ class XMLStream(object):
|
||||
try:
|
||||
cert.verify(self._expected_server_name, self._der_cert)
|
||||
except cert.CertificateError as err:
|
||||
log.error(err.message)
|
||||
if not self.event_handled('ssl_invalid_cert'):
|
||||
log.error(err.message)
|
||||
self.disconnect(self.auto_reconnect, send_close=False)
|
||||
else:
|
||||
self.event('ssl_invalid_cert', pem_cert, direct=True)
|
||||
@@ -954,6 +954,10 @@ class XMLStream(object):
|
||||
else:
|
||||
self.__filters[mode].append(handler)
|
||||
|
||||
def del_filter(self, mode, handler):
|
||||
"""Remove an incoming or outgoing filter."""
|
||||
self.__filters[mode].remove(handler)
|
||||
|
||||
def add_handler(self, mask, pointer, name=None, disposable=False,
|
||||
threaded=False, filter=False, instream=False):
|
||||
"""A shortcut method for registering a handler using XML masks.
|
||||
|
@@ -1,5 +1,6 @@
|
||||
from sleekxmpp.test import *
|
||||
from sleekxmpp.xmlstream.stanzabase import ElementBase
|
||||
from sleekxmpp.thirdparty import OrderedDict
|
||||
|
||||
|
||||
class TestElementBase(SleekTest):
|
||||
@@ -760,7 +761,7 @@ class TestElementBase(SleekTest):
|
||||
<foo xmlns="foo" />
|
||||
""")
|
||||
|
||||
self.assertFalse(stanza['bar'],
|
||||
self.assertFalse(stanza['bar'],
|
||||
"Returned True for missing bool interface element.")
|
||||
|
||||
stanza['bar'] = True
|
||||
@@ -797,7 +798,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -829,9 +830,9 @@ class TestElementBase(SleekTest):
|
||||
<baz xmlns="baz" />
|
||||
""")
|
||||
|
||||
self.assertEqual(len(bars), 2,
|
||||
self.assertEqual(len(bars), 2,
|
||||
"Wrong number of <bar /> stanzas: %s" % len(bars))
|
||||
self.assertEqual(len(bazs), 2,
|
||||
self.assertEqual(len(bazs), 2,
|
||||
"Wrong number of <baz /> stanzas: %s" % len(bazs))
|
||||
|
||||
def testSetMultiAttrib(self):
|
||||
@@ -853,7 +854,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -906,7 +907,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -938,5 +939,313 @@ class TestElementBase(SleekTest):
|
||||
self.assertEqual(len(stanza['substanzas']), 2,
|
||||
"Wrong number of substanzas: %s" % len(stanza['substanzas']))
|
||||
|
||||
def testDefaultLang(self):
|
||||
"""Test setting a normal subinterface when a default language is set"""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['lang'] = 'sv'
|
||||
stanza['test'] = 'hej'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="sv">
|
||||
<test>hej</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'hej',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test|sv'], 'hej',
|
||||
"Incorrect subinterface value: %s" % stanza['test|sv'])
|
||||
|
||||
def testSpecifyLangWithDefault(self):
|
||||
"""Test specifying various languages."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['lang'] = 'sv'
|
||||
stanza['test'] = 'hej'
|
||||
stanza['test|en'] = 'hi'
|
||||
stanza['test|es'] = 'hola'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="sv">
|
||||
<test>hej</test>
|
||||
<test xml:lang="en">hi</test>
|
||||
<test xml:lang="es">hola</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'hej',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test|sv'], 'hej',
|
||||
"Incorrect subinterface value: %s" % stanza['test|sv'])
|
||||
|
||||
self.assertEqual(stanza['test|en'], 'hi',
|
||||
"Incorrect subinterface value: %s" % stanza['test|en'])
|
||||
|
||||
self.assertEqual(stanza['test|es'], 'hola',
|
||||
"Incorrect subinterface value: %s" % stanza['test|es'])
|
||||
|
||||
def testSpecifyLangWithNoDefault(self):
|
||||
"""Test specifying various languages."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['test'] = 'hej'
|
||||
stanza['test|en'] = 'hi'
|
||||
stanza['test|es'] = 'hola'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test>hej</test>
|
||||
<test xml:lang="en">hi</test>
|
||||
<test xml:lang="es">hola</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'hej',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test|en'], 'hi',
|
||||
"Incorrect subinterface value: %s" % stanza['test|en'])
|
||||
|
||||
self.assertEqual(stanza['test|es'], 'hola',
|
||||
"Incorrect subinterface value: %s" % stanza['test|es'])
|
||||
|
||||
def testModifyLangInterfaceWithDefault(self):
|
||||
"""Test resetting an interface when a default lang is used."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['lang'] = 'es'
|
||||
stanza['test'] = 'hola'
|
||||
stanza['test|en'] = 'hi'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="es">
|
||||
<test>hola</test>
|
||||
<test xml:lang="en">hi</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
stanza['test'] = 'adios'
|
||||
stanza['test|en'] = 'bye'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="es">
|
||||
<test>adios</test>
|
||||
<test xml:lang="en">bye</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'adios',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test|es'], 'adios',
|
||||
"Incorrect subinterface value: %s" % stanza['test|es'])
|
||||
|
||||
self.assertEqual(stanza['test|en'], 'bye',
|
||||
"Incorrect subinterface value: %s" % stanza['test|en'])
|
||||
|
||||
stanza['test|es'] = 'hola'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="es">
|
||||
<test>hola</test>
|
||||
<test xml:lang="en">bye</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'hola',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test|es'], 'hola',
|
||||
"Incorrect subinterface value: %s" % stanza['test|es'])
|
||||
|
||||
def testModifyLangInterfaceWithNoDefault(self):
|
||||
"""Test resetting an interface when no default lang is used."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['test'] = 'hola'
|
||||
stanza['test|en'] = 'hi'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test>hola</test>
|
||||
<test xml:lang="en">hi</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
stanza['test'] = 'adios'
|
||||
stanza['test|en'] = 'bye'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test>adios</test>
|
||||
<test xml:lang="en">bye</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
self.assertEqual(stanza['test'], 'adios',
|
||||
"Incorrect subinterface value: %s" % stanza['test'])
|
||||
|
||||
self.assertEqual(stanza['test'], 'adios',
|
||||
"Incorrect subinterface value: %s" % stanza['test|es'])
|
||||
|
||||
self.assertEqual(stanza['test|en'], 'bye',
|
||||
"Incorrect subinterface value: %s" % stanza['test|en'])
|
||||
|
||||
def testDelInterfacesWithDefaultLang(self):
|
||||
"""Test deleting interfaces with a default lang set."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['lang'] = 'en'
|
||||
stanza['test'] = 'hi'
|
||||
stanza['test|no'] = 'hej'
|
||||
stanza['test|fr'] = 'bonjour'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="en">
|
||||
<test>hi</test>
|
||||
<test xml:lang="no">hej</test>
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
del stanza['test']
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="en">
|
||||
<test xml:lang="no">hej</test>
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
del stanza['test|no']
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" xml:lang="en">
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
def testDelInterfacesWithNoDefaultLang(self):
|
||||
"""Test deleting interfaces with no default lang set."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['test'] = 'hi'
|
||||
stanza['test|no'] = 'hej'
|
||||
stanza['test|fr'] = 'bonjour'
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test>hi</test>
|
||||
<test xml:lang="no">hej</test>
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
del stanza['test']
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test xml:lang="no">hej</test>
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
del stanza['test|no']
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
def testStarLang(self):
|
||||
"""Test using interface|*."""
|
||||
|
||||
class TestStanza(ElementBase):
|
||||
name = 'foo'
|
||||
namespace = 'test'
|
||||
interfaces = set(['test'])
|
||||
sub_interfaces = interfaces
|
||||
lang_interfaces = interfaces
|
||||
|
||||
data = OrderedDict()
|
||||
data['en'] = 'hi'
|
||||
data['fr'] = 'bonjour'
|
||||
data['no'] = 'hej'
|
||||
|
||||
stanza = TestStanza()
|
||||
stanza['test|*'] = data
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test">
|
||||
<test xml:lang="en">hi</test>
|
||||
<test xml:lang="fr">bonjour</test>
|
||||
<test xml:lang="no">hej</test>
|
||||
</foo>
|
||||
""")
|
||||
|
||||
data2 = stanza['test|*']
|
||||
|
||||
self.assertEqual(data, data2,
|
||||
"Did not extract expected language data: %s" % data2)
|
||||
|
||||
del stanza['test|*']
|
||||
|
||||
self.check(stanza, """
|
||||
<foo xmlns="test" />
|
||||
""")
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase)
|
||||
|
Reference in New Issue
Block a user