From b0a2a305028bf284fc5dcf7e1a696d85787f128f Mon Sep 17 00:00:00 2001 From: Thibaut Horel Date: Mon, 8 Nov 2010 00:59:14 +0100 Subject: Add the sleekxmpp library (will be added as a submodule later) --- sleekxmpp/componentxmpp.py | 138 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 sleekxmpp/componentxmpp.py (limited to 'sleekxmpp/componentxmpp.py') diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py new file mode 100644 index 0000000..3fcb88d --- /dev/null +++ b/sleekxmpp/componentxmpp.py @@ -0,0 +1,138 @@ +""" + 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 absolute_import + +import logging +import base64 +import sys +import hashlib + +from sleekxmpp import plugins +from sleekxmpp import stanza +from sleekxmpp.basexmpp import BaseXMPP, SRV_SUPPORT +from sleekxmpp.xmlstream import XMLStream, RestartStream +from sleekxmpp.xmlstream import StanzaBase, ET +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * + + +class ComponentXMPP(BaseXMPP): + + """ + SleekXMPP's basic XMPP server component. + + Use only for good, not for evil. + + Methods: + connect -- Overrides XMLStream.connect. + incoming_filter -- Overrides XMLStream.incoming_filter. + start_stream_handler -- Overrides XMLStream.start_stream_handler. + """ + + def __init__(self, jid, secret, host, port, + plugin_config={}, plugin_whitelist=[], use_jc_ns=False): + """ + Arguments: + jid -- The JID of the component. + secret -- The secret or password for the component. + host -- The server accepting the component. + port -- The port used to connect to the server. + plugin_config -- A dictionary of plugin configurations. + plugin_whitelist -- A list of desired plugins to load + when using register_plugins. + use_js_ns -- Indicates if the 'jabber:client' namespace + should be used instead of the standard + 'jabber:component:accept' namespace. + Defaults to False. + """ + if use_jc_ns: + default_ns = 'jabber:client' + else: + default_ns = 'jabber:component:accept' + BaseXMPP.__init__(self, default_ns) + + self.auto_authorize = None + self.stream_header = "" % ( + 'xmlns="jabber:component:accept"', + 'xmlns:stream="%s"' % self.stream_ns, + jid) + self.stream_footer = "" + self.server_host = host + self.server_port = port + self.set_jid(jid) + self.secret = secret + self.plugin_config = plugin_config + self.plugin_whitelist = plugin_whitelist + self.is_component = True + + self.register_handler( + Callback('Handshake', + MatchXPath('{jabber:component:accept}handshake'), + self._handle_handshake)) + + def connect(self): + """ + Connect to the server. + + Overrides XMLStream.connect. + """ + logging.debug("Connecting to %s:%s" % (self.server_host, + self.server_port)) + return XMLStream.connect(self, self.server_host, + self.server_port) + + def incoming_filter(self, xml): + """ + Pre-process incoming XML stanzas by converting any 'jabber:client' + namespaced elements to the component's default namespace. + + Overrides XMLStream.incoming_filter. + + Arguments: + xml -- The XML stanza to pre-process. + """ + if xml.tag.startswith('{jabber:client}'): + xml.tag = xml.tag.replace('jabber:client', self.default_ns) + + # The incoming_filter call is only made on top level stanza + # elements. So we manually continue filtering on sub-elements. + for sub in xml: + self.incoming_filter(sub) + + return xml + + def start_stream_handler(self, xml): + """ + Once the streams are established, attempt to handshake + with the server to be accepted as a component. + + Overrides XMLStream.start_stream_handler. + + Arguments: + xml -- The incoming stream's root element. + """ + # Construct a hash of the stream ID and the component secret. + sid = xml.get('id', '') + pre_hash = '%s%s' % (sid, self.secret) + if sys.version_info >= (3, 0): + # Handle Unicode byte encoding in Python 3. + pre_hash = bytes(pre_hash, 'utf-8') + + handshake = ET.Element('{jabber:component:accept}handshake') + handshake.text = hashlib.sha1(pre_hash).hexdigest().lower() + self.send_xml(handshake) + + def _handle_handshake(self, xml): + """ + The handshake has been accepted. + + Arguments: + xml -- The reply handshake stanza. + """ + self.event("session_start") -- cgit v1.2.3-70-g09d2