Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0234.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 | fee60f17ebac |
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 Jingle File Transfer (XEP-0234) | 4 # SAT plugin for Jingle File Transfer (XEP-0234) |
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 |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 | 23 |
24 log = getLogger(__name__) | 24 log = getLogger(__name__) |
25 from sat.core import exceptions | 25 from sat.core import exceptions |
26 from wokkel import disco, iwokkel | 26 from wokkel import disco, iwokkel |
27 from zope.interface import implements | 27 from zope.interface import implementer |
28 from sat.tools import utils | 28 from sat.tools import utils |
29 from sat.tools import stream | 29 from sat.tools import stream |
30 from sat.tools.common import date_utils | 30 from sat.tools.common import date_utils |
31 import os.path | 31 import os.path |
32 from twisted.words.xish import domish | 32 from twisted.words.xish import domish |
53 C.PI_MAIN: "XEP_0234", | 53 C.PI_MAIN: "XEP_0234", |
54 C.PI_HANDLER: "yes", | 54 C.PI_HANDLER: "yes", |
55 C.PI_DESCRIPTION: _("""Implementation of Jingle File Transfer"""), | 55 C.PI_DESCRIPTION: _("""Implementation of Jingle File Transfer"""), |
56 } | 56 } |
57 | 57 |
58 EXTRA_ALLOWED = {u"path", u"namespace", u"file_desc", u"file_hash"} | 58 EXTRA_ALLOWED = {"path", "namespace", "file_desc", "file_hash"} |
59 Range = namedtuple("Range", ("offset", "length")) | 59 Range = namedtuple("Range", ("offset", "length")) |
60 | 60 |
61 | 61 |
62 class XEP_0234(object): | 62 class XEP_0234(object): |
63 # TODO: assure everything is closed when file is sent or session terminate is received | 63 # TODO: assure everything is closed when file is sent or session terminate is received |
70 host.registerNamespace("jingle-ft", NS_JINGLE_FT) | 70 host.registerNamespace("jingle-ft", NS_JINGLE_FT) |
71 self._j = host.plugins["XEP-0166"] # shortcut to access jingle | 71 self._j = host.plugins["XEP-0166"] # shortcut to access jingle |
72 self._j.registerApplication(NS_JINGLE_FT, self) | 72 self._j.registerApplication(NS_JINGLE_FT, self) |
73 self._f = host.plugins["FILE"] | 73 self._f = host.plugins["FILE"] |
74 self._f.register( | 74 self._f.register( |
75 NS_JINGLE_FT, self.fileJingleSend, priority=10000, method_name=u"Jingle" | 75 NS_JINGLE_FT, self.fileJingleSend, priority=10000, method_name="Jingle" |
76 ) | 76 ) |
77 self._hash = self.host.plugins["XEP-0300"] | 77 self._hash = self.host.plugins["XEP-0300"] |
78 host.bridge.addMethod( | 78 host.bridge.addMethod( |
79 "fileJingleSend", | 79 "fileJingleSend", |
80 ".plugin", | 80 ".plugin", |
81 in_sign="ssssa{ss}s", | 81 in_sign="ssssa{ss}s", |
82 out_sign="", | 82 out_sign="", |
83 method=self._fileJingleSend, | 83 method=self._fileJingleSend, |
84 async=True, | 84 async_=True, |
85 ) | 85 ) |
86 host.bridge.addMethod( | 86 host.bridge.addMethod( |
87 "fileJingleRequest", | 87 "fileJingleRequest", |
88 ".plugin", | 88 ".plugin", |
89 in_sign="sssssa{ss}s", | 89 in_sign="sssssa{ss}s", |
90 out_sign="s", | 90 out_sign="s", |
91 method=self._fileJingleRequest, | 91 method=self._fileJingleRequest, |
92 async=True, | 92 async_=True, |
93 ) | 93 ) |
94 | 94 |
95 def getHandler(self, client): | 95 def getHandler(self, client): |
96 return XEP_0234_handler() | 96 return XEP_0234_handler() |
97 | 97 |
100 | 100 |
101 @param session(dict): jingle session | 101 @param session(dict): jingle session |
102 @param content_name(unicode): name of the content | 102 @param content_name(unicode): name of the content |
103 @return (unicode): unique progress id | 103 @return (unicode): unique progress id |
104 """ | 104 """ |
105 return u"{}_{}".format(session["id"], content_name) | 105 return "{}_{}".format(session["id"], content_name) |
106 | 106 |
107 # generic methods | 107 # generic methods |
108 | 108 |
109 def buildFileElement(self, name, file_hash=None, hash_algo=None, size=None, | 109 def buildFileElement(self, name, file_hash=None, hash_algo=None, size=None, |
110 mime_type=None, desc=None, modified=None, transfer_range=None, path=None, | 110 mime_type=None, desc=None, modified=None, transfer_range=None, path=None, |
125 @param **kwargs: data for plugin extension (ignored by default) | 125 @param **kwargs: data for plugin extension (ignored by default) |
126 @return (domish.Element): generated element | 126 @return (domish.Element): generated element |
127 @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add | 127 @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add |
128 """ | 128 """ |
129 if file_elt is None: | 129 if file_elt is None: |
130 file_elt = domish.Element((NS_JINGLE_FT, u"file")) | 130 file_elt = domish.Element((NS_JINGLE_FT, "file")) |
131 for name, value in ( | 131 for name, value in ( |
132 (u"name", name), | 132 ("name", name), |
133 (u"size", size), | 133 ("size", size), |
134 ("media-type", mime_type), | 134 ("media-type", mime_type), |
135 (u"desc", desc), | 135 ("desc", desc), |
136 (u"path", path), | 136 ("path", path), |
137 (u"namespace", namespace), | 137 ("namespace", namespace), |
138 ): | 138 ): |
139 if value is not None: | 139 if value is not None: |
140 file_elt.addElement(name, content=unicode(value)) | 140 file_elt.addElement(name, content=str(value)) |
141 | 141 |
142 if modified is not None: | 142 if modified is not None: |
143 if isinstance(modified, int): | 143 if isinstance(modified, int): |
144 file_elt.addElement(u"date", utils.xmpp_date(modified or None)) | 144 file_elt.addElement("date", utils.xmpp_date(modified or None)) |
145 else: | 145 else: |
146 file_elt.addElement(u"date", modified) | 146 file_elt.addElement("date", modified) |
147 elif "created" in kwargs: | 147 elif "created" in kwargs: |
148 file_elt.addElement(u"date", utils.xmpp_date(kwargs.pop("created"))) | 148 file_elt.addElement("date", utils.xmpp_date(kwargs.pop("created"))) |
149 | 149 |
150 range_elt = file_elt.addElement(u"range") | 150 range_elt = file_elt.addElement("range") |
151 if transfer_range is not None: | 151 if transfer_range is not None: |
152 if transfer_range.offset is not None: | 152 if transfer_range.offset is not None: |
153 range_elt[u"offset"] = transfer_range.offset | 153 range_elt["offset"] = transfer_range.offset |
154 if transfer_range.length is not None: | 154 if transfer_range.length is not None: |
155 range_elt[u"length"] = transfer_range.length | 155 range_elt["length"] = transfer_range.length |
156 if file_hash is not None: | 156 if file_hash is not None: |
157 if not file_hash: | 157 if not file_hash: |
158 file_elt.addChild(self._hash.buildHashUsedElt()) | 158 file_elt.addChild(self._hash.buildHashUsedElt()) |
159 else: | 159 else: |
160 file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo)) | 160 file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo)) |
161 elif hash_algo is not None: | 161 elif hash_algo is not None: |
162 file_elt.addChild(self._hash.buildHashUsedElt(hash_algo)) | 162 file_elt.addChild(self._hash.buildHashUsedElt(hash_algo)) |
163 self.host.trigger.point(u"XEP-0234_buildFileElement", file_elt, extra_args=kwargs) | 163 self.host.trigger.point("XEP-0234_buildFileElement", file_elt, extra_args=kwargs) |
164 if kwargs: | 164 if kwargs: |
165 for kw in kwargs: | 165 for kw in kwargs: |
166 log.debug("ignored keyword: {}".format(kw)) | 166 log.debug("ignored keyword: {}".format(kw)) |
167 return file_elt | 167 return file_elt |
168 | 168 |
202 @raise exceptions.DataError: if file_elt uri is not NS_JINGLE_FT | 202 @raise exceptions.DataError: if file_elt uri is not NS_JINGLE_FT |
203 """ | 203 """ |
204 if parent_elt is not None: | 204 if parent_elt is not None: |
205 if file_elt is not None: | 205 if file_elt is not None: |
206 raise exceptions.InternalError( | 206 raise exceptions.InternalError( |
207 u"file_elt must be None if parent_elt is set" | 207 "file_elt must be None if parent_elt is set" |
208 ) | 208 ) |
209 try: | 209 try: |
210 file_elt = next(parent_elt.elements(NS_JINGLE_FT, u"file")) | 210 file_elt = next(parent_elt.elements(NS_JINGLE_FT, "file")) |
211 except StopIteration: | 211 except StopIteration: |
212 raise exceptions.NotFound() | 212 raise exceptions.NotFound() |
213 else: | 213 else: |
214 if not file_elt or file_elt.uri != NS_JINGLE_FT: | 214 if not file_elt or file_elt.uri != NS_JINGLE_FT: |
215 raise exceptions.DataError( | 215 raise exceptions.DataError( |
216 u"invalid <file> element: {stanza}".format(stanza=file_elt.toXml()) | 216 "invalid <file> element: {stanza}".format(stanza=file_elt.toXml()) |
217 ) | 217 ) |
218 | 218 |
219 if file_data is None: | 219 if file_data is None: |
220 file_data = {} | 220 file_data = {} |
221 | 221 |
222 for name in (u"name", u"desc", u"path", u"namespace"): | 222 for name in ("name", "desc", "path", "namespace"): |
223 try: | 223 try: |
224 file_data[name] = unicode(next(file_elt.elements(NS_JINGLE_FT, name))) | 224 file_data[name] = str(next(file_elt.elements(NS_JINGLE_FT, name))) |
225 except StopIteration: | 225 except StopIteration: |
226 pass | 226 pass |
227 | 227 |
228 name = file_data.get(u"name") | 228 name = file_data.get("name") |
229 if name == u"..": | 229 if name == "..": |
230 # we don't want to go to parent dir when joining to a path | 230 # we don't want to go to parent dir when joining to a path |
231 name = u"--" | 231 name = "--" |
232 file_data[u"name"] = name | 232 file_data["name"] = name |
233 elif name is not None and u"/" in name or u"\\" in name: | 233 elif name is not None and "/" in name or "\\" in name: |
234 file_data[u"name"] = regex.pathEscape(name) | 234 file_data["name"] = regex.pathEscape(name) |
235 | 235 |
236 try: | 236 try: |
237 file_data[u"mime_type"] = unicode( | 237 file_data["mime_type"] = str( |
238 next(file_elt.elements(NS_JINGLE_FT, u"media-type")) | 238 next(file_elt.elements(NS_JINGLE_FT, "media-type")) |
239 ) | 239 ) |
240 except StopIteration: | 240 except StopIteration: |
241 pass | 241 pass |
242 | 242 |
243 try: | 243 try: |
244 file_data[u"size"] = int( | 244 file_data["size"] = int( |
245 unicode(next(file_elt.elements(NS_JINGLE_FT, u"size"))) | 245 str(next(file_elt.elements(NS_JINGLE_FT, "size"))) |
246 ) | 246 ) |
247 except StopIteration: | 247 except StopIteration: |
248 pass | 248 pass |
249 | 249 |
250 try: | 250 try: |
251 file_data[u"modified"] = date_utils.date_parse( | 251 file_data["modified"] = date_utils.date_parse( |
252 next(file_elt.elements(NS_JINGLE_FT, u"date")) | 252 next(file_elt.elements(NS_JINGLE_FT, "date")) |
253 ) | 253 ) |
254 except StopIteration: | 254 except StopIteration: |
255 pass | 255 pass |
256 | 256 |
257 try: | 257 try: |
258 range_elt = file_elt.elements(NS_JINGLE_FT, u"range").next() | 258 range_elt = next(file_elt.elements(NS_JINGLE_FT, "range")) |
259 except StopIteration: | 259 except StopIteration: |
260 pass | 260 pass |
261 else: | 261 else: |
262 offset = range_elt.getAttribute("offset") | 262 offset = range_elt.getAttribute("offset") |
263 length = range_elt.getAttribute("length") | 263 length = range_elt.getAttribute("length") |
264 if offset or length or keep_empty_range: | 264 if offset or length or keep_empty_range: |
265 file_data[u"transfer_range"] = Range(offset=offset, length=length) | 265 file_data["transfer_range"] = Range(offset=offset, length=length) |
266 | 266 |
267 prefix = u"given_" if given else u"" | 267 prefix = "given_" if given else "" |
268 hash_algo_key, hash_key = u"hash_algo", prefix + u"file_hash" | 268 hash_algo_key, hash_key = "hash_algo", prefix + "file_hash" |
269 try: | 269 try: |
270 file_data[hash_algo_key], file_data[hash_key] = self._hash.parseHashElt( | 270 file_data[hash_algo_key], file_data[hash_key] = self._hash.parseHashElt( |
271 file_elt | 271 file_elt |
272 ) | 272 ) |
273 except exceptions.NotFound: | 273 except exceptions.NotFound: |
274 pass | 274 pass |
275 | 275 |
276 self.host.trigger.point(u"XEP-0234_parseFileElement", file_elt, file_data) | 276 self.host.trigger.point("XEP-0234_parseFileElement", file_elt, file_data) |
277 | 277 |
278 return file_data | 278 return file_data |
279 | 279 |
280 # bridge methods | 280 # bridge methods |
281 | 281 |
377 progress_id_d = defer.Deferred() | 377 progress_id_d = defer.Deferred() |
378 if extra is None: | 378 if extra is None: |
379 extra = {} | 379 extra = {} |
380 if file_hash is not None: | 380 if file_hash is not None: |
381 if hash_algo is None: | 381 if hash_algo is None: |
382 raise ValueError(_(u"hash_algo must be set if file_hash is set")) | 382 raise ValueError(_("hash_algo must be set if file_hash is set")) |
383 extra["file_hash"] = file_hash | 383 extra["file_hash"] = file_hash |
384 extra["hash_algo"] = hash_algo | 384 extra["hash_algo"] = hash_algo |
385 else: | 385 else: |
386 if hash_algo is not None: | 386 if hash_algo is not None: |
387 raise ValueError(_(u"file_hash must be set if hash_algo is set")) | 387 raise ValueError(_("file_hash must be set if hash_algo is set")) |
388 yield self._j.initiate( | 388 yield self._j.initiate( |
389 client, | 389 client, |
390 peer_jid, | 390 peer_jid, |
391 [ | 391 [ |
392 { | 392 { |
412 if extra is None: | 412 if extra is None: |
413 extra = {} | 413 extra = {} |
414 else: | 414 else: |
415 if not EXTRA_ALLOWED.issuperset(extra): | 415 if not EXTRA_ALLOWED.issuperset(extra): |
416 raise ValueError( | 416 raise ValueError( |
417 _(u"only the following keys are allowed in extra: {keys}").format( | 417 _("only the following keys are allowed in extra: {keys}").format( |
418 keys=u", ".join(EXTRA_ALLOWED) | 418 keys=", ".join(EXTRA_ALLOWED) |
419 ) | 419 ) |
420 ) | 420 ) |
421 progress_id_d.callback(self.getProgressId(session, content_name)) | 421 progress_id_d.callback(self.getProgressId(session, content_name)) |
422 content_data = session["contents"][content_name] | 422 content_data = session["contents"][content_name] |
423 application_data = content_data["application_data"] | 423 application_data = content_data["application_data"] |
425 application_data["file_path"] = filepath | 425 application_data["file_path"] = filepath |
426 file_data = application_data["file_data"] = {} | 426 file_data = application_data["file_data"] = {} |
427 desc_elt = domish.Element((NS_JINGLE_FT, "description")) | 427 desc_elt = domish.Element((NS_JINGLE_FT, "description")) |
428 file_elt = desc_elt.addElement("file") | 428 file_elt = desc_elt.addElement("file") |
429 | 429 |
430 if content_data[u"senders"] == self._j.ROLE_INITIATOR: | 430 if content_data["senders"] == self._j.ROLE_INITIATOR: |
431 # we send a file | 431 # we send a file |
432 if name is None: | 432 if name is None: |
433 name = os.path.basename(filepath) | 433 name = os.path.basename(filepath) |
434 file_data[u"date"] = utils.xmpp_date() | 434 file_data["date"] = utils.xmpp_date() |
435 file_data[u"desc"] = extra.pop(u"file_desc", u"") | 435 file_data["desc"] = extra.pop("file_desc", "") |
436 file_data[u"name"] = name | 436 file_data["name"] = name |
437 mime_type = mimetypes.guess_type(name, strict=False)[0] | 437 mime_type = mimetypes.guess_type(name, strict=False)[0] |
438 if mime_type is not None: | 438 if mime_type is not None: |
439 file_data[u"mime_type"] = mime_type | 439 file_data["mime_type"] = mime_type |
440 file_data[u"size"] = os.path.getsize(filepath) | 440 file_data["size"] = os.path.getsize(filepath) |
441 if u"namespace" in extra: | 441 if "namespace" in extra: |
442 file_data[u"namespace"] = extra[u"namespace"] | 442 file_data["namespace"] = extra["namespace"] |
443 if u"path" in extra: | 443 if "path" in extra: |
444 file_data[u"path"] = extra[u"path"] | 444 file_data["path"] = extra["path"] |
445 self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash=u"") | 445 self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash="") |
446 else: | 446 else: |
447 # we request a file | 447 # we request a file |
448 file_hash = extra.pop(u"file_hash", u"") | 448 file_hash = extra.pop("file_hash", "") |
449 if not name and not file_hash: | 449 if not name and not file_hash: |
450 raise ValueError(_(u"you need to provide at least name or file hash")) | 450 raise ValueError(_("you need to provide at least name or file hash")) |
451 if name: | 451 if name: |
452 file_data[u"name"] = name | 452 file_data["name"] = name |
453 if file_hash: | 453 if file_hash: |
454 file_data[u"file_hash"] = file_hash | 454 file_data["file_hash"] = file_hash |
455 file_data[u"hash_algo"] = extra[u"hash_algo"] | 455 file_data["hash_algo"] = extra["hash_algo"] |
456 else: | 456 else: |
457 file_data[u"hash_algo"] = self._hash.getDefaultAlgo() | 457 file_data["hash_algo"] = self._hash.getDefaultAlgo() |
458 if u"namespace" in extra: | 458 if "namespace" in extra: |
459 file_data[u"namespace"] = extra[u"namespace"] | 459 file_data["namespace"] = extra["namespace"] |
460 if u"path" in extra: | 460 if "path" in extra: |
461 file_data[u"path"] = extra[u"path"] | 461 file_data["path"] = extra["path"] |
462 self.buildFileElementFromDict(file_data, file_elt=file_elt) | 462 self.buildFileElementFromDict(file_data, file_elt=file_elt) |
463 | 463 |
464 return desc_elt | 464 return desc_elt |
465 | 465 |
466 def jingleRequestConfirmation(self, client, action, session, content_name, desc_elt): | 466 def jingleRequestConfirmation(self, client, action, session, content_name, desc_elt): |
467 """This method request confirmation for a jingle session""" | 467 """This method request confirmation for a jingle session""" |
468 content_data = session["contents"][content_name] | 468 content_data = session["contents"][content_name] |
469 senders = content_data[u"senders"] | 469 senders = content_data["senders"] |
470 if senders not in (self._j.ROLE_INITIATOR, self._j.ROLE_RESPONDER): | 470 if senders not in (self._j.ROLE_INITIATOR, self._j.ROLE_RESPONDER): |
471 log.warning(u"Bad sender, assuming initiator") | 471 log.warning("Bad sender, assuming initiator") |
472 senders = content_data[u"senders"] = self._j.ROLE_INITIATOR | 472 senders = content_data["senders"] = self._j.ROLE_INITIATOR |
473 # first we grab file informations | 473 # first we grab file informations |
474 try: | 474 try: |
475 file_elt = desc_elt.elements(NS_JINGLE_FT, "file").next() | 475 file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file")) |
476 except StopIteration: | 476 except StopIteration: |
477 raise failure.Failure(exceptions.DataError) | 477 raise failure.Failure(exceptions.DataError) |
478 file_data = {"progress_id": self.getProgressId(session, content_name)} | 478 file_data = {"progress_id": self.getProgressId(session, content_name)} |
479 | 479 |
480 if senders == self._j.ROLE_RESPONDER: | 480 if senders == self._j.ROLE_RESPONDER: |
514 finished_d.addCallbacks( | 514 finished_d.addCallbacks( |
515 self._finishedCb, self._finishedEb, args, None, args | 515 self._finishedCb, self._finishedEb, args, None, args |
516 ) | 516 ) |
517 defer.returnValue(confirmed) | 517 defer.returnValue(confirmed) |
518 | 518 |
519 log.warning(_(u"File continue is not implemented yet")) | 519 log.warning(_("File continue is not implemented yet")) |
520 defer.returnValue(False) | 520 defer.returnValue(False) |
521 | 521 |
522 def _fileReceivingRequestConf( | 522 def _fileReceivingRequestConf( |
523 self, client, session, content_data, content_name, file_data, file_elt | 523 self, client, session, content_data, content_name, file_data, file_elt |
524 ): | 524 ): |
543 raise failure.Failure(exceptions.DataError) | 543 raise failure.Failure(exceptions.DataError) |
544 | 544 |
545 name = file_data["name"] | 545 name = file_data["name"] |
546 if "/" in name or "\\" in name: | 546 if "/" in name or "\\" in name: |
547 log.warning( | 547 log.warning( |
548 u"File name contain path characters, we replace them: {}".format(name) | 548 "File name contain path characters, we replace them: {}".format(name) |
549 ) | 549 ) |
550 file_data["name"] = name.replace("/", "_").replace("\\", "_") | 550 file_data["name"] = name.replace("/", "_").replace("\\", "_") |
551 | 551 |
552 content_data["application_data"]["file_data"] = file_data | 552 content_data["application_data"]["file_data"] = file_data |
553 | 553 |
572 content_data = session["contents"][content_name] | 572 content_data = session["contents"][content_name] |
573 application_data = content_data["application_data"] | 573 application_data = content_data["application_data"] |
574 if action in (self._j.A_ACCEPTED_ACK,): | 574 if action in (self._j.A_ACCEPTED_ACK,): |
575 pass | 575 pass |
576 elif action == self._j.A_SESSION_INITIATE: | 576 elif action == self._j.A_SESSION_INITIATE: |
577 file_elt = desc_elt.elements(NS_JINGLE_FT, "file").next() | 577 file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file")) |
578 try: | 578 try: |
579 file_elt.elements(NS_JINGLE_FT, "range").next() | 579 next(file_elt.elements(NS_JINGLE_FT, "range")) |
580 except StopIteration: | 580 except StopIteration: |
581 # initiator doesn't manage <range>, but we do so we advertise it | 581 # initiator doesn't manage <range>, but we do so we advertise it |
582 # FIXME: to be checked | 582 # FIXME: to be checked |
583 log.debug("adding <range> element") | 583 log.debug("adding <range> element") |
584 file_elt.addElement("range") | 584 file_elt.addElement("range") |
585 elif action == self._j.A_SESSION_ACCEPT: | 585 elif action == self._j.A_SESSION_ACCEPT: |
586 assert not "stream_object" in content_data | 586 assert not "stream_object" in content_data |
587 file_data = application_data["file_data"] | 587 file_data = application_data["file_data"] |
588 file_path = application_data["file_path"] | 588 file_path = application_data["file_path"] |
589 senders = content_data[u"senders"] | 589 senders = content_data["senders"] |
590 if senders != session[u"role"]: | 590 if senders != session["role"]: |
591 # we are receiving the file | 591 # we are receiving the file |
592 try: | 592 try: |
593 # did the responder specified the size of the file? | 593 # did the responder specified the size of the file? |
594 file_elt = next(desc_elt.elements(NS_JINGLE_FT, u"file")) | 594 file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file")) |
595 size_elt = next(file_elt.elements(NS_JINGLE_FT, u"size")) | 595 size_elt = next(file_elt.elements(NS_JINGLE_FT, "size")) |
596 size = int(unicode(size_elt)) | 596 size = int(str(size_elt)) |
597 except (StopIteration, ValueError): | 597 except (StopIteration, ValueError): |
598 size = None | 598 size = None |
599 # XXX: hash security is not critical here, so we just take the higher mandatory one | 599 # XXX: hash security is not critical here, so we just take the higher mandatory one |
600 hasher = file_data["hash_hasher"] = self._hash.getHasher() | 600 hasher = file_data["hash_hasher"] = self._hash.getHasher() |
601 content_data["stream_object"] = stream.FileStreamObject( | 601 content_data["stream_object"] = stream.FileStreamObject( |
622 ) | 622 ) |
623 finished_d = content_data["finished_d"] = defer.Deferred() | 623 finished_d = content_data["finished_d"] = defer.Deferred() |
624 args = [client, session, content_name, content_data] | 624 args = [client, session, content_name, content_data] |
625 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) | 625 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) |
626 else: | 626 else: |
627 log.warning(u"FIXME: unmanaged action {}".format(action)) | 627 log.warning("FIXME: unmanaged action {}".format(action)) |
628 return desc_elt | 628 return desc_elt |
629 | 629 |
630 def jingleSessionInfo(self, client, action, session, content_name, jingle_elt): | 630 def jingleSessionInfo(self, client, action, session, content_name, jingle_elt): |
631 """Called on session-info action | 631 """Called on session-info action |
632 | 632 |
642 pass | 642 pass |
643 elif elt.name == "checksum": | 643 elif elt.name == "checksum": |
644 # we have received the file hash, we need to parse it | 644 # we have received the file hash, we need to parse it |
645 if content_data["senders"] == session["role"]: | 645 if content_data["senders"] == session["role"]: |
646 log.warning( | 646 log.warning( |
647 u"unexpected checksum received while we are the file sender" | 647 "unexpected checksum received while we are the file sender" |
648 ) | 648 ) |
649 raise exceptions.DataError | 649 raise exceptions.DataError |
650 info_content_name = elt["name"] | 650 info_content_name = elt["name"] |
651 if info_content_name != content_name: | 651 if info_content_name != content_name: |
652 # it was for an other content... | 652 # it was for an other content... |
653 return | 653 return |
654 file_data = content_data["application_data"]["file_data"] | 654 file_data = content_data["application_data"]["file_data"] |
655 try: | 655 try: |
656 file_elt = elt.elements((NS_JINGLE_FT, "file")).next() | 656 file_elt = next(elt.elements((NS_JINGLE_FT, "file"))) |
657 except StopIteration: | 657 except StopIteration: |
658 raise exceptions.DataError | 658 raise exceptions.DataError |
659 algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt) | 659 algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt) |
660 if algo != file_data.get("hash_algo"): | 660 if algo != file_data.get("hash_algo"): |
661 log.warning( | 661 log.warning( |
662 u"Hash algorithm used in given hash ({peer_algo}) doesn't correspond to the one we have used ({our_algo}) [{profile}]".format( | 662 "Hash algorithm used in given hash ({peer_algo}) doesn't correspond to the one we have used ({our_algo}) [{profile}]".format( |
663 peer_algo=algo, | 663 peer_algo=algo, |
664 our_algo=file_data.get("hash_algo"), | 664 our_algo=file_data.get("hash_algo"), |
665 profile=client.profile, | 665 profile=client.profile, |
666 ) | 666 ) |
667 ) | 667 ) |
683 def _sendCheckSum(self, client, session, content_name, content_data): | 683 def _sendCheckSum(self, client, session, content_name, content_data): |
684 """Send the session-info with the hash checksum""" | 684 """Send the session-info with the hash checksum""" |
685 file_data = content_data["application_data"]["file_data"] | 685 file_data = content_data["application_data"]["file_data"] |
686 hasher = file_data["hash_hasher"] | 686 hasher = file_data["hash_hasher"] |
687 hash_ = hasher.hexdigest() | 687 hash_ = hasher.hexdigest() |
688 log.debug(u"Calculated hash: {}".format(hash_)) | 688 log.debug("Calculated hash: {}".format(hash_)) |
689 iq_elt, jingle_elt = self._j.buildSessionInfo(client, session) | 689 iq_elt, jingle_elt = self._j.buildSessionInfo(client, session) |
690 checksum_elt = jingle_elt.addElement((NS_JINGLE_FT, "checksum")) | 690 checksum_elt = jingle_elt.addElement((NS_JINGLE_FT, "checksum")) |
691 checksum_elt["creator"] = content_data["creator"] | 691 checksum_elt["creator"] = content_data["creator"] |
692 checksum_elt["name"] = content_name | 692 checksum_elt["name"] = content_name |
693 file_elt = checksum_elt.addElement("file") | 693 file_elt = checksum_elt.addElement("file") |
710 file_data = content_data["application_data"]["file_data"] | 710 file_data = content_data["application_data"]["file_data"] |
711 given_hash = file_data.get("given_file_hash") | 711 given_hash = file_data.get("given_file_hash") |
712 if given_hash is None: | 712 if given_hash is None: |
713 if last_try: | 713 if last_try: |
714 log.warning( | 714 log.warning( |
715 u"sender didn't sent hash checksum, we can't check the file [{profile}]".format( | 715 "sender didn't sent hash checksum, we can't check the file [{profile}]".format( |
716 profile=client.profile | 716 profile=client.profile |
717 ) | 717 ) |
718 ) | 718 ) |
719 self._j.delayedContentTerminate(client, session, content_name) | 719 self._j.delayedContentTerminate(client, session, content_name) |
720 content_data["stream_object"].close() | 720 content_data["stream_object"].close() |
721 return True | 721 return True |
722 return False | 722 return False |
723 hasher = file_data["hash_hasher"] | 723 hasher = file_data["hash_hasher"] |
724 hash_ = hasher.hexdigest() | 724 hash_ = hasher.hexdigest().encode('utf-8') |
725 | 725 |
726 if hash_ == given_hash: | 726 if hash_ == given_hash: |
727 log.info(u"Hash checked, file was successfully transfered: {}".format(hash_)) | 727 log.info("Hash checked, file was successfully transfered: {}".format(hash_)) |
728 progress_metadata = { | 728 progress_metadata = { |
729 "hash": hash_, | 729 "hash": hash_, |
730 "hash_algo": file_data["hash_algo"], | 730 "hash_algo": file_data["hash_algo"], |
731 "hash_verified": C.BOOL_TRUE, | 731 "hash_verified": C.BOOL_TRUE, |
732 } | 732 } |
733 error = None | 733 error = None |
734 else: | 734 else: |
735 log.warning(u"Hash mismatch, the file was not transfered correctly") | 735 log.warning("Hash mismatch, the file was not transfered correctly") |
736 progress_metadata = None | 736 progress_metadata = None |
737 error = u"Hash mismatch: given={algo}:{given}, calculated={algo}:{our}".format( | 737 error = "Hash mismatch: given={algo}:{given}, calculated={algo}:{our}".format( |
738 algo=file_data["hash_algo"], given=given_hash, our=hash_ | 738 algo=file_data["hash_algo"], given=given_hash, our=hash_ |
739 ) | 739 ) |
740 | 740 |
741 self._j.delayedContentTerminate(client, session, content_name) | 741 self._j.delayedContentTerminate(client, session, content_name) |
742 content_data["stream_object"].close(progress_metadata, error) | 742 content_data["stream_object"].close(progress_metadata, error) |
746 except (KeyError, internet_error.AlreadyCalled): | 746 except (KeyError, internet_error.AlreadyCalled): |
747 pass | 747 pass |
748 return True | 748 return True |
749 | 749 |
750 def _finishedCb(self, __, client, session, content_name, content_data): | 750 def _finishedCb(self, __, client, session, content_name, content_data): |
751 log.info(u"File transfer terminated") | 751 log.info("File transfer terminated") |
752 if content_data["senders"] != session["role"]: | 752 if content_data["senders"] != session["role"]: |
753 # we terminate the session only if we are the receiver, | 753 # we terminate the session only if we are the receiver, |
754 # as recommanded in XEP-0234 §2 (after example 6) | 754 # as recommanded in XEP-0234 §2 (after example 6) |
755 content_data["transfer_finished"] = True | 755 content_data["transfer_finished"] = True |
756 if not self._receiverTryTerminate( | 756 if not self._receiverTryTerminate( |
770 # we are the sender, we send the checksum | 770 # we are the sender, we send the checksum |
771 self._sendCheckSum(client, session, content_name, content_data) | 771 self._sendCheckSum(client, session, content_name, content_data) |
772 content_data["stream_object"].close() | 772 content_data["stream_object"].close() |
773 | 773 |
774 def _finishedEb(self, failure, client, session, content_name, content_data): | 774 def _finishedEb(self, failure, client, session, content_name, content_data): |
775 log.warning(u"Error while streaming file: {}".format(failure)) | 775 log.warning("Error while streaming file: {}".format(failure)) |
776 content_data["stream_object"].close() | 776 content_data["stream_object"].close() |
777 self._j.contentTerminate( | 777 self._j.contentTerminate( |
778 client, session, content_name, reason=self._j.REASON_FAILED_TRANSPORT | 778 client, session, content_name, reason=self._j.REASON_FAILED_TRANSPORT |
779 ) | 779 ) |
780 | 780 |
781 | 781 |
782 @implementer(iwokkel.IDisco) | |
782 class XEP_0234_handler(XMPPHandler): | 783 class XEP_0234_handler(XMPPHandler): |
783 implements(iwokkel.IDisco) | |
784 | 784 |
785 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | 785 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
786 return [disco.DiscoFeature(NS_JINGLE_FT)] | 786 return [disco.DiscoFeature(NS_JINGLE_FT)] |
787 | 787 |
788 def getDiscoItems(self, requestor, target, nodeIdentifier=""): | 788 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |