Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0363.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 26edcf3a30eb |
children | 378188abe941 |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.i18n import _ | 20 from sat.core.i18n import _ |
21 from sat.core.constants import Const as C | 21 from sat.core.constants import Const as C |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 | |
23 log = getLogger(__name__) | 24 log = getLogger(__name__) |
24 from sat.core import exceptions | 25 from sat.core import exceptions |
25 from wokkel import disco, iwokkel | 26 from wokkel import disco, iwokkel |
26 from zope.interface import implements | 27 from zope.interface import implements |
27 from twisted.words.protocols.jabber import jid | 28 from twisted.words.protocols.jabber import jid |
47 C.PI_TYPE: "XEP", | 48 C.PI_TYPE: "XEP", |
48 C.PI_PROTOCOLS: ["XEP-0363"], | 49 C.PI_PROTOCOLS: ["XEP-0363"], |
49 C.PI_DEPENDENCIES: ["FILE", "UPLOAD"], | 50 C.PI_DEPENDENCIES: ["FILE", "UPLOAD"], |
50 C.PI_MAIN: "XEP_0363", | 51 C.PI_MAIN: "XEP_0363", |
51 C.PI_HANDLER: "yes", | 52 C.PI_HANDLER: "yes", |
52 C.PI_DESCRIPTION: _("""Implementation of HTTP File Upload""") | 53 C.PI_DESCRIPTION: _("""Implementation of HTTP File Upload"""), |
53 } | 54 } |
54 | 55 |
55 NS_HTTP_UPLOAD = 'urn:xmpp:http:upload' | 56 NS_HTTP_UPLOAD = "urn:xmpp:http:upload" |
56 | 57 |
57 | 58 |
58 Slot = namedtuple('Slot', ['put', 'get']) | 59 Slot = namedtuple("Slot", ["put", "get"]) |
59 | 60 |
60 | 61 |
61 @implementer(IOpenSSLClientConnectionCreator) | 62 @implementer(IOpenSSLClientConnectionCreator) |
62 class NoCheckConnectionCreator(object): | 63 class NoCheckConnectionCreator(object): |
63 | |
64 def __init__(self, hostname, ctx): | 64 def __init__(self, hostname, ctx): |
65 self._ctx = ctx | 65 self._ctx = ctx |
66 | 66 |
67 def clientConnectionForTLS(self, tlsProtocol): | 67 def clientConnectionForTLS(self, tlsProtocol): |
68 context = self._ctx | 68 context = self._ctx |
78 /!\\ it's obvisously a security flaw to use this class, | 78 /!\\ it's obvisously a security flaw to use this class, |
79 and it should be used only wiht explicite agreement from the end used | 79 and it should be used only wiht explicite agreement from the end used |
80 """ | 80 """ |
81 | 81 |
82 def creatorForNetloc(self, hostname, port): | 82 def creatorForNetloc(self, hostname, port): |
83 log.warning(u"TLS check disabled for {host} on port {port}".format(host=hostname, port=port)) | 83 log.warning( |
84 u"TLS check disabled for {host} on port {port}".format( | |
85 host=hostname, port=port | |
86 ) | |
87 ) | |
84 certificateOptions = ssl.CertificateOptions(trustRoot=None) | 88 certificateOptions = ssl.CertificateOptions(trustRoot=None) |
85 return NoCheckConnectionCreator(hostname, certificateOptions.getContext()) | 89 return NoCheckConnectionCreator(hostname, certificateOptions.getContext()) |
86 | 90 |
87 | 91 |
88 class XEP_0363(object): | 92 class XEP_0363(object): |
89 | |
90 def __init__(self, host): | 93 def __init__(self, host): |
91 log.info(_("plugin HTTP File Upload initialization")) | 94 log.info(_("plugin HTTP File Upload initialization")) |
92 self.host = host | 95 self.host = host |
93 host.bridge.addMethod("fileHTTPUpload", ".plugin", in_sign='sssbs', out_sign='', method=self._fileHTTPUpload) | 96 host.bridge.addMethod( |
94 host.bridge.addMethod("fileHTTPUploadGetSlot", ".plugin", in_sign='sisss', out_sign='(ss)', method=self._getSlot, async=True) | 97 "fileHTTPUpload", |
95 host.plugins['UPLOAD'].register(u"HTTP Upload", self.getHTTPUploadEntity, self.fileHTTPUpload) | 98 ".plugin", |
99 in_sign="sssbs", | |
100 out_sign="", | |
101 method=self._fileHTTPUpload, | |
102 ) | |
103 host.bridge.addMethod( | |
104 "fileHTTPUploadGetSlot", | |
105 ".plugin", | |
106 in_sign="sisss", | |
107 out_sign="(ss)", | |
108 method=self._getSlot, | |
109 async=True, | |
110 ) | |
111 host.plugins["UPLOAD"].register( | |
112 u"HTTP Upload", self.getHTTPUploadEntity, self.fileHTTPUpload | |
113 ) | |
96 | 114 |
97 def getHandler(self, client): | 115 def getHandler(self, client): |
98 return XEP_0363_handler() | 116 return XEP_0363_handler() |
99 | 117 |
100 @defer.inlineCallbacks | 118 @defer.inlineCallbacks |
101 def getHTTPUploadEntity(self, upload_jid=None, profile=C.PROF_KEY_NONE): | 119 def getHTTPUploadEntity(self, upload_jid=None, profile=C.PROF_KEY_NONE): |
102 """Get HTTP upload capable entity | 120 """Get HTTP upload capable entity |
103 | 121 |
104 upload_jid is checked, then its components | 122 upload_jid is checked, then its components |
105 @param upload_jid(None, jid.JID): entity to check | 123 @param upload_jid(None, jid.JID): entity to check |
106 @return(D(jid.JID)): first HTTP upload capable entity | 124 @return(D(jid.JID)): first HTTP upload capable entity |
107 @raise exceptions.NotFound: no entity found | 125 @raise exceptions.NotFound: no entity found |
108 """ | 126 """ |
109 client = self.host.getClient(profile) | 127 client = self.host.getClient(profile) |
110 try: | 128 try: |
111 entity = client.http_upload_service | 129 entity = client.http_upload_service |
112 except AttributeError: | 130 except AttributeError: |
113 found_entities = yield self.host.findFeaturesSet(client, (NS_HTTP_UPLOAD,)) | 131 found_entities = yield self.host.findFeaturesSet(client, (NS_HTTP_UPLOAD,)) |
114 try: | 132 try: |
115 entity = client.http_upload_service = iter(found_entities).next() | 133 entity = client.http_upload_service = iter(found_entities).next() |
116 except StopIteration: | 134 except StopIteration: |
117 entity = client.http_upload_service = None | 135 entity = client.http_upload_service = None |
118 | 136 |
119 if entity is None: | 137 if entity is None: |
120 raise failure.Failure(exceptions.NotFound(u'No HTTP upload entity found')) | 138 raise failure.Failure(exceptions.NotFound(u"No HTTP upload entity found")) |
121 | 139 |
122 defer.returnValue(entity) | 140 defer.returnValue(entity) |
123 | 141 |
124 def _fileHTTPUpload(self, filepath, filename='', upload_jid='', ignore_tls_errors=False, profile=C.PROF_KEY_NONE): | 142 def _fileHTTPUpload( |
143 self, | |
144 filepath, | |
145 filename="", | |
146 upload_jid="", | |
147 ignore_tls_errors=False, | |
148 profile=C.PROF_KEY_NONE, | |
149 ): | |
125 assert os.path.isabs(filepath) and os.path.isfile(filepath) | 150 assert os.path.isabs(filepath) and os.path.isfile(filepath) |
126 progress_id_d, dummy = self.fileHTTPUpload(filepath, filename or None, jid.JID(upload_jid) if upload_jid else None, {'ignore_tls_errors': ignore_tls_errors}, profile) | 151 progress_id_d, dummy = self.fileHTTPUpload( |
152 filepath, | |
153 filename or None, | |
154 jid.JID(upload_jid) if upload_jid else None, | |
155 {"ignore_tls_errors": ignore_tls_errors}, | |
156 profile, | |
157 ) | |
127 return progress_id_d | 158 return progress_id_d |
128 | 159 |
129 def fileHTTPUpload(self, filepath, filename=None, upload_jid=None, options=None, profile=C.PROF_KEY_NONE): | 160 def fileHTTPUpload( |
161 self, | |
162 filepath, | |
163 filename=None, | |
164 upload_jid=None, | |
165 options=None, | |
166 profile=C.PROF_KEY_NONE, | |
167 ): | |
130 """upload a file through HTTP | 168 """upload a file through HTTP |
131 | 169 |
132 @param filepath(str): absolute path of the file | 170 @param filepath(str): absolute path of the file |
133 @param filename(None, unicode): name to use for the upload | 171 @param filename(None, unicode): name to use for the upload |
134 None to use basename of the path | 172 None to use basename of the path |
139 @param profile: %(doc_profile)s | 177 @param profile: %(doc_profile)s |
140 @return (D(tuple[D(unicode), D(unicode)])): progress id and Deferred which fire download URL | 178 @return (D(tuple[D(unicode), D(unicode)])): progress id and Deferred which fire download URL |
141 """ | 179 """ |
142 if options is None: | 180 if options is None: |
143 options = {} | 181 options = {} |
144 ignore_tls_errors = options.get('ignore_tls_errors', False) | 182 ignore_tls_errors = options.get("ignore_tls_errors", False) |
145 client = self.host.getClient(profile) | 183 client = self.host.getClient(profile) |
146 filename = filename or os.path.basename(filepath) | 184 filename = filename or os.path.basename(filepath) |
147 size = os.path.getsize(filepath) | 185 size = os.path.getsize(filepath) |
148 progress_id_d = defer.Deferred() | 186 progress_id_d = defer.Deferred() |
149 download_d = defer.Deferred() | 187 download_d = defer.Deferred() |
150 d = self.getSlot(client, filename, size, upload_jid=upload_jid) | 188 d = self.getSlot(client, filename, size, upload_jid=upload_jid) |
151 d.addCallbacks(self._getSlotCb, self._getSlotEb, (client, progress_id_d, download_d, filepath, size, ignore_tls_errors), None, (client, progress_id_d, download_d)) | 189 d.addCallbacks( |
190 self._getSlotCb, | |
191 self._getSlotEb, | |
192 (client, progress_id_d, download_d, filepath, size, ignore_tls_errors), | |
193 None, | |
194 (client, progress_id_d, download_d), | |
195 ) | |
152 return progress_id_d, download_d | 196 return progress_id_d, download_d |
153 | 197 |
154 def _getSlotEb(self, fail, client, progress_id_d, download_d): | 198 def _getSlotEb(self, fail, client, progress_id_d, download_d): |
155 """an error happened while trying to get slot""" | 199 """an error happened while trying to get slot""" |
156 log.warning(u"Can't get upload slot: {reason}".format(reason=fail.value)) | 200 log.warning(u"Can't get upload slot: {reason}".format(reason=fail.value)) |
157 progress_id_d.errback(fail) | 201 progress_id_d.errback(fail) |
158 download_d.errback(fail) | 202 download_d.errback(fail) |
159 | 203 |
160 def _getSlotCb(self, slot, client, progress_id_d, download_d, path, size, ignore_tls_errors=False): | 204 def _getSlotCb( |
205 self, slot, client, progress_id_d, download_d, path, size, ignore_tls_errors=False | |
206 ): | |
161 """Called when slot is received, try to do the upload | 207 """Called when slot is received, try to do the upload |
162 | 208 |
163 @param slot(Slot): slot instance with the get and put urls | 209 @param slot(Slot): slot instance with the get and put urls |
164 @param progress_id_d(defer.Deferred): Deferred to call when progress_id is known | 210 @param progress_id_d(defer.Deferred): Deferred to call when progress_id is known |
165 @param progress_id_d(defer.Deferred): Deferred to call with URL when upload is done | 211 @param progress_id_d(defer.Deferred): Deferred to call with URL when upload is done |
167 @param size(int): size of the file to upload | 213 @param size(int): size of the file to upload |
168 @param ignore_tls_errors(bool): ignore TLS certificate is True | 214 @param ignore_tls_errors(bool): ignore TLS certificate is True |
169 @return (tuple | 215 @return (tuple |
170 """ | 216 """ |
171 log.debug(u"Got upload slot: {}".format(slot)) | 217 log.debug(u"Got upload slot: {}".format(slot)) |
172 sat_file = self.host.plugins['FILE'].File(self.host, client, path, size=size, auto_end_signals=False) | 218 sat_file = self.host.plugins["FILE"].File( |
219 self.host, client, path, size=size, auto_end_signals=False | |
220 ) | |
173 progress_id_d.callback(sat_file.uid) | 221 progress_id_d.callback(sat_file.uid) |
174 file_producer = http_client.FileBodyProducer(sat_file) | 222 file_producer = http_client.FileBodyProducer(sat_file) |
175 if ignore_tls_errors: | 223 if ignore_tls_errors: |
176 agent = http_client.Agent(reactor, NoCheckContextFactory()) | 224 agent = http_client.Agent(reactor, NoCheckContextFactory()) |
177 else: | 225 else: |
178 agent = http_client.Agent(reactor) | 226 agent = http_client.Agent(reactor) |
179 d = agent.request('PUT', slot.put.encode('utf-8'), http_headers.Headers({'User-Agent': [C.APP_NAME.encode('utf-8')]}), file_producer) | 227 d = agent.request( |
180 d.addCallbacks(self._uploadCb, self._uploadEb, (sat_file, slot, download_d), None, (sat_file, download_d)) | 228 "PUT", |
229 slot.put.encode("utf-8"), | |
230 http_headers.Headers({"User-Agent": [C.APP_NAME.encode("utf-8")]}), | |
231 file_producer, | |
232 ) | |
233 d.addCallbacks( | |
234 self._uploadCb, | |
235 self._uploadEb, | |
236 (sat_file, slot, download_d), | |
237 None, | |
238 (sat_file, download_d), | |
239 ) | |
181 return d | 240 return d |
182 | 241 |
183 def _uploadCb(self, dummy, sat_file, slot, download_d): | 242 def _uploadCb(self, dummy, sat_file, slot, download_d): |
184 """Called once file is successfully uploaded | 243 """Called once file is successfully uploaded |
185 | 244 |
186 @param sat_file(SatFile): file used for the upload | 245 @param sat_file(SatFile): file used for the upload |
187 should be closed, be is needed to send the progressFinished signal | 246 should be closed, be is needed to send the progressFinished signal |
188 @param slot(Slot): put/get urls | 247 @param slot(Slot): put/get urls |
189 """ | 248 """ |
190 log.info(u"HTTP upload finished") | 249 log.info(u"HTTP upload finished") |
191 sat_file.progressFinished({'url': slot.get}) | 250 sat_file.progressFinished({"url": slot.get}) |
192 download_d.callback(slot.get) | 251 download_d.callback(slot.get) |
193 | 252 |
194 def _uploadEb(self, fail, sat_file, download_d): | 253 def _uploadEb(self, fail, sat_file, download_d): |
195 """Called on unsuccessful upload | 254 """Called on unsuccessful upload |
196 | 255 |
214 | 273 |
215 This method convert the iq_elt result to a Slot instance | 274 This method convert the iq_elt result to a Slot instance |
216 @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 |
217 """ | 276 """ |
218 try: | 277 try: |
219 slot_elt = iq_elt.elements(NS_HTTP_UPLOAD, 'slot').next() | 278 slot_elt = iq_elt.elements(NS_HTTP_UPLOAD, "slot").next() |
220 put_url = unicode(slot_elt.elements(NS_HTTP_UPLOAD, 'put').next()) | 279 put_url = unicode(slot_elt.elements(NS_HTTP_UPLOAD, "put").next()) |
221 get_url = unicode(slot_elt.elements(NS_HTTP_UPLOAD, 'get').next()) | 280 get_url = unicode(slot_elt.elements(NS_HTTP_UPLOAD, "get").next()) |
222 except StopIteration: | 281 except StopIteration: |
223 raise exceptions.DataError(u"Incorrect stanza received from server") | 282 raise exceptions.DataError(u"Incorrect stanza received from server") |
224 slot = Slot(put=put_url, get=get_url) | 283 slot = Slot(put=put_url, get=get_url) |
225 return slot | 284 return slot |
226 | 285 |
227 def _getSlot(self, filename, size, content_type, upload_jid, profile_key=C.PROF_KEY_NONE): | 286 def _getSlot( |
287 self, filename, size, content_type, upload_jid, profile_key=C.PROF_KEY_NONE | |
288 ): | |
228 """Get a upload slot | 289 """Get a upload slot |
229 | 290 |
230 This method can be used when uploading is done by the frontend | 291 This method can be used when uploading is done by the frontend |
231 @param filename(unicode): name of the file to upload | 292 @param filename(unicode): name of the file to upload |
232 @param size(int): size of the file (must be non null) | 293 @param size(int): size of the file (must be non null) |
233 @param upload_jid(jid.JID(), None, ''): HTTP upload capable entity | 294 @param upload_jid(jid.JID(), None, ''): HTTP upload capable entity |
234 @param content_type(unicode, None): MIME type of the content | 295 @param content_type(unicode, None): MIME type of the content |
235 empty string or None to guess automatically | 296 empty string or None to guess automatically |
236 """ | 297 """ |
237 filename = filename.replace('/', '_') | 298 filename = filename.replace("/", "_") |
238 client = self.host.getClient(profile_key) | 299 client = self.host.getClient(profile_key) |
239 return self.getSlot(client, filename, size, content_type or None, upload_jid or None) | 300 return self.getSlot( |
301 client, filename, size, content_type or None, upload_jid or None | |
302 ) | |
240 | 303 |
241 def getSlot(self, client, filename, size, content_type=None, upload_jid=None): | 304 def getSlot(self, client, filename, size, content_type=None, upload_jid=None): |
242 """Get a slot (i.e. download/upload links) | 305 """Get a slot (i.e. download/upload links) |
243 | 306 |
244 @param filename(unicode): name to use for the upload | 307 @param filename(unicode): name to use for the upload |
259 if upload_jid is None: | 322 if upload_jid is None: |
260 try: | 323 try: |
261 upload_jid = client.http_upload_service | 324 upload_jid = client.http_upload_service |
262 except AttributeError: | 325 except AttributeError: |
263 d = self.getHTTPUploadEntity(profile=client.profile) | 326 d = self.getHTTPUploadEntity(profile=client.profile) |
264 d.addCallback(lambda found_entity: self.getSlot(client, filename, size, content_type, found_entity)) | 327 d.addCallback( |
328 lambda found_entity: self.getSlot( | |
329 client, filename, size, content_type, found_entity | |
330 ) | |
331 ) | |
265 return d | 332 return d |
266 else: | 333 else: |
267 if upload_jid is None: | 334 if upload_jid is None: |
268 raise failure.Failure(exceptions.NotFound(u'No HTTP upload entity found')) | 335 raise failure.Failure( |
269 | 336 exceptions.NotFound(u"No HTTP upload entity found") |
270 iq_elt = client.IQ('get') | 337 ) |
271 iq_elt['to'] = upload_jid.full() | 338 |
272 request_elt = iq_elt.addElement((NS_HTTP_UPLOAD, 'request')) | 339 iq_elt = client.IQ("get") |
273 request_elt.addElement('filename', content=filename) | 340 iq_elt["to"] = upload_jid.full() |
274 request_elt.addElement('size', content=unicode(size)) | 341 request_elt = iq_elt.addElement((NS_HTTP_UPLOAD, "request")) |
342 request_elt.addElement("filename", content=filename) | |
343 request_elt.addElement("size", content=unicode(size)) | |
275 if content_type is not None: | 344 if content_type is not None: |
276 request_elt.addElement('content-type', content=content_type) | 345 request_elt.addElement("content-type", content=content_type) |
277 | 346 |
278 d = iq_elt.send() | 347 d = iq_elt.send() |
279 d.addCallback(self._gotSlot, client) | 348 d.addCallback(self._gotSlot, client) |
280 | 349 |
281 return d | 350 return d |
282 | 351 |
283 | 352 |
284 class XEP_0363_handler(XMPPHandler): | 353 class XEP_0363_handler(XMPPHandler): |
285 implements(iwokkel.IDisco) | 354 implements(iwokkel.IDisco) |
286 | 355 |
287 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 356 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
288 return [disco.DiscoFeature(NS_HTTP_UPLOAD)] | 357 return [disco.DiscoFeature(NS_HTTP_UPLOAD)] |
289 | 358 |
290 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 359 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |
291 return [] | 360 return [] |