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