4375ac7d8b
Stanza objects now accept the use of underscored names. The CamelCase versions are still available for backwards compatibility, but are discouraged. The property stanza.values now maps to the old getStanzaValues and setStanzaValues, in addition to _set_stanza_values and _get_stanza_values.
136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
"""
|
|
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 = "<stream:stream %s %s to='%s'>" % (
|
|
'xmlns="jabber:component:accept"',
|
|
'xmlns:stream="%s"' % self.stream_ns,
|
|
jid)
|
|
self.stream_footer = "</stream:stream>"
|
|
self.server_host = host
|
|
self.server_port = port
|
|
self.set_jid(jid)
|
|
self.secret = secret
|
|
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")
|