Cleanup how events are run, they are always direct by definition now
[slixmpp.git] / slixmpp / componentxmpp.py
blob52829dfa8e265f8b2b14bbd8f89c96fc38ac03cf
1 # -*- coding: utf-8 -*-
2 """
3 slixmpp.clientxmpp
4 ~~~~~~~~~~~~~~~~~~~~
6 This module provides XMPP functionality that
7 is specific to external server component connections.
9 Part of Slixmpp: The Slick XMPP Library
11 :copyright: (c) 2011 Nathanael C. Fritz
12 :license: MIT, see LICENSE for more details
13 """
15 from __future__ import absolute_import
17 import logging
18 import sys
19 import hashlib
21 from slixmpp.basexmpp import BaseXMPP
22 from slixmpp.xmlstream import XMLStream
23 from slixmpp.xmlstream import ET
24 from slixmpp.xmlstream.matcher import MatchXPath
25 from slixmpp.xmlstream.handler import Callback
28 log = logging.getLogger(__name__)
31 class ComponentXMPP(BaseXMPP):
33 """
34 Slixmpp's basic XMPP server component.
36 Use only for good, not for evil.
38 :param jid: The JID of the component.
39 :param secret: The secret or password for the component.
40 :param host: The server accepting the component.
41 :param port: The port used to connect to the server.
42 :param plugin_config: A dictionary of plugin configurations.
43 :param plugin_whitelist: A list of approved plugins that
44 will be loaded when calling
45 :meth:`~slixmpp.basexmpp.BaseXMPP.register_plugins()`.
46 :param use_jc_ns: Indicates if the ``'jabber:client'`` namespace
47 should be used instead of the standard
48 ``'jabber:component:accept'`` namespace.
49 Defaults to ``False``.
50 """
52 def __init__(self, jid, secret, host=None, port=None,
53 plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
54 if use_jc_ns:
55 default_ns = 'jabber:client'
56 else:
57 default_ns = 'jabber:component:accept'
58 BaseXMPP.__init__(self, jid, default_ns)
60 self.auto_authorize = None
61 self.stream_header = "<stream:stream %s %s to='%s'>" % (
62 'xmlns="jabber:component:accept"',
63 'xmlns:stream="%s"' % self.stream_ns,
64 jid)
65 self.stream_footer = "</stream:stream>"
66 self.server_host = host
67 self.server_port = port
68 self.secret = secret
70 self.plugin_config = plugin_config
71 self.plugin_whitelist = plugin_whitelist
72 self.is_component = True
74 self.register_handler(
75 Callback('Handshake',
76 MatchXPath('{jabber:component:accept}handshake'),
77 self._handle_handshake))
78 self.add_event_handler('presence_probe',
79 self._handle_probe)
81 def connect(self, host=None, port=None, use_ssl=False,
82 use_tls=False, reattempt=True):
83 """Connect to the server.
85 Setting ``reattempt`` to ``True`` will cause connection attempts to
86 be made every second until a successful connection is established.
88 :param host: The name of the desired server for the connection.
89 Defaults to :attr:`server_host`.
90 :param port: Port to connect to on the server.
91 Defauts to :attr:`server_port`.
92 :param use_ssl: Flag indicating if SSL should be used by connecting
93 directly to a port using SSL.
94 :param use_tls: Flag indicating if TLS should be used, allowing for
95 connecting to a port without using SSL immediately and
96 later upgrading the connection.
97 :param reattempt: Flag indicating if the socket should reconnect
98 after disconnections.
99 """
100 if host is None:
101 host = self.server_host
102 if port is None:
103 port = self.server_port
105 self.server_name = self.boundjid.host
107 if use_tls:
108 log.info("XEP-0114 components can not use TLS")
110 log.debug("Connecting to %s:%s", host, port)
111 return XMLStream.connect(self, host=host, port=port,
112 use_ssl=use_ssl,
113 use_tls=False,
114 reattempt=reattempt)
116 def incoming_filter(self, xml):
118 Pre-process incoming XML stanzas by converting any
119 ``'jabber:client'`` namespaced elements to the component's
120 default namespace.
122 :param xml: The XML stanza to pre-process.
124 if xml.tag.startswith('{jabber:client}'):
125 xml.tag = xml.tag.replace('jabber:client', self.default_ns)
126 return xml
128 def start_stream_handler(self, xml):
130 Once the streams are established, attempt to handshake
131 with the server to be accepted as a component.
133 :param xml: The incoming stream's root element.
135 BaseXMPP.start_stream_handler(self, xml)
137 # Construct a hash of the stream ID and the component secret.
138 sid = xml.get('id', '')
139 pre_hash = '%s%s' % (sid, self.secret)
140 if sys.version_info >= (3, 0):
141 # Handle Unicode byte encoding in Python 3.
142 pre_hash = bytes(pre_hash, 'utf-8')
144 handshake = ET.Element('{jabber:component:accept}handshake')
145 handshake.text = hashlib.sha1(pre_hash).hexdigest().lower()
146 self.send_xml(handshake)
148 def _handle_handshake(self, xml):
149 """The handshake has been accepted.
151 :param xml: The reply handshake stanza.
153 self.session_bind_event.set()
154 self.session_started_event.set()
155 self.event('session_bind', self.boundjid)
156 self.event('session_start')
158 def _handle_probe(self, pres):
159 self.roster[pres['to']][pres['from']].handle_probe(pres)