diff options
53 files changed, 455 insertions, 55 deletions
@@ -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.8 <http://github.com/fritzy/SleekXMPP/zipball/1.1.8>`_ **Develop Releases** - `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_ diff --git a/examples/echo_client.py b/examples/echo_client.py index 7e553c4a..73990089 100755 --- a/examples/echo_client.py +++ b/examples/echo_client.py @@ -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 @@ -86,6 +86,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', diff --git a/sleekxmpp/api.py b/sleekxmpp/api.py index 103de2ff..4004f5b7 100644 --- a/sleekxmpp/api.py +++ b/sleekxmpp/api.py @@ -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 diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 275275d1..da5b3e41 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -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) @@ -655,6 +659,7 @@ 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) diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index 03070b06..48637dad 100644 --- a/sleekxmpp/clientxmpp.py +++ b/sleekxmpp/clientxmpp.py @@ -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. diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py index d69d8266..20748b69 100644 --- a/sleekxmpp/componentxmpp.py +++ b/sleekxmpp/componentxmpp.py @@ -156,7 +156,9 @@ 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.full, direct=True) self.event("session_start") def _handle_probe(self, presence): diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py index b828e26f..2253d5ae 100644 --- a/sleekxmpp/features/feature_bind/bind.py +++ b/sleekxmpp/features/feature_bind/bind.py @@ -50,7 +50,8 @@ 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.event('session_bind', self.xmpp.boundjid.full, direct=True) + self.xmpp.session_bind_event.set() self.xmpp.features.add('bind') diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index fef7072c..1613ac4d 100644 --- a/sleekxmpp/plugins/__init__.py +++ b/sleekxmpp/plugins/__init__.py @@ -47,6 +47,7 @@ __all__ = [ 'xep_0172', # User Nickname 'xep_0184', # Message Receipts 'xep_0186', # Invisible Command + 'xep_0191', # Simple Communications Blocking 'xep_0198', # Stream Management 'xep_0199', # Ping 'xep_0202', # Entity Time diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py index 337db2db..26f0c827 100644 --- a/sleekxmpp/plugins/base.py +++ b/sleekxmpp/plugins/base.py @@ -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. diff --git a/sleekxmpp/plugins/xep_0004/dataforms.py b/sleekxmpp/plugins/xep_0004/dataforms.py index 1097bd29..dde6e6a8 100644 --- a/sleekxmpp/plugins/xep_0004/dataforms.py +++ b/sleekxmpp/plugins/xep_0004/dataforms.py @@ -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=''): diff --git a/sleekxmpp/plugins/xep_0012/last_activity.py b/sleekxmpp/plugins/xep_0012/last_activity.py index b71b6907..8790b47c 100644 --- a/sleekxmpp/plugins/xep_0012/last_activity.py +++ b/sleekxmpp/plugins/xep_0012/last_activity.py @@ -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)
diff --git a/sleekxmpp/plugins/xep_0027/gpg.py b/sleekxmpp/plugins/xep_0027/gpg.py index 7cc128bd..9c6ca078 100644 --- a/sleekxmpp/plugins/xep_0027/gpg.py +++ b/sleekxmpp/plugins/xep_0027/gpg.py @@ -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 \ diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py index 18c1dba2..eeb977b1 100644 --- a/sleekxmpp/plugins/xep_0030/disco.py +++ b/sleekxmpp/plugins/xep_0030/disco.py @@ -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() diff --git a/sleekxmpp/plugins/xep_0030/static.py b/sleekxmpp/plugins/xep_0030/static.py index 8dd412d4..dd5317d1 100644 --- a/sleekxmpp/plugins/xep_0030/static.py +++ b/sleekxmpp/plugins/xep_0030/static.py @@ -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: diff --git a/sleekxmpp/plugins/xep_0033/addresses.py b/sleekxmpp/plugins/xep_0033/addresses.py index 78b9fbb5..13cb7267 100644 --- a/sleekxmpp/plugins/xep_0033/addresses.py +++ b/sleekxmpp/plugins/xep_0033/addresses.py @@ -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) + diff --git a/sleekxmpp/plugins/xep_0047/ibb.py b/sleekxmpp/plugins/xep_0047/ibb.py index c8a4b5e7..2b8c57d4 100644 --- a/sleekxmpp/plugins/xep_0047/ibb.py +++ b/sleekxmpp/plugins/xep_0047/ibb.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0050/adhoc.py b/sleekxmpp/plugins/xep_0050/adhoc.py index fb3af7cf..a833221a 100644 --- a/sleekxmpp/plugins/xep_0050/adhoc.py +++ b/sleekxmpp/plugins/xep_0050/adhoc.py @@ -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()) diff --git a/sleekxmpp/plugins/xep_0054/vcard_temp.py b/sleekxmpp/plugins/xep_0054/vcard_temp.py index 672f948a..83cbccf8 100644 --- a/sleekxmpp/plugins/xep_0054/vcard_temp.py +++ b/sleekxmpp/plugins/xep_0054/vcard_temp.py @@ -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() diff --git a/sleekxmpp/plugins/xep_0059/rsm.py b/sleekxmpp/plugins/xep_0059/rsm.py index 9335ed22..59cfc10b 100644 --- a/sleekxmpp/plugins/xep_0059/rsm.py +++ b/sleekxmpp/plugins/xep_0059/rsm.py @@ -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. diff --git a/sleekxmpp/plugins/xep_0060/pubsub.py b/sleekxmpp/plugins/xep_0060/pubsub.py index 31e59be9..387c5a0f 100644 --- a/sleekxmpp/plugins/xep_0060/pubsub.py +++ b/sleekxmpp/plugins/xep_0060/pubsub.py @@ -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'] diff --git a/sleekxmpp/plugins/xep_0066/oob.py b/sleekxmpp/plugins/xep_0066/oob.py index dc215e83..959c15a2 100644 --- a/sleekxmpp/plugins/xep_0066/oob.py +++ b/sleekxmpp/plugins/xep_0066/oob.py @@ -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) diff --git a/sleekxmpp/plugins/xep_0077/register.py b/sleekxmpp/plugins/xep_0077/register.py index 1d04ab25..7f00354b 100644 --- a/sleekxmpp/plugins/xep_0077/register.py +++ b/sleekxmpp/plugins/xep_0077/register.py @@ -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 diff --git a/sleekxmpp/plugins/xep_0078/legacyauth.py b/sleekxmpp/plugins/xep_0078/legacyauth.py index 95587843..8ea78fba 100644 --- a/sleekxmpp/plugins/xep_0078/legacyauth.py +++ b/sleekxmpp/plugins/xep_0078/legacyauth.py @@ -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']: diff --git a/sleekxmpp/plugins/xep_0080/geoloc.py b/sleekxmpp/plugins/xep_0080/geoloc.py index 28c69a2d..ba594cce 100644 --- a/sleekxmpp/plugins/xep_0080/geoloc.py +++ b/sleekxmpp/plugins/xep_0080/geoloc.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0084/avatar.py b/sleekxmpp/plugins/xep_0084/avatar.py index 14ab7d97..bbac330a 100644 --- a/sleekxmpp/plugins/xep_0084/avatar.py +++ b/sleekxmpp/plugins/xep_0084/avatar.py @@ -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, diff --git a/sleekxmpp/plugins/xep_0085/chat_states.py b/sleekxmpp/plugins/xep_0085/chat_states.py index d10b317b..17e19d35 100644 --- a/sleekxmpp/plugins/xep_0085/chat_states.py +++ b/sleekxmpp/plugins/xep_0085/chat_states.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0092/version.py b/sleekxmpp/plugins/xep_0092/version.py index 5e84b2ff..463da158 100644 --- a/sleekxmpp/plugins/xep_0092/version.py +++ b/sleekxmpp/plugins/xep_0092/version.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0107/user_mood.py b/sleekxmpp/plugins/xep_0107/user_mood.py index 95e17d45..2d2f3551 100644 --- a/sleekxmpp/plugins/xep_0107/user_mood.py +++ b/sleekxmpp/plugins/xep_0107/user_mood.py @@ -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, diff --git a/sleekxmpp/plugins/xep_0108/user_activity.py b/sleekxmpp/plugins/xep_0108/user_activity.py index cd4f48d1..3a2f49b8 100644 --- a/sleekxmpp/plugins/xep_0108/user_activity.py +++ b/sleekxmpp/plugins/xep_0108/user_activity.py @@ -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, diff --git a/sleekxmpp/plugins/xep_0115/caps.py b/sleekxmpp/plugins/xep_0115/caps.py index b0cba42d..8ce10edb 100644 --- a/sleekxmpp/plugins/xep_0115/caps.py +++ b/sleekxmpp/plugins/xep_0115/caps.py @@ -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']) diff --git a/sleekxmpp/plugins/xep_0118/user_tune.py b/sleekxmpp/plugins/xep_0118/user_tune.py index 53a4f51a..1bb00122 100644 --- a/sleekxmpp/plugins/xep_0118/user_tune.py +++ b/sleekxmpp/plugins/xep_0118/user_tune.py @@ -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, diff --git a/sleekxmpp/plugins/xep_0128/extended_disco.py b/sleekxmpp/plugins/xep_0128/extended_disco.py index 5adc2368..d785affe 100644 --- a/sleekxmpp/plugins/xep_0128/extended_disco.py +++ b/sleekxmpp/plugins/xep_0128/extended_disco.py @@ -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) diff --git a/sleekxmpp/plugins/xep_0153/vcard_avatar.py b/sleekxmpp/plugins/xep_0153/vcard_avatar.py index 3f36d135..1e32595a 100644 --- a/sleekxmpp/plugins/xep_0153/vcard_avatar.py +++ b/sleekxmpp/plugins/xep_0153/vcard_avatar.py @@ -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) diff --git a/sleekxmpp/plugins/xep_0163.py b/sleekxmpp/plugins/xep_0163.py index 43d3ad3a..5aa3aef9 100644 --- a/sleekxmpp/plugins/xep_0163.py +++ b/sleekxmpp/plugins/xep_0163.py @@ -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: diff --git a/sleekxmpp/plugins/xep_0172/user_nick.py b/sleekxmpp/plugins/xep_0172/user_nick.py index 324407c3..cab13c15 100644 --- a/sleekxmpp/plugins/xep_0172/user_nick.py +++ b/sleekxmpp/plugins/xep_0172/user_nick.py @@ -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, diff --git a/sleekxmpp/plugins/xep_0184/receipt.py b/sleekxmpp/plugins/xep_0184/receipt.py index 83d89269..044fa83f 100644 --- a/sleekxmpp/plugins/xep_0184/receipt.py +++ b/sleekxmpp/plugins/xep_0184/receipt.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0191/__init__.py b/sleekxmpp/plugins/xep_0191/__init__.py new file mode 100644 index 00000000..934ac631 --- /dev/null +++ b/sleekxmpp/plugins/xep_0191/__init__.py @@ -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) diff --git a/sleekxmpp/plugins/xep_0191/blocking.py b/sleekxmpp/plugins/xep_0191/blocking.py new file mode 100644 index 00000000..0d903acc --- /dev/null +++ b/sleekxmpp/plugins/xep_0191/blocking.py @@ -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: Simple Communications Blocking' + 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) diff --git a/sleekxmpp/plugins/xep_0191/stanza.py b/sleekxmpp/plugins/xep_0191/stanza.py new file mode 100644 index 00000000..c5a284bd --- /dev/null +++ b/sleekxmpp/plugins/xep_0191/stanza.py @@ -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' diff --git a/sleekxmpp/plugins/xep_0198/stream_management.py b/sleekxmpp/plugins/xep_0198/stream_management.py index 05d5856f..a150ad39 100644 --- a/sleekxmpp/plugins/xep_0198/stream_management.py +++ b/sleekxmpp/plugins/xep_0198/stream_management.py @@ -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() diff --git a/sleekxmpp/plugins/xep_0199/ping.py b/sleekxmpp/plugins/xep_0199/ping.py index 851e5ae5..b9d145aa 100644 --- a/sleekxmpp/plugins/xep_0199/ping.py +++ b/sleekxmpp/plugins/xep_0199/ping.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0202/time.py b/sleekxmpp/plugins/xep_0202/time.py index 319a9bc5..50af4730 100644 --- a/sleekxmpp/plugins/xep_0202/time.py +++ b/sleekxmpp/plugins/xep_0202/time.py @@ -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):
diff --git a/sleekxmpp/plugins/xep_0224/attention.py b/sleekxmpp/plugins/xep_0224/attention.py index 6eea5d9d..4e560604 100644 --- a/sleekxmpp/plugins/xep_0224/attention.py +++ b/sleekxmpp/plugins/xep_0224/attention.py @@ -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=''): diff --git a/sleekxmpp/plugins/xep_0231/bob.py b/sleekxmpp/plugins/xep_0231/bob.py index f411a8f7..d86a5ddf 100644 --- a/sleekxmpp/plugins/xep_0231/bob.py +++ b/sleekxmpp/plugins/xep_0231/bob.py @@ -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() diff --git a/sleekxmpp/plugins/xep_0249/invite.py b/sleekxmpp/plugins/xep_0249/invite.py index 737684f5..4b7abd4a 100644 --- a/sleekxmpp/plugins/xep_0249/invite.py +++ b/sleekxmpp/plugins/xep_0249/invite.py @@ -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): diff --git a/sleekxmpp/plugins/xep_0256.py b/sleekxmpp/plugins/xep_0256.py index 265a5da8..dd407fff 100644 --- a/sleekxmpp/plugins/xep_0256.py +++ b/sleekxmpp/plugins/xep_0256.py @@ -9,6 +9,7 @@ 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 @@ -35,6 +36,10 @@ class XEP_0256(BasePlugin): 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() diff --git a/sleekxmpp/plugins/xep_0258/security_labels.py b/sleekxmpp/plugins/xep_0258/security_labels.py index e0426f32..439143c1 100644 --- a/sleekxmpp/plugins/xep_0258/security_labels.py +++ b/sleekxmpp/plugins/xep_0258/security_labels.py @@ -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() diff --git a/sleekxmpp/thirdparty/suelta/util.py b/sleekxmpp/thirdparty/suelta/util.py index 7d822a81..cd2439d5 100644 --- a/sleekxmpp/thirdparty/suelta/util.py +++ b/sleekxmpp/thirdparty/suelta/util.py @@ -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) diff --git a/sleekxmpp/version.py b/sleekxmpp/version.py index 782a8560..eb39fd68 100644 --- a/sleekxmpp/version.py +++ b/sleekxmpp/version.py @@ -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.8' +__version_info__ = (1, 1, 8, '', 0) diff --git a/sleekxmpp/xmlstream/resolver.py b/sleekxmpp/xmlstream/resolver.py index 455ab8d0..0d7a8c0d 100644 --- a/sleekxmpp/xmlstream/resolver.py +++ b/sleekxmpp/xmlstream/resolver.py @@ -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) diff --git a/sleekxmpp/xmlstream/scheduler.py b/sleekxmpp/xmlstream/scheduler.py index 70e36f24..f68af081 100644 --- a/sleekxmpp/xmlstream/scheduler.py +++ b/sleekxmpp/xmlstream/scheduler.py @@ -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, diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 3baa5b80..49f33933 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -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. |