comparison sat/plugins/plugin_xep_0047.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 69e4716d6268
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin for managing gateways (xep-0047) 4 # SAT plugin for managing gateways (xep-0047)
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
30 from twisted.internet import defer 30 from twisted.internet import defer
31 from twisted.python import failure 31 from twisted.python import failure
32 32
33 from wokkel import disco, iwokkel 33 from wokkel import disco, iwokkel
34 34
35 from zope.interface import implements 35 from zope.interface import implementer
36 36
37 import base64 37 import base64
38 38
39 try: 39 try:
40 from twisted.words.protocols.xmlstream import XMPPHandler 40 from twisted.words.protocols.xmlstream import XMPPHandler
82 82
83 @param sid(unicode): session id of client.xep_0047_current_stream 83 @param sid(unicode): session id of client.xep_0047_current_stream
84 @param client: %(doc_client)s 84 @param client: %(doc_client)s
85 """ 85 """
86 log.info( 86 log.info(
87 u"In-Band Bytestream: TimeOut reached for id {sid} [{profile}]".format( 87 "In-Band Bytestream: TimeOut reached for id {sid} [{profile}]".format(
88 sid=sid, profile=client.profile 88 sid=sid, profile=client.profile
89 ) 89 )
90 ) 90 )
91 self._killSession(sid, client, "TIMEOUT") 91 self._killSession(sid, client, "TIMEOUT")
92 92
99 else, will be used to call failure_cb 99 else, will be used to call failure_cb
100 """ 100 """
101 try: 101 try:
102 session = client.xep_0047_current_stream[sid] 102 session = client.xep_0047_current_stream[sid]
103 except KeyError: 103 except KeyError:
104 log.warning(u"kill id called on a non existant id") 104 log.warning("kill id called on a non existant id")
105 return 105 return
106 106
107 try: 107 try:
108 observer_cb = session["observer_cb"] 108 observer_cb = session["observer_cb"]
109 except KeyError: 109 except KeyError:
139 @param to_jid(jid.JId): jid of the other peer 139 @param to_jid(jid.JId): jid of the other peer
140 @param sid(unicode): session id 140 @param sid(unicode): session id
141 @return (dict): session data 141 @return (dict): session data
142 """ 142 """
143 if sid in client.xep_0047_current_stream: 143 if sid in client.xep_0047_current_stream:
144 raise exceptions.ConflictError(u"A session with this id already exists !") 144 raise exceptions.ConflictError("A session with this id already exists !")
145 session_data = client.xep_0047_current_stream[sid] = { 145 session_data = client.xep_0047_current_stream[sid] = {
146 "id": sid, 146 "id": sid,
147 DEFER_KEY: defer.Deferred(), 147 DEFER_KEY: defer.Deferred(),
148 "local_jid": local_jid, 148 "local_jid": local_jid,
149 "to": to_jid, 149 "to": to_jid,
157 def _onIBBOpen(self, iq_elt, client): 157 def _onIBBOpen(self, iq_elt, client):
158 """"Called when an IBB <open> element is received 158 """"Called when an IBB <open> element is received
159 159
160 @param iq_elt(domish.Element): the whole <iq> stanza 160 @param iq_elt(domish.Element): the whole <iq> stanza
161 """ 161 """
162 log.debug(_(u"IBB stream opening")) 162 log.debug(_("IBB stream opening"))
163 iq_elt.handled = True 163 iq_elt.handled = True
164 open_elt = iq_elt.elements(NS_IBB, "open").next() 164 open_elt = next(iq_elt.elements(NS_IBB, "open"))
165 block_size = open_elt.getAttribute("block-size") 165 block_size = open_elt.getAttribute("block-size")
166 sid = open_elt.getAttribute("sid") 166 sid = open_elt.getAttribute("sid")
167 stanza = open_elt.getAttribute("stanza", "iq") 167 stanza = open_elt.getAttribute("stanza", "iq")
168 if not sid or not block_size or int(block_size) > 65535: 168 if not sid or not block_size or int(block_size) > 65535:
169 return self._sendError("not-acceptable", sid or None, iq_elt, client) 169 return self._sendError("not-acceptable", sid or None, iq_elt, client)
170 if not sid in client.xep_0047_current_stream: 170 if not sid in client.xep_0047_current_stream:
171 log.warning(_(u"Ignoring unexpected IBB transfer: %s" % sid)) 171 log.warning(_("Ignoring unexpected IBB transfer: %s" % sid))
172 return self._sendError("not-acceptable", sid or None, iq_elt, client) 172 return self._sendError("not-acceptable", sid or None, iq_elt, client)
173 session_data = client.xep_0047_current_stream[sid] 173 session_data = client.xep_0047_current_stream[sid]
174 if session_data["to"] != jid.JID(iq_elt["from"]): 174 if session_data["to"] != jid.JID(iq_elt["from"]):
175 log.warning( 175 log.warning(
176 _("sended jid inconsistency (man in the middle attack attempt ?)") 176 _("sended jid inconsistency (man in the middle attack attempt ?)")
202 202
203 @param iq_elt(domish.Element): the whole <iq> stanza 203 @param iq_elt(domish.Element): the whole <iq> stanza
204 """ 204 """
205 iq_elt.handled = True 205 iq_elt.handled = True
206 log.debug(_("IBB stream closing")) 206 log.debug(_("IBB stream closing"))
207 close_elt = iq_elt.elements(NS_IBB, "close").next() 207 close_elt = next(iq_elt.elements(NS_IBB, "close"))
208 # XXX: this observer is only triggered on valid sid, so we don't need to check it 208 # XXX: this observer is only triggered on valid sid, so we don't need to check it
209 sid = close_elt["sid"] 209 sid = close_elt["sid"]
210 210
211 iq_result_elt = xmlstream.toResponse(iq_elt, "result") 211 iq_result_elt = xmlstream.toResponse(iq_elt, "result")
212 client.send(iq_result_elt) 212 client.send(iq_result_elt)
217 217
218 Manage the data elelement (check validity and write to the stream_object) 218 Manage the data elelement (check validity and write to the stream_object)
219 @param element(domish.Element): <iq> or <message> stanza 219 @param element(domish.Element): <iq> or <message> stanza
220 """ 220 """
221 element.handled = True 221 element.handled = True
222 data_elt = element.elements(NS_IBB, "data").next() 222 data_elt = next(element.elements(NS_IBB, "data"))
223 sid = data_elt["sid"] 223 sid = data_elt["sid"]
224 224
225 try: 225 try:
226 session_data = client.xep_0047_current_stream[sid] 226 session_data = client.xep_0047_current_stream[sid]
227 except KeyError: 227 except KeyError:
228 log.warning(_(u"Received data for an unknown session id")) 228 log.warning(_("Received data for an unknown session id"))
229 return self._sendError("item-not-found", None, element, client) 229 return self._sendError("item-not-found", None, element, client)
230 230
231 from_jid = session_data["to"] 231 from_jid = session_data["to"]
232 stream_object = session_data["stream_object"] 232 stream_object = session_data["stream_object"]
233 233
234 if from_jid.full() != element["from"]: 234 if from_jid.full() != element["from"]:
235 log.warning( 235 log.warning(
236 _( 236 _(
237 u"sended jid inconsistency (man in the middle attack attempt ?)\ninitial={initial}\ngiven={given}" 237 "sended jid inconsistency (man in the middle attack attempt ?)\ninitial={initial}\ngiven={given}"
238 ).format(initial=from_jid, given=element["from"]) 238 ).format(initial=from_jid, given=element["from"])
239 ) 239 )
240 if element.name == "iq": 240 if element.name == "iq":
241 self._sendError("not-acceptable", sid, element, client) 241 self._sendError("not-acceptable", sid, element, client)
242 return 242 return
243 243
244 session_data["seq"] = (session_data["seq"] + 1) % 65535 244 session_data["seq"] = (session_data["seq"] + 1) % 65535
245 if int(data_elt.getAttribute("seq", -1)) != session_data["seq"]: 245 if int(data_elt.getAttribute("seq", -1)) != session_data["seq"]:
246 log.warning(_(u"Sequence error")) 246 log.warning(_("Sequence error"))
247 if element.name == "iq": 247 if element.name == "iq":
248 reason = "not-acceptable" 248 reason = "not-acceptable"
249 self._sendError(reason, sid, element, client) 249 self._sendError(reason, sid, element, client)
250 self.terminateStream(session_data, client, reason) 250 self.terminateStream(session_data, client, reason)
251 return 251 return
256 # we can now decode the data 256 # we can now decode the data
257 try: 257 try:
258 stream_object.write(base64.b64decode(str(data_elt))) 258 stream_object.write(base64.b64decode(str(data_elt)))
259 except TypeError: 259 except TypeError:
260 # The base64 data is invalid 260 # The base64 data is invalid
261 log.warning(_(u"Invalid base64 data")) 261 log.warning(_("Invalid base64 data"))
262 if element.name == "iq": 262 if element.name == "iq":
263 self._sendError("not-acceptable", sid, element, client) 263 self._sendError("not-acceptable", sid, element, client)
264 self.terminateStream(session_data, client, reason) 264 self.terminateStream(session_data, client, reason)
265 return 265 return
266 266
277 @param iq_elt(domish.Element): full <iq> stanza 277 @param iq_elt(domish.Element): full <iq> stanza
278 @param client: %(doc_client)s 278 @param client: %(doc_client)s
279 """ 279 """
280 iq_elt = error.StanzaError(error_condition).toResponse(iq_elt) 280 iq_elt = error.StanzaError(error_condition).toResponse(iq_elt)
281 log.warning( 281 log.warning(
282 u"Error while managing in-band bytestream session, cancelling: {}".format( 282 "Error while managing in-band bytestream session, cancelling: {}".format(
283 error_condition 283 error_condition
284 ) 284 )
285 ) 285 )
286 if sid is not None: 286 if sid is not None:
287 self._killSession(sid, client, error_condition) 287 self._killSession(sid, client, error_condition)
332 next_iq_elt = client.IQ() 332 next_iq_elt = client.IQ()
333 next_iq_elt["from"] = session_data["local_jid"].full() 333 next_iq_elt["from"] = session_data["local_jid"].full()
334 next_iq_elt["to"] = session_data["to"].full() 334 next_iq_elt["to"] = session_data["to"].full()
335 data_elt = next_iq_elt.addElement((NS_IBB, "data")) 335 data_elt = next_iq_elt.addElement((NS_IBB, "data"))
336 seq = session_data["seq"] = (session_data["seq"] + 1) % 65535 336 seq = session_data["seq"] = (session_data["seq"] + 1) % 65535
337 data_elt["seq"] = unicode(seq) 337 data_elt["seq"] = str(seq)
338 data_elt["sid"] = session_data["id"] 338 data_elt["sid"] = session_data["id"]
339 data_elt.addContent(base64.b64encode(buffer_)) 339 data_elt.addContent(base64.b64encode(buffer_))
340 args = [session_data, client] 340 args = [session_data, client]
341 d = next_iq_elt.send() 341 d = next_iq_elt.send()
342 d.addCallbacks(self._IQDataStreamCb, self._IQDataStreamEb, args, None, args) 342 d.addCallbacks(self._IQDataStreamCb, self._IQDataStreamEb, args, None, args)
343 else: 343 else:
344 self.terminateStream(session_data, client) 344 self.terminateStream(session_data, client)
345 345
346 def _IQDataStreamEb(self, failure, session_data, client): 346 def _IQDataStreamEb(self, failure, session_data, client):
347 if failure.check(error.StanzaError): 347 if failure.check(error.StanzaError):
348 log.warning(u"IBB transfer failed: {}".format(failure.value)) 348 log.warning("IBB transfer failed: {}".format(failure.value))
349 else: 349 else:
350 log.error(u"IBB transfer failed: {}".format(failure.value)) 350 log.error("IBB transfer failed: {}".format(failure.value))
351 self.terminateStream(session_data, client, "IQ_ERROR") 351 self.terminateStream(session_data, client, "IQ_ERROR")
352 352
353 def terminateStream(self, session_data, client, failure_reason=None): 353 def terminateStream(self, session_data, client, failure_reason=None):
354 """Terminate the stream session 354 """Terminate the stream session
355 355
364 close_elt["sid"] = session_data["id"] 364 close_elt["sid"] = session_data["id"]
365 iq_elt.send() 365 iq_elt.send()
366 self._killSession(session_data["id"], client, failure_reason) 366 self._killSession(session_data["id"], client, failure_reason)
367 367
368 368
369 @implementer(iwokkel.IDisco)
369 class XEP_0047_handler(XMPPHandler): 370 class XEP_0047_handler(XMPPHandler):
370 implements(iwokkel.IDisco)
371 371
372 def __init__(self, parent): 372 def __init__(self, parent):
373 self.plugin_parent = parent 373 self.plugin_parent = parent
374 374
375 def connectionInitialized(self): 375 def connectionInitialized(self):