Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0363.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 | 8ce5748bfe97 |
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 HTTP File Upload (XEP-0363) | 4 # SAT plugin for HTTP File Upload (XEP-0363) |
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 twisted.words.protocols.jabber import jid | 28 from twisted.words.protocols.jabber import jid |
29 from twisted.words.protocols.jabber.xmlstream import XMPPHandler | 29 from twisted.words.protocols.jabber.xmlstream import XMPPHandler |
30 from twisted.internet import reactor | 30 from twisted.internet import reactor |
31 from twisted.internet import defer | 31 from twisted.internet import defer |
32 from twisted.internet import ssl | 32 from twisted.internet import ssl |
48 C.PI_TYPE: "XEP", | 48 C.PI_TYPE: "XEP", |
49 C.PI_PROTOCOLS: ["XEP-0363"], | 49 C.PI_PROTOCOLS: ["XEP-0363"], |
50 C.PI_DEPENDENCIES: ["FILE", "UPLOAD"], | 50 C.PI_DEPENDENCIES: ["FILE", "UPLOAD"], |
51 C.PI_MAIN: "XEP_0363", | 51 C.PI_MAIN: "XEP_0363", |
52 C.PI_HANDLER: "yes", | 52 C.PI_HANDLER: "yes", |
53 C.PI_DESCRIPTION: _(u"""Implementation of HTTP File Upload"""), | 53 C.PI_DESCRIPTION: _("""Implementation of HTTP File Upload"""), |
54 } | 54 } |
55 | 55 |
56 NS_HTTP_UPLOAD = "urn:xmpp:http:upload:0" | 56 NS_HTTP_UPLOAD = "urn:xmpp:http:upload:0" |
57 ALLOWED_HEADERS = ('authorization', 'cookie', 'expires') | 57 ALLOWED_HEADERS = ('authorization', 'cookie', 'expires') |
58 | 58 |
80 and it should be used only with explicite agreement from the end used | 80 and it should be used only with explicite agreement from the end used |
81 """ | 81 """ |
82 | 82 |
83 def creatorForNetloc(self, hostname, port): | 83 def creatorForNetloc(self, hostname, port): |
84 log.warning( | 84 log.warning( |
85 u"TLS check disabled for {host} on port {port}".format( | 85 "TLS check disabled for {host} on port {port}".format( |
86 host=hostname, port=port | 86 host=hostname, port=port |
87 ) | 87 ) |
88 ) | 88 ) |
89 certificateOptions = ssl.CertificateOptions(trustRoot=None) | 89 certificateOptions = ssl.CertificateOptions(trustRoot=None) |
90 return NoCheckConnectionCreator(hostname, certificateOptions.getContext()) | 90 return NoCheckConnectionCreator(hostname, certificateOptions.getContext()) |
105 "fileHTTPUploadGetSlot", | 105 "fileHTTPUploadGetSlot", |
106 ".plugin", | 106 ".plugin", |
107 in_sign="sisss", | 107 in_sign="sisss", |
108 out_sign="(ss)", | 108 out_sign="(ss)", |
109 method=self._getSlot, | 109 method=self._getSlot, |
110 async=True, | 110 async_=True, |
111 ) | 111 ) |
112 host.plugins["UPLOAD"].register( | 112 host.plugins["UPLOAD"].register( |
113 u"HTTP Upload", self.getHTTPUploadEntity, self.fileHTTPUpload | 113 "HTTP Upload", self.getHTTPUploadEntity, self.fileHTTPUpload |
114 ) | 114 ) |
115 | 115 |
116 def getHandler(self, client): | 116 def getHandler(self, client): |
117 return XEP_0363_handler() | 117 return XEP_0363_handler() |
118 | 118 |
129 try: | 129 try: |
130 entity = client.http_upload_service | 130 entity = client.http_upload_service |
131 except AttributeError: | 131 except AttributeError: |
132 found_entities = yield self.host.findFeaturesSet(client, (NS_HTTP_UPLOAD,)) | 132 found_entities = yield self.host.findFeaturesSet(client, (NS_HTTP_UPLOAD,)) |
133 try: | 133 try: |
134 entity = client.http_upload_service = iter(found_entities).next() | 134 entity = client.http_upload_service = next(iter(found_entities)) |
135 except StopIteration: | 135 except StopIteration: |
136 entity = client.http_upload_service = None | 136 entity = client.http_upload_service = None |
137 | 137 |
138 if entity is None: | 138 if entity is None: |
139 raise failure.Failure(exceptions.NotFound(u"No HTTP upload entity found")) | 139 raise failure.Failure(exceptions.NotFound("No HTTP upload entity found")) |
140 | 140 |
141 defer.returnValue(entity) | 141 defer.returnValue(entity) |
142 | 142 |
143 def _fileHTTPUpload(self, filepath, filename="", upload_jid="", | 143 def _fileHTTPUpload(self, filepath, filename="", upload_jid="", |
144 ignore_tls_errors=False, profile=C.PROF_KEY_NONE): | 144 ignore_tls_errors=False, profile=C.PROF_KEY_NONE): |
185 ) | 185 ) |
186 return progress_id_d, download_d | 186 return progress_id_d, download_d |
187 | 187 |
188 def _getSlotEb(self, fail, client, progress_id_d, download_d): | 188 def _getSlotEb(self, fail, client, progress_id_d, download_d): |
189 """an error happened while trying to get slot""" | 189 """an error happened while trying to get slot""" |
190 log.warning(u"Can't get upload slot: {reason}".format(reason=fail.value)) | 190 log.warning("Can't get upload slot: {reason}".format(reason=fail.value)) |
191 progress_id_d.errback(fail) | 191 progress_id_d.errback(fail) |
192 download_d.errback(fail) | 192 download_d.errback(fail) |
193 | 193 |
194 def _getSlotCb(self, slot, client, progress_id_d, download_d, path, size, | 194 def _getSlotCb(self, slot, client, progress_id_d, download_d, path, size, |
195 ignore_tls_errors=False): | 195 ignore_tls_errors=False): |
202 @param path(str): path to the file to upload | 202 @param path(str): path to the file to upload |
203 @param size(int): size of the file to upload | 203 @param size(int): size of the file to upload |
204 @param ignore_tls_errors(bool): ignore TLS certificate is True | 204 @param ignore_tls_errors(bool): ignore TLS certificate is True |
205 @return (tuple | 205 @return (tuple |
206 """ | 206 """ |
207 log.debug(u"Got upload slot: {}".format(slot)) | 207 log.debug("Got upload slot: {}".format(slot)) |
208 sat_file = self.host.plugins["FILE"].File( | 208 sat_file = self.host.plugins["FILE"].File( |
209 self.host, client, path, size=size, auto_end_signals=False | 209 self.host, client, path, size=size, auto_end_signals=False |
210 ) | 210 ) |
211 progress_id_d.callback(sat_file.uid) | 211 progress_id_d.callback(sat_file.uid) |
212 file_producer = http_client.FileBodyProducer(sat_file) | 212 file_producer = http_client.FileBodyProducer(sat_file) |
241 | 241 |
242 @param sat_file(SatFile): file used for the upload | 242 @param sat_file(SatFile): file used for the upload |
243 should be closed, be is needed to send the progressFinished signal | 243 should be closed, be is needed to send the progressFinished signal |
244 @param slot(Slot): put/get urls | 244 @param slot(Slot): put/get urls |
245 """ | 245 """ |
246 log.info(u"HTTP upload finished") | 246 log.info("HTTP upload finished") |
247 sat_file.progressFinished({"url": slot.get}) | 247 sat_file.progressFinished({"url": slot.get}) |
248 download_d.callback(slot.get) | 248 download_d.callback(slot.get) |
249 | 249 |
250 def _uploadEb(self, fail, sat_file, download_d): | 250 def _uploadEb(self, fail, sat_file, download_d): |
251 """Called on unsuccessful upload | 251 """Called on unsuccessful upload |
255 """ | 255 """ |
256 download_d.errback(fail) | 256 download_d.errback(fail) |
257 try: | 257 try: |
258 wrapped_fail = fail.value.reasons[0] | 258 wrapped_fail = fail.value.reasons[0] |
259 except (AttributeError, IndexError) as e: | 259 except (AttributeError, IndexError) as e: |
260 log.warning(_(u"upload failed: {reason}").format(reason=e)) | 260 log.warning(_("upload failed: {reason}").format(reason=e)) |
261 sat_file.progressError(unicode(fail)) | 261 sat_file.progressError(str(fail)) |
262 raise fail | 262 raise fail |
263 else: | 263 else: |
264 if wrapped_fail.check(SSL.Error): | 264 if wrapped_fail.check(SSL.Error): |
265 msg = u"TLS validation error, can't connect to HTTPS server" | 265 msg = "TLS validation error, can't connect to HTTPS server" |
266 else: | 266 else: |
267 msg = u"can't upload file" | 267 msg = "can't upload file" |
268 log.warning(msg + ": " + unicode(wrapped_fail.value)) | 268 log.warning(msg + ": " + str(wrapped_fail.value)) |
269 sat_file.progressError(msg) | 269 sat_file.progressError(msg) |
270 | 270 |
271 def _gotSlot(self, iq_elt, client): | 271 def _gotSlot(self, iq_elt, client): |
272 """Slot have been received | 272 """Slot have been received |
273 | 273 |
274 This method convert the iq_elt result to a Slot instance | 274 This method convert the iq_elt result to a Slot instance |
275 @param iq_elt(domish.Element): <IQ/> result as specified in XEP-0363 | 275 @param iq_elt(domish.Element): <IQ/> result as specified in XEP-0363 |
276 """ | 276 """ |
277 try: | 277 try: |
278 slot_elt = iq_elt.elements(NS_HTTP_UPLOAD, "slot").next() | 278 slot_elt = next(iq_elt.elements(NS_HTTP_UPLOAD, "slot")) |
279 put_elt = slot_elt.elements(NS_HTTP_UPLOAD, "put").next() | 279 put_elt = next(slot_elt.elements(NS_HTTP_UPLOAD, "put")) |
280 put_url = put_elt['url'] | 280 put_url = put_elt['url'] |
281 get_elt = slot_elt.elements(NS_HTTP_UPLOAD, "get").next() | 281 get_elt = next(slot_elt.elements(NS_HTTP_UPLOAD, "get")) |
282 get_url = get_elt['url'] | 282 get_url = get_elt['url'] |
283 except (StopIteration, KeyError): | 283 except (StopIteration, KeyError): |
284 raise exceptions.DataError(u"Incorrect stanza received from server") | 284 raise exceptions.DataError("Incorrect stanza received from server") |
285 headers = [] | 285 headers = [] |
286 for header_elt in put_elt.elements(NS_HTTP_UPLOAD, "header"): | 286 for header_elt in put_elt.elements(NS_HTTP_UPLOAD, "header"): |
287 try: | 287 try: |
288 name = header_elt["name"] | 288 name = header_elt["name"] |
289 value = unicode(header_elt) | 289 value = str(header_elt) |
290 except KeyError: | 290 except KeyError: |
291 log.warning(_(u"Invalid header element: {xml}").format( | 291 log.warning(_("Invalid header element: {xml}").format( |
292 iq_elt.toXml())) | 292 iq_elt.toXml())) |
293 continue | 293 continue |
294 name = name.replace('\n', '') | 294 name = name.replace('\n', '') |
295 value = value.replace('\n', '') | 295 value = value.replace('\n', '') |
296 if name.lower() not in ALLOWED_HEADERS: | 296 if name.lower() not in ALLOWED_HEADERS: |
297 log.warning(_(u'Ignoring unauthorised header "{name}": {xml}') | 297 log.warning(_('Ignoring unauthorised header "{name}": {xml}') |
298 .format(name=name, xml = iq_elt.toXml())) | 298 .format(name=name, xml = iq_elt.toXml())) |
299 continue | 299 continue |
300 headers.append((name, value)) | 300 headers.append((name, value)) |
301 | 301 |
302 slot = Slot(put=put_url, get=get_url, headers=tuple(headers)) | 302 slot = Slot(put=put_url, get=get_url, headers=tuple(headers)) |
349 ) | 349 ) |
350 return d | 350 return d |
351 else: | 351 else: |
352 if upload_jid is None: | 352 if upload_jid is None: |
353 raise failure.Failure( | 353 raise failure.Failure( |
354 exceptions.NotFound(u"No HTTP upload entity found") | 354 exceptions.NotFound("No HTTP upload entity found") |
355 ) | 355 ) |
356 | 356 |
357 iq_elt = client.IQ("get") | 357 iq_elt = client.IQ("get") |
358 iq_elt["to"] = upload_jid.full() | 358 iq_elt["to"] = upload_jid.full() |
359 request_elt = iq_elt.addElement((NS_HTTP_UPLOAD, "request")) | 359 request_elt = iq_elt.addElement((NS_HTTP_UPLOAD, "request")) |
360 request_elt["filename"] = filename | 360 request_elt["filename"] = filename |
361 request_elt["size"] = unicode(size) | 361 request_elt["size"] = str(size) |
362 if content_type is not None: | 362 if content_type is not None: |
363 request_elt["content-type"] = content_type | 363 request_elt["content-type"] = content_type |
364 | 364 |
365 d = iq_elt.send() | 365 d = iq_elt.send() |
366 d.addCallback(self._gotSlot, client) | 366 d.addCallback(self._gotSlot, client) |
367 | 367 |
368 return d | 368 return d |
369 | 369 |
370 | 370 |
371 @implementer(iwokkel.IDisco) | |
371 class XEP_0363_handler(XMPPHandler): | 372 class XEP_0363_handler(XMPPHandler): |
372 implements(iwokkel.IDisco) | |
373 | 373 |
374 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | 374 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
375 return [disco.DiscoFeature(NS_HTTP_UPLOAD)] | 375 return [disco.DiscoFeature(NS_HTTP_UPLOAD)] |
376 | 376 |
377 def getDiscoItems(self, requestor, target, nodeIdentifier=""): | 377 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |