diff options
| author | Thibaut Horel <thibaut.horel@gmail.com> | 2010-12-31 19:19:25 +0100 |
|---|---|---|
| committer | Thibaut Horel <thibaut.horel@gmail.com> | 2010-12-31 19:19:25 +0100 |
| commit | d90aec17e2201f256783a531c548dcc9857c889d (patch) | |
| tree | 56b6d0580ee1993c73e67c63d4a452a81bbaaf1e /sleekxmpp/basexmpp.py | |
| parent | af76bcdf7a947702eaa19d39f5b9ecfcd7ec6fd2 (diff) | |
| download | alias-d90aec17e2201f256783a531c548dcc9857c889d.tar.gz | |
Cleanup of repository. Bases of webclient.
* remove sleekxmpp (install guideline in server/README)
* move server code to server directory
* webclient directory with basic strophejs example
Diffstat (limited to 'sleekxmpp/basexmpp.py')
| -rw-r--r-- | sleekxmpp/basexmpp.py | 634 |
1 files changed, 0 insertions, 634 deletions
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py deleted file mode 100644 index cd7d251..0000000 --- a/sleekxmpp/basexmpp.py +++ /dev/null @@ -1,634 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -from __future__ import with_statement, unicode_literals - -import sys -import copy -import logging - -import sleekxmpp -from sleekxmpp import plugins - -from sleekxmpp.stanza import Message, Presence, Iq, Error -from sleekxmpp.stanza.roster import Roster -from sleekxmpp.stanza.nick import Nick -from sleekxmpp.stanza.htmlim import HTMLIM - -from sleekxmpp.xmlstream import XMLStream, JID, tostring -from sleekxmpp.xmlstream import ET, register_stanza_plugin -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * - - -log = logging.getLogger(__name__) - -# In order to make sure that Unicode is handled properly -# in Python 2.x, reset the default encoding. -if sys.version_info < (3, 0): - reload(sys) - sys.setdefaultencoding('utf8') - - -class BaseXMPP(XMLStream): - - """ - The BaseXMPP class adapts the generic XMLStream class for use - with XMPP. It also provides a plugin mechanism to easily extend - and add support for new XMPP features. - - Attributes: - auto_authorize -- Manage automatically accepting roster - subscriptions. - auto_subscribe -- Manage automatically requesting mutual - subscriptions. - is_component -- Indicates if this stream is for an XMPP component. - jid -- The XMPP JID for this stream. - plugin -- A dictionary of loaded plugins. - plugin_config -- A dictionary of plugin configurations. - plugin_whitelist -- A list of approved plugins. - sentpresence -- Indicates if an initial presence has been sent. - roster -- A dictionary containing subscribed JIDs and - their presence statuses. - - Methods: - Iq -- Factory for creating an Iq stanzas. - Message -- Factory for creating Message stanzas. - Presence -- Factory for creating Presence stanzas. - get -- Return a plugin given its name. - make_iq -- Create and initialize an Iq stanza. - make_iq_error -- Create an Iq stanza of type 'error'. - make_iq_get -- Create an Iq stanza of type 'get'. - make_iq_query -- Create an Iq stanza with a given query. - make_iq_result -- Create an Iq stanza of type 'result'. - make_iq_set -- Create an Iq stanza of type 'set'. - make_message -- Create and initialize a Message stanza. - make_presence -- Create and initialize a Presence stanza. - make_query_roster -- Create a roster query. - process -- Overrides XMLStream.process. - register_plugin -- Load and configure a plugin. - register_plugins -- Load and configure multiple plugins. - send_message -- Create and send a Message stanza. - send_presence -- Create and send a Presence stanza. - send_presence_subscribe -- Send a subscription request. - """ - - def __init__(self, default_ns='jabber:client'): - """ - Adapt an XML stream for use with XMPP. - - Arguments: - default_ns -- Ensure that the correct default XML namespace - is used during initialization. - """ - XMLStream.__init__(self) - - # To comply with PEP8, method names now use underscores. - # Deprecated method names are re-mapped for backwards compatibility. - self.registerPlugin = self.register_plugin - self.makeIq = self.make_iq - self.makeIqGet = self.make_iq_get - self.makeIqResult = self.make_iq_result - self.makeIqSet = self.make_iq_set - self.makeIqError = self.make_iq_error - self.makeIqQuery = self.make_iq_query - self.makeQueryRoster = self.make_query_roster - self.makeMessage = self.make_message - self.makePresence = self.make_presence - self.sendMessage = self.send_message - self.sendPresence = self.send_presence - self.sendPresenceSubscription = self.send_presence_subscription - - self.default_ns = default_ns - self.stream_ns = 'http://etherx.jabber.org/streams' - - self.boundjid = JID("") - - self.plugin = {} - self.roster = {} - self.is_component = False - self.auto_authorize = True - self.auto_subscribe = True - - self.sentpresence = False - - self.register_handler( - Callback('IM', - MatchXPath('{%s}message/{%s}body' % (self.default_ns, - self.default_ns)), - self._handle_message)) - self.register_handler( - Callback('Presence', - MatchXPath("{%s}presence" % self.default_ns), - self._handle_presence)) - - self.add_event_handler('presence_subscribe', - self._handle_subscribe) - self.add_event_handler('disconnected', - self._handle_disconnected) - - # Set up the XML stream with XMPP's root stanzas. - self.registerStanza(Message) - self.registerStanza(Iq) - self.registerStanza(Presence) - - # Initialize a few default stanza plugins. - register_stanza_plugin(Iq, Roster) - register_stanza_plugin(Message, Nick) - register_stanza_plugin(Message, HTMLIM) - - def process(self, *args, **kwargs): - """ - Ensure that plugin inter-dependencies are handled before starting - event processing. - - Overrides XMLStream.process. - """ - for name in self.plugin: - if not self.plugin[name].post_inited: - self.plugin[name].post_init() - return XMLStream.process(self, *args, **kwargs) - - def register_plugin(self, plugin, pconfig={}, module=None): - """ - Register and configure a plugin for use in this stream. - - Arguments: - plugin -- The name of the plugin class. Plugin names must - be unique. - pconfig -- A dictionary of configuration data for the plugin. - Defaults to an empty dictionary. - module -- Optional refence to the module containing the plugin - class if using custom plugins. - """ - try: - # Import the given module that contains the plugin. - if not module: - module = sleekxmpp.plugins - module = __import__("%s.%s" % (module.__name__, plugin), - globals(), locals(), [plugin]) - if isinstance(module, str): - # We probably want to load a module from outside - # the sleekxmpp package, so leave out the globals(). - module = __import__(module, fromlist=[plugin]) - - # Load the plugin class from the module. - self.plugin[plugin] = getattr(module, plugin)(self, pconfig) - - # Let XEP implementing plugins have some extra logging info. - xep = '' - if hasattr(self.plugin[plugin], 'xep'): - xep = "(XEP-%s) " % self.plugin[plugin].xep - - desc = (xep, self.plugin[plugin].description) - log.debug("Loaded Plugin %s%s" % desc) - except: - log.exception("Unable to load plugin: %s", plugin) - - def register_plugins(self): - """ - Register and initialize all built-in plugins. - - Optionally, the list of plugins loaded may be limited to those - contained in self.plugin_whitelist. - - Plugin configurations stored in self.plugin_config will be used. - """ - if self.plugin_whitelist: - plugin_list = self.plugin_whitelist - else: - plugin_list = plugins.__all__ - - for plugin in plugin_list: - if plugin in plugins.__all__: - self.register_plugin(plugin, - self.plugin_config.get(plugin, {})) - else: - raise NameError("Plugin %s not in plugins.__all__." % plugin) - - # Resolve plugin inter-dependencies. - for plugin in self.plugin: - self.plugin[plugin].post_init() - - def __getitem__(self, key): - """ - Return a plugin given its name, if it has been registered. - """ - if key in self.plugin: - return self.plugin[key] - else: - log.warning("""Plugin "%s" is not loaded.""" % key) - return False - - def get(self, key, default): - """ - Return a plugin given its name, if it has been registered. - """ - return self.plugin.get(key, default) - - def Message(self, *args, **kwargs): - """Create a Message stanza associated with this stream.""" - return Message(self, *args, **kwargs) - - def Iq(self, *args, **kwargs): - """Create an Iq stanza associated with this stream.""" - return Iq(self, *args, **kwargs) - - def Presence(self, *args, **kwargs): - """Create a Presence stanza associated with this stream.""" - return Presence(self, *args, **kwargs) - - def make_iq(self, id=0, ifrom=None): - """ - Create a new Iq stanza with a given Id and from JID. - - Arguments: - id -- An ideally unique ID value for this stanza thread. - Defaults to 0. - ifrom -- The from JID to use for this stanza. - """ - return self.Iq()._set_stanza_values({'id': str(id), - 'from': ifrom}) - - def make_iq_get(self, queryxmlns=None): - """ - Create an Iq stanza of type 'get'. - - Optionally, a query element may be added. - - Arguments: - queryxmlns -- The namespace of the query to use. - """ - return self.Iq()._set_stanza_values({'type': 'get', - 'query': queryxmlns}) - - def make_iq_result(self, id): - """ - Create an Iq stanza of type 'result' with the given ID value. - - Arguments: - id -- An ideally unique ID value. May use self.new_id(). - """ - return self.Iq()._set_stanza_values({'id': id, - 'type': 'result'}) - - def make_iq_set(self, sub=None): - """ - Create an Iq stanza of type 'set'. - - Optionally, a substanza may be given to use as the - stanza's payload. - - Arguments: - sub -- A stanza or XML object to use as the Iq's payload. - """ - iq = self.Iq()._set_stanza_values({'type': 'set'}) - if sub != None: - iq.append(sub) - return iq - - def make_iq_error(self, id, type='cancel', - condition='feature-not-implemented', text=None): - """ - Create an Iq stanza of type 'error'. - - Arguments: - id -- An ideally unique ID value. May use self.new_id(). - type -- The type of the error, such as 'cancel' or 'modify'. - Defaults to 'cancel'. - condition -- The error condition. - Defaults to 'feature-not-implemented'. - text -- A message describing the cause of the error. - """ - iq = self.Iq()._set_stanza_values({'id': id}) - iq['error']._set_stanza_values({'type': type, - 'condition': condition, - 'text': text}) - return iq - - def make_iq_query(self, iq=None, xmlns=''): - """ - Create or modify an Iq stanza to use the given - query namespace. - - Arguments: - iq -- Optional Iq stanza to modify. A new - stanza is created otherwise. - xmlns -- The query's namespace. - """ - if not iq: - iq = self.Iq() - iq['query'] = xmlns - return iq - - def make_query_roster(self, iq=None): - """ - Create a roster query element. - - Arguments: - iq -- Optional Iq stanza to modify. A new stanza - is created otherwise. - """ - if iq: - iq['query'] = 'jabber:iq:roster' - return ET.Element("{jabber:iq:roster}query") - - def make_message(self, mto, mbody=None, msubject=None, mtype=None, - mhtml=None, mfrom=None, mnick=None): - """ - Create and initialize a new Message stanza. - - Arguments: - mto -- The recipient of the message. - mbody -- The main contents of the message. - msubject -- Optional subject for the message. - mtype -- The message's type, such as 'chat' or 'groupchat'. - mhtml -- Optional HTML body content. - mfrom -- The sender of the message. If sending from a client, - be aware that some servers require that the full JID - of the sender be used. - mnick -- Optional nickname of the sender. - """ - message = self.Message(sto=mto, stype=mtype, sfrom=mfrom) - message['body'] = mbody - message['subject'] = msubject - if mnick is not None: - message['nick'] = mnick - if mhtml is not None: - message['html']['body'] = mhtml - return message - - def make_presence(self, pshow=None, pstatus=None, ppriority=None, - pto=None, ptype=None, pfrom=None): - """ - Create and initialize a new Presence stanza. - - Arguments: - pshow -- The presence's show value. - pstatus -- The presence's status message. - ppriority -- This connections' priority. - pto -- The recipient of a directed presence. - ptype -- The type of presence, such as 'subscribe'. - pfrom -- The sender of the presence. - """ - presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto) - if pshow is not None: - presence['type'] = pshow - if pfrom is None: - presence['from'] = self.boundjid.full - presence['priority'] = ppriority - presence['status'] = pstatus - return presence - - def send_message(self, mto, mbody, msubject=None, mtype=None, - mhtml=None, mfrom=None, mnick=None): - """ - Create, initialize, and send a Message stanza. - - - """ - self.makeMessage(mto, mbody, msubject, mtype, - mhtml, mfrom, mnick).send() - - def send_presence(self, pshow=None, pstatus=None, ppriority=None, - pto=None, pfrom=None, ptype=None): - """ - Create, initialize, and send a Presence stanza. - - Arguments: - pshow -- The presence's show value. - pstatus -- The presence's status message. - ppriority -- This connections' priority. - pto -- The recipient of a directed presence. - ptype -- The type of presence, such as 'subscribe'. - pfrom -- The sender of the presence. - """ - self.makePresence(pshow, pstatus, ppriority, pto, - ptype=ptype, pfrom=pfrom).send() - # Unexpected errors may occur if - if not self.sentpresence: - self.event('sent_presence') - self.sentpresence = True - - def send_presence_subscription(self, pto, pfrom=None, - ptype='subscribe', pnick=None): - """ - Create, initialize, and send a Presence stanza of type 'subscribe'. - - Arguments: - pto -- The recipient of a directed presence. - pfrom -- The sender of the presence. - ptype -- The type of presence. Defaults to 'subscribe'. - pnick -- Nickname of the presence's sender. - """ - presence = self.makePresence(ptype=ptype, - pfrom=pfrom, - pto=self.getjidbare(pto)) - if pnick: - nick = ET.Element('{http://jabber.org/protocol/nick}nick') - nick.text = pnick - presence.append(nick) - presence.send() - - @property - def jid(self): - """ - Attribute accessor for bare jid - """ - log.warning("jid property deprecated. Use boundjid.bare") - return self.boundjid.bare - - @jid.setter - def jid(self, value): - log.warning("jid property deprecated. Use boundjid.bare") - self.boundjid.bare = value - - @property - def fulljid(self): - """ - Attribute accessor for full jid - """ - log.warning("fulljid property deprecated. Use boundjid.full") - return self.boundjid.full - - @fulljid.setter - def fulljid(self, value): - log.warning("fulljid property deprecated. Use boundjid.full") - self.boundjid.full = value - - @property - def resource(self): - """ - Attribute accessor for jid resource - """ - log.warning("resource property deprecated. Use boundjid.resource") - return self.boundjid.resource - - @resource.setter - def resource(self, value): - log.warning("fulljid property deprecated. Use boundjid.full") - self.boundjid.resource = value - - @property - def username(self): - """ - Attribute accessor for jid usernode - """ - log.warning("username property deprecated. Use boundjid.user") - return self.boundjid.user - - @username.setter - def username(self, value): - log.warning("username property deprecated. Use boundjid.user") - self.boundjid.user = value - - @property - def server(self): - """ - Attribute accessor for jid host - """ - log.warning("server property deprecated. Use boundjid.host") - return self.boundjid.server - - @server.setter - def server(self, value): - log.warning("server property deprecated. Use boundjid.host") - self.boundjid.server = value - - def set_jid(self, jid): - """Rip a JID apart and claim it as our own.""" - log.debug("setting jid to %s" % jid) - self.boundjid.full = jid - - def getjidresource(self, fulljid): - if '/' in fulljid: - return fulljid.split('/', 1)[-1] - else: - return '' - - def getjidbare(self, fulljid): - return fulljid.split('/', 1)[0] - - def _handle_disconnected(self, event): - """When disconnected, reset the roster""" - self.roster = {} - - def _handle_message(self, msg): - """Process incoming message stanzas.""" - self.event('message', msg) - - def _handle_presence(self, presence): - """ - Process incoming presence stanzas. - - Update the roster with presence information. - """ - self.event("presence_%s" % presence['type'], presence) - - # Check for changes in subscription state. - if presence['type'] in ('subscribe', 'subscribed', - 'unsubscribe', 'unsubscribed'): - self.event('changed_subscription', presence) - return - elif not presence['type'] in ('available', 'unavailable') and \ - not presence['type'] in presence.showtypes: - return - - # Strip the information from the stanza. - jid = presence['from'].bare - resource = presence['from'].resource - show = presence['type'] - status = presence['status'] - priority = presence['priority'] - - was_offline = False - got_online = False - old_roster = self.roster.get(jid, {}).get(resource, {}) - - # Create a new roster entry if needed. - if not jid in self.roster: - self.roster[jid] = {'groups': [], - 'name': '', - 'subscription': 'none', - 'presence': {}, - 'in_roster': False} - - # Alias to simplify some references. - connections = self.roster[jid]['presence'] - - # Determine if the user has just come online. - if not resource in connections: - if show == 'available' or show in presence.showtypes: - got_online = True - was_offline = True - connections[resource] = {} - - if connections[resource].get('show', 'unavailable') == 'unavailable': - was_offline = True - - # Update the roster's state for this JID's resource. - connections[resource] = {'show': show, - 'status': status, - 'priority': priority} - - name = self.roster[jid].get('name', '') - - # Remove unneeded state information after a resource - # disconnects. Determine if this was the last connection - # for the JID. - if show == 'unavailable': - log.debug("%s %s got offline" % (jid, resource)) - del connections[resource] - - if not connections and not self.roster[jid]['in_roster']: - del self.roster[jid] - if not was_offline: - self.event("got_offline", presence) - else: - return False - - name = '(%s) ' % name if name else '' - - # Presence state has changed. - self.event("changed_status", presence) - if got_online: - self.event("got_online", presence) - log.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, - show, status)) - - def _handle_subscribe(self, presence): - """ - Automatically managage subscription requests. - - Subscription behavior is controlled by the settings - self.auto_authorize and self.auto_subscribe. - - auto_auth auto_sub Result: - True True Create bi-directional subsriptions. - True False Create only directed subscriptions. - False * Decline all subscriptions. - None * Disable automatic handling and use - a custom handler. - """ - presence.reply() - presence['to'] = presence['to'].bare - - # We are using trinary logic, so conditions have to be - # more explicit than usual. - if self.auto_authorize == True: - presence['type'] = 'subscribed' - presence.send() - if self.auto_subscribe: - presence['type'] = 'subscribe' - presence.send() - elif self.auto_authorize == False: - presence['type'] = 'unsubscribed' - presence.send() - -# Restore the old, lowercased name for backwards compatibility. -basexmpp = BaseXMPP |
