comparison sat/plugins/plugin_xep_0363.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 412b99c29d83
children
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
84 Slot=Slot 84 Slot=Slot
85 85
86 def __init__(self, host): 86 def __init__(self, host):
87 log.info(_("plugin HTTP File Upload initialization")) 87 log.info(_("plugin HTTP File Upload initialization"))
88 self.host = host 88 self.host = host
89 host.bridge.addMethod( 89 host.bridge.add_method(
90 "fileHTTPUpload", 90 "file_http_upload",
91 ".plugin", 91 ".plugin",
92 in_sign="sssbs", 92 in_sign="sssbs",
93 out_sign="", 93 out_sign="",
94 method=self._file_http_upload, 94 method=self._file_http_upload,
95 ) 95 )
96 host.bridge.addMethod( 96 host.bridge.add_method(
97 "fileHTTPUploadGetSlot", 97 "file_http_upload_get_slot",
98 ".plugin", 98 ".plugin",
99 in_sign="sisss", 99 in_sign="sisss",
100 out_sign="(ssaa{ss})", 100 out_sign="(ssaa{ss})",
101 method=self._getSlot, 101 method=self._get_slot,
102 async_=True, 102 async_=True,
103 ) 103 )
104 host.plugins["UPLOAD"].register( 104 host.plugins["UPLOAD"].register(
105 "HTTP Upload", self.getHTTPUploadEntity, self.file_http_upload 105 "HTTP Upload", self.get_http_upload_entity, self.file_http_upload
106 ) 106 )
107 # list of callbacks used when a request is done to a component 107 # list of callbacks used when a request is done to a component
108 self.handlers = [] 108 self.handlers = []
109 # XXX: there is not yet official short name, so we use "http_upload" 109 # XXX: there is not yet official short name, so we use "http_upload"
110 host.registerNamespace("http_upload", NS_HTTP_UPLOAD) 110 host.register_namespace("http_upload", NS_HTTP_UPLOAD)
111 111
112 def getHandler(self, client): 112 def get_handler(self, client):
113 return XEP_0363_handler(self) 113 return XEP_0363_handler(self)
114 114
115 def registerHandler(self, callback, priority=0): 115 def register_handler(self, callback, priority=0):
116 """Register a request handler 116 """Register a request handler
117 117
118 @param callack: method to call when a request is done 118 @param callack: method to call when a request is done
119 the callback must return a Slot if the request is handled, 119 the callback must return a Slot if the request is handled,
120 otherwise, other callbacks will be tried. 120 otherwise, other callbacks will be tried.
125 assert callback not in self.handlers 125 assert callback not in self.handlers
126 req_handler = RequestHandler(callback, priority) 126 req_handler = RequestHandler(callback, priority)
127 self.handlers.append(req_handler) 127 self.handlers.append(req_handler)
128 self.handlers.sort(key=lambda handler: handler.priority, reverse=True) 128 self.handlers.sort(key=lambda handler: handler.priority, reverse=True)
129 129
130 def getFileTooLargeElt(self, max_size: int) -> domish.Element: 130 def get_file_too_large_elt(self, max_size: int) -> domish.Element:
131 """Generate <file-too-large> app condition for errors""" 131 """Generate <file-too-large> app condition for errors"""
132 file_too_large_elt = domish.Element((NS_HTTP_UPLOAD, "file-too-large")) 132 file_too_large_elt = domish.Element((NS_HTTP_UPLOAD, "file-too-large"))
133 file_too_large_elt.addElement("max-file-size", str(max_size)) 133 file_too_large_elt.addElement("max-file-size", str(max_size))
134 return file_too_large_elt 134 return file_too_large_elt
135 135
136 async def getHTTPUploadEntity(self, client, upload_jid=None): 136 async def get_http_upload_entity(self, client, upload_jid=None):
137 """Get HTTP upload capable entity 137 """Get HTTP upload capable entity
138 138
139 upload_jid is checked, then its components 139 upload_jid is checked, then its components
140 @param upload_jid(None, jid.JID): entity to check 140 @param upload_jid(None, jid.JID): entity to check
141 @return(D(jid.JID)): first HTTP upload capable entity 141 @return(D(jid.JID)): first HTTP upload capable entity
142 @raise exceptions.NotFound: no entity found 142 @raise exceptions.NotFound: no entity found
143 """ 143 """
144 try: 144 try:
145 entity = client.http_upload_service 145 entity = client.http_upload_service
146 except AttributeError: 146 except AttributeError:
147 found_entities = await self.host.findFeaturesSet(client, (NS_HTTP_UPLOAD,)) 147 found_entities = await self.host.find_features_set(client, (NS_HTTP_UPLOAD,))
148 try: 148 try:
149 entity = client.http_upload_service = next(iter(found_entities)) 149 entity = client.http_upload_service = next(iter(found_entities))
150 except StopIteration: 150 except StopIteration:
151 entity = client.http_upload_service = None 151 entity = client.http_upload_service = None
152 152
156 return entity 156 return entity
157 157
158 def _file_http_upload(self, filepath, filename="", upload_jid="", 158 def _file_http_upload(self, filepath, filename="", upload_jid="",
159 ignore_tls_errors=False, profile=C.PROF_KEY_NONE): 159 ignore_tls_errors=False, profile=C.PROF_KEY_NONE):
160 assert os.path.isabs(filepath) and os.path.isfile(filepath) 160 assert os.path.isabs(filepath) and os.path.isfile(filepath)
161 client = self.host.getClient(profile) 161 client = self.host.get_client(profile)
162 return defer.ensureDeferred(self.file_http_upload( 162 return defer.ensureDeferred(self.file_http_upload(
163 client, 163 client,
164 filepath, 164 filepath,
165 filename or None, 165 filename or None,
166 jid.JID(upload_jid) if upload_jid else None, 166 jid.JID(upload_jid) if upload_jid else None,
202 self.host.trigger.point( 202 self.host.trigger.point(
203 "XEP-0363_upload_pre_slot", client, extra, file_metadata, 203 "XEP-0363_upload_pre_slot", client, extra, file_metadata,
204 triggers_no_cancel=True 204 triggers_no_cancel=True
205 ) 205 )
206 try: 206 try:
207 slot = await self.getSlot( 207 slot = await self.get_slot(
208 client, file_metadata["filename"], file_metadata["size"], 208 client, file_metadata["filename"], file_metadata["size"],
209 upload_jid=upload_jid 209 upload_jid=upload_jid
210 ) 210 )
211 except Exception as e: 211 except Exception as e:
212 log.warning(_("Can't get upload slot: {reason}").format(reason=e)) 212 log.warning(_("Can't get upload slot: {reason}").format(reason=e))
233 name = name.encode('utf-8') 233 name = name.encode('utf-8')
234 value = value.encode('utf-8') 234 value = value.encode('utf-8')
235 headers[name] = value 235 headers[name] = value
236 236
237 237
238 await self.host.trigger.asyncPoint( 238 await self.host.trigger.async_point(
239 "XEP-0363_upload", client, extra, sat_file, file_producer, slot, 239 "XEP-0363_upload", client, extra, sat_file, file_producer, slot,
240 triggers_no_cancel=True) 240 triggers_no_cancel=True)
241 241
242 download_d = agent.request( 242 download_d = agent.request(
243 b"PUT", 243 b"PUT",
257 257
258 def _upload_cb(self, __, sat_file, slot): 258 def _upload_cb(self, __, sat_file, slot):
259 """Called once file is successfully uploaded 259 """Called once file is successfully uploaded
260 260
261 @param sat_file(SatFile): file used for the upload 261 @param sat_file(SatFile): file used for the upload
262 should be closed, but it is needed to send the progressFinished signal 262 should be closed, but it is needed to send the progress_finished signal
263 @param slot(Slot): put/get urls 263 @param slot(Slot): put/get urls
264 """ 264 """
265 log.info(f"HTTP upload finished ({slot.get})") 265 log.info(f"HTTP upload finished ({slot.get})")
266 sat_file.progressFinished({"url": slot.get}) 266 sat_file.progress_finished({"url": slot.get})
267 return slot.get 267 return slot.get
268 268
269 def _upload_eb(self, failure_, sat_file): 269 def _upload_eb(self, failure_, sat_file):
270 """Called on unsuccessful upload 270 """Called on unsuccessful upload
271 271
272 @param sat_file(SatFile): file used for the upload 272 @param sat_file(SatFile): file used for the upload
273 should be closed, be is needed to send the progressError signal 273 should be closed, be is needed to send the progress_error signal
274 """ 274 """
275 try: 275 try:
276 wrapped_fail = failure_.value.reasons[0] 276 wrapped_fail = failure_.value.reasons[0]
277 except (AttributeError, IndexError) as e: 277 except (AttributeError, IndexError) as e:
278 log.warning(_("upload failed: {reason}").format(reason=e)) 278 log.warning(_("upload failed: {reason}").format(reason=e))
279 sat_file.progressError(str(failure_)) 279 sat_file.progress_error(str(failure_))
280 else: 280 else:
281 if wrapped_fail.check(sat_web.SSLError): 281 if wrapped_fail.check(sat_web.SSLError):
282 msg = "TLS validation error, can't connect to HTTPS server" 282 msg = "TLS validation error, can't connect to HTTPS server"
283 else: 283 else:
284 msg = "can't upload file" 284 msg = "can't upload file"
285 log.warning(msg + ": " + str(wrapped_fail.value)) 285 log.warning(msg + ": " + str(wrapped_fail.value))
286 sat_file.progressError(msg) 286 sat_file.progress_error(msg)
287 raise failure_ 287 raise failure_
288 288
289 def _getSlot(self, filename, size, content_type, upload_jid, 289 def _get_slot(self, filename, size, content_type, upload_jid,
290 profile_key=C.PROF_KEY_NONE): 290 profile_key=C.PROF_KEY_NONE):
291 """Get an upload slot 291 """Get an upload slot
292 292
293 This method can be used when uploading is done by the frontend 293 This method can be used when uploading is done by the frontend
294 @param filename(unicode): name of the file to upload 294 @param filename(unicode): name of the file to upload
295 @param size(int): size of the file (must be non null) 295 @param size(int): size of the file (must be non null)
296 @param upload_jid(str, ''): HTTP upload capable entity 296 @param upload_jid(str, ''): HTTP upload capable entity
297 @param content_type(unicode, None): MIME type of the content 297 @param content_type(unicode, None): MIME type of the content
298 empty string or None to guess automatically 298 empty string or None to guess automatically
299 """ 299 """
300 client = self.host.getClient(profile_key) 300 client = self.host.get_client(profile_key)
301 filename = filename.replace("/", "_") 301 filename = filename.replace("/", "_")
302 d = defer.ensureDeferred(self.getSlot( 302 d = defer.ensureDeferred(self.get_slot(
303 client, filename, size, content_type or None, jid.JID(upload_jid) or None 303 client, filename, size, content_type or None, jid.JID(upload_jid) or None
304 )) 304 ))
305 d.addCallback(lambda slot: (slot.get, slot.put, slot.headers)) 305 d.addCallback(lambda slot: (slot.get, slot.put, slot.headers))
306 return d 306 return d
307 307
308 async def getSlot(self, client, filename, size, content_type=None, upload_jid=None): 308 async def get_slot(self, client, filename, size, content_type=None, upload_jid=None):
309 """Get a slot (i.e. download/upload links) 309 """Get a slot (i.e. download/upload links)
310 310
311 @param filename(unicode): name to use for the upload 311 @param filename(unicode): name to use for the upload
312 @param size(int): size of the file to upload (must be >0) 312 @param size(int): size of the file to upload (must be >0)
313 @param content_type(None, unicode): MIME type of the content 313 @param content_type(None, unicode): MIME type of the content
325 325
326 if upload_jid is None: 326 if upload_jid is None:
327 try: 327 try:
328 upload_jid = client.http_upload_service 328 upload_jid = client.http_upload_service
329 except AttributeError: 329 except AttributeError:
330 found_entity = await self.getHTTPUploadEntity(client) 330 found_entity = await self.get_http_upload_entity(client)
331 return await self.getSlot( 331 return await self.get_slot(
332 client, filename, size, content_type, found_entity) 332 client, filename, size, content_type, found_entity)
333 else: 333 else:
334 if upload_jid is None: 334 if upload_jid is None:
335 raise exceptions.NotFound("No HTTP upload entity found") 335 raise exceptions.NotFound("No HTTP upload entity found")
336 336
372 372
373 return Slot(put=put_url, get=get_url, headers=headers) 373 return Slot(put=put_url, get=get_url, headers=headers)
374 374
375 # component 375 # component
376 376
377 def onComponentRequest(self, iq_elt, client): 377 def on_component_request(self, iq_elt, client):
378 iq_elt.handled=True 378 iq_elt.handled=True
379 defer.ensureDeferred(self.handleComponentRequest(client, iq_elt)) 379 defer.ensureDeferred(self.handle_component_request(client, iq_elt))
380 380
381 async def handleComponentRequest(self, client, iq_elt): 381 async def handle_component_request(self, client, iq_elt):
382 try: 382 try:
383 request_elt = next(iq_elt.elements(NS_HTTP_UPLOAD, "request")) 383 request_elt = next(iq_elt.elements(NS_HTTP_UPLOAD, "request"))
384 request = UploadRequest( 384 request = UploadRequest(
385 from_=jid.JID(iq_elt['from']), 385 from_=jid.JID(iq_elt['from']),
386 filename=parse.quote(request_elt['filename'].replace('/', '_'), safe=''), 386 filename=parse.quote(request_elt['filename'].replace('/', '_'), safe=''),
393 393
394 err = None 394 err = None
395 395
396 for handler in self.handlers: 396 for handler in self.handlers:
397 try: 397 try:
398 slot = await utils.asDeferred(handler.callback, client, request) 398 slot = await utils.as_deferred(handler.callback, client, request)
399 except error.StanzaError as e: 399 except error.StanzaError as e:
400 log.warning( 400 log.warning(
401 "a stanza error has been raised while processing HTTP Upload of " 401 "a stanza error has been raised while processing HTTP Upload of "
402 f"request: {e}" 402 f"request: {e}"
403 ) 403 )
434 434
435 def connectionInitialized(self): 435 def connectionInitialized(self):
436 if ((self.parent.is_component 436 if ((self.parent.is_component
437 and PLUGIN_INFO[C.PI_IMPORT_NAME] in self.parent.enabled_features)): 437 and PLUGIN_INFO[C.PI_IMPORT_NAME] in self.parent.enabled_features)):
438 self.xmlstream.addObserver( 438 self.xmlstream.addObserver(
439 IQ_HTTP_UPLOAD_REQUEST, self.plugin_parent.onComponentRequest, 439 IQ_HTTP_UPLOAD_REQUEST, self.plugin_parent.on_component_request,
440 client=self.parent 440 client=self.parent
441 ) 441 )
442 442
443 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): 443 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
444 if ((self.parent.is_component 444 if ((self.parent.is_component