1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """Component (jabber:component:accept) stream handling.
19
20 Normative reference:
21 - `JEP 114 <http://www.jabber.org/jeps/jep-0114.html>`__
22 """
23
24 __revision__="$Id: componentstream.py 703 2010-04-03 17:45:43Z jajcus $"
25 __docformat__="restructuredtext en"
26
27 import hashlib
28
29 import logging
30
31 from pyxmpp.stream import Stream
32 from pyxmpp.streambase import stanza_factory,HostMismatch
33 from pyxmpp.xmlextra import common_doc,common_root
34 from pyxmpp.utils import to_utf8
35 from pyxmpp.exceptions import StreamError,FatalStreamError,ComponentStreamError,FatalComponentStreamError
36
38 """Handles jabberd component (jabber:component:accept) connection stream.
39
40 :Ivariables:
41 - `server`: server to use.
42 - `port`: port number to use.
43 - `secret`: authentication secret.
44 :Types:
45 - `server`: `unicode`
46 - `port`: `int`
47 - `secret`: `unicode`"""
48
49 - def __init__(self, jid, secret, server, port, keepalive = 0, owner = None):
50 """Initialize a `ComponentStream` object.
51
52 :Parameters:
53 - `jid`: JID of the component.
54 - `secret`: authentication secret.
55 - `server`: server address.
56 - `port`: TCP port number on the server.
57 - `keepalive`: keepalive interval. 0 to disable.
58 - `owner`: `Client`, `Component` or similar object "owning" this stream.
59 """
60 Stream.__init__(self, "jabber:component:accept",
61 sasl_mechanisms = [],
62 tls_settings = None,
63 keepalive = keepalive,
64 owner = owner)
65 self.server=server
66 self.port=port
67 self.me=jid
68 self.secret=secret
69 self.process_all_stanzas=1
70 self.__logger=logging.getLogger("pyxmpp.jabberd.ComponentStream")
71
73 """Reset `ComponentStream` object state, making the object ready to
74 handle new connections."""
75 Stream._reset(self)
76
77 - def connect(self,server=None,port=None):
78 """Establish a client connection to a server.
79
80 [component only]
81
82 :Parameters:
83 - `server`: name or address of the server to use. If not given
84 then use the one specified when creating the object.
85 - `port`: port number of the server to use. If not given then use
86 the one specified when creating the object.
87
88 :Types:
89 - `server`: `unicode`
90 - `port`: `int`"""
91 self.lock.acquire()
92 try:
93 self._connect(server,port)
94 finally:
95 self.lock.release()
96
97 - def _connect(self,server=None,port=None):
98 """Same as `ComponentStream.connect` but assume `self.lock` is acquired."""
99 if self.me.node or self.me.resource:
100 raise Value, "Component JID may have only domain defined"
101 if not server:
102 server=self.server
103 if not port:
104 port=self.port
105 if not server or not port:
106 raise ValueError, "Server or port not given"
107 Stream._connect(self,server,port,None,self.me)
108
110 """Accept an incoming component connection.
111
112 [server only]
113
114 :Parameters:
115 - `sock`: a listening socket."""
116 Stream.accept(self,sock,None)
117
119 """Process <stream:stream> (stream start) tag received from peer.
120
121 Call `Stream.stream_start`, but ignore any `HostMismatch` error.
122
123 :Parameters:
124 - `doc`: document created by the parser"""
125 try:
126 Stream.stream_start(self,doc)
127 except HostMismatch:
128 pass
129
130 - def _post_connect(self):
131 """Initialize authentication when the connection is established
132 and we are the initiator."""
133 if self.initiator:
134 self._auth()
135
137 """Compute the authentication handshake value.
138
139 :return: the computed hash value.
140 :returntype: `str`"""
141 return hashlib.sha1(to_utf8(self.stream_id)+to_utf8(self.secret)).hexdigest()
142
144 """Authenticate on the server.
145
146 [component only]"""
147 if self.authenticated:
148 self.__logger.debug("_auth: already authenticated")
149 return
150 self.__logger.debug("doing handshake...")
151 hash_value=self._compute_handshake()
152 n=common_root.newTextChild(None,"handshake",hash_value)
153 self._write_node(n)
154 n.unlinkNode()
155 n.freeNode()
156 self.__logger.debug("handshake hash sent.")
157
159 """Process first level element of the stream.
160
161 Handle component handshake (authentication) element, and
162 treat elements in "jabber:component:accept", "jabber:client"
163 and "jabber:server" equally (pass to `self.process_stanza`).
164 All other elements are passed to `Stream._process_node`.
165
166 :Parameters:
167 - `node`: XML node describing the element
168 """
169 ns=node.ns()
170 if ns:
171 ns_uri=node.ns().getContent()
172 if (not ns or ns_uri=="jabber:component:accept") and node.name=="handshake":
173 if self.initiator and not self.authenticated:
174 self.authenticated=1
175 self.state_change("authenticated",self.me)
176 self._post_auth()
177 return
178 elif not self.authenticated and node.getContent()==self._compute_handshake():
179 self.peer=self.me
180 n=common_doc.newChild(None,"handshake",None)
181 self._write_node(n)
182 n.unlinkNode()
183 n.freeNode()
184 self.peer_authenticated=1
185 self.state_change("authenticated",self.peer)
186 self._post_auth()
187 return
188 else:
189 self._send_stream_error("not-authorized")
190 raise FatalComponentStreamError,"Hanshake error."
191
192 if ns_uri in ("jabber:component:accept","jabber:client","jabber:server"):
193 stanza=stanza_factory(node)
194 self.lock.release()
195 try:
196 self.process_stanza(stanza)
197 finally:
198 self.lock.acquire()
199 stanza.free()
200 return
201 return Stream._process_node(self,node)
202
203
204