Mercurial > libervia-backend
comparison sat/plugins/plugin_comp_file_sharing_management.py @ 3541:888109774673
core: various changes and fixes to work with new storage and D-Bus bridge:
- fixes coroutines handling in various places
- fixes types which are not serialised by Tx DBus
- XEP-0384: call storage methods in main thread in XEP: Python OMEMO's Promise use thread
which prevent the use of AsyncIO loop. To work around that, callLater is used to launch
storage method in main thread. This is a temporary workaround, as Python OMEMO should
get rid of Promise implementation and threads soon.
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 03 Jun 2021 15:21:43 +0200 |
parents | a8259a1f89b2 |
children | 7af29260ecb8 |
comparison
equal
deleted
inserted
replaced
3540:aa58451b77ba | 3541:888109774673 |
---|---|
147 form.addField(field) | 147 form.addField(field) |
148 | 148 |
149 payload = form.toElement() | 149 payload = form.toElement() |
150 return payload, status, None, None | 150 return payload, status, None, None |
151 | 151 |
152 @defer.inlineCallbacks | 152 async def _getFileData(self, client, session_data, command_form): |
153 def _getFileData(self, client, session_data, command_form): | |
154 """Retrieve field requested in root form | 153 """Retrieve field requested in root form |
155 | 154 |
156 "found_file" will also be set in session_data | 155 "found_file" will also be set in session_data |
157 @param command_form(data_form.Form): response to root form | 156 @param command_form(data_form.Form): response to root form |
158 @return (D(dict)): found file data | 157 @return (D(dict)): found file data |
175 | 174 |
176 # TODO: if parent_path and basename are empty, we ask for root directory | 175 # TODO: if parent_path and basename are empty, we ask for root directory |
177 # this must be managed | 176 # this must be managed |
178 | 177 |
179 try: | 178 try: |
180 found_files = yield self.host.memory.getFiles( | 179 found_files = await self.host.memory.getFiles( |
181 client, requestor_bare, path=parent_path, name=basename, | 180 client, requestor_bare, path=parent_path, name=basename, |
182 namespace=namespace) | 181 namespace=namespace) |
183 found_file = found_files[0] | 182 found_file = found_files[0] |
184 except (exceptions.NotFound, IndexError): | 183 except (exceptions.NotFound, IndexError): |
185 raise WorkflowError(self._err(_("file not found"))) | 184 raise WorkflowError(self._err(_("file not found"))) |
191 log.warning(_("Only owner can manage files")) | 190 log.warning(_("Only owner can manage files")) |
192 raise WorkflowError(self._err(_("forbidden"))) | 191 raise WorkflowError(self._err(_("forbidden"))) |
193 | 192 |
194 session_data['found_file'] = found_file | 193 session_data['found_file'] = found_file |
195 session_data['namespace'] = namespace | 194 session_data['namespace'] = namespace |
196 defer.returnValue(found_file) | 195 return found_file |
197 | 196 |
198 def _updateReadPermission(self, access, allowed_jids): | 197 def _updateReadPermission(self, access, allowed_jids): |
199 if not allowed_jids: | 198 if not allowed_jids: |
200 if C.ACCESS_PERM_READ in access: | 199 if C.ACCESS_PERM_READ in access: |
201 del access[C.ACCESS_PERM_READ] | 200 del access[C.ACCESS_PERM_READ] |
207 access[C.ACCESS_PERM_READ] = { | 206 access[C.ACCESS_PERM_READ] = { |
208 "type": C.ACCESS_TYPE_WHITELIST, | 207 "type": C.ACCESS_TYPE_WHITELIST, |
209 "jids": [j.full() for j in allowed_jids] | 208 "jids": [j.full() for j in allowed_jids] |
210 } | 209 } |
211 | 210 |
212 @defer.inlineCallbacks | 211 async def _updateDir(self, client, requestor, namespace, file_data, allowed_jids): |
213 def _updateDir(self, client, requestor, namespace, file_data, allowed_jids): | |
214 """Recursively update permission of a directory and all subdirectories | 212 """Recursively update permission of a directory and all subdirectories |
215 | 213 |
216 @param file_data(dict): metadata of the file | 214 @param file_data(dict): metadata of the file |
217 @param allowed_jids(list[jid.JID]): list of entities allowed to read the file | 215 @param allowed_jids(list[jid.JID]): list of entities allowed to read the file |
218 """ | 216 """ |
219 assert file_data['type'] == C.FILE_TYPE_DIRECTORY | 217 assert file_data['type'] == C.FILE_TYPE_DIRECTORY |
220 files_data = yield self.host.memory.getFiles( | 218 files_data = await self.host.memory.getFiles( |
221 client, requestor, parent=file_data['id'], namespace=namespace) | 219 client, requestor, parent=file_data['id'], namespace=namespace) |
222 | 220 |
223 for file_data in files_data: | 221 for file_data in files_data: |
224 if not file_data['access'].get(C.ACCESS_PERM_READ, {}): | 222 if not file_data['access'].get(C.ACCESS_PERM_READ, {}): |
225 log.debug("setting {perm} read permission for {name}".format( | 223 log.debug("setting {perm} read permission for {name}".format( |
226 perm=allowed_jids, name=file_data['name'])) | 224 perm=allowed_jids, name=file_data['name'])) |
227 yield self.host.memory.fileUpdate( | 225 await self.host.memory.fileUpdate( |
228 file_data['id'], 'access', | 226 file_data['id'], 'access', |
229 partial(self._updateReadPermission, allowed_jids=allowed_jids)) | 227 partial(self._updateReadPermission, allowed_jids=allowed_jids)) |
230 if file_data['type'] == C.FILE_TYPE_DIRECTORY: | 228 if file_data['type'] == C.FILE_TYPE_DIRECTORY: |
231 yield self._updateDir(client, requestor, namespace, file_data, 'PUBLIC') | 229 await self._updateDir(client, requestor, namespace, file_data, 'PUBLIC') |
232 | 230 |
233 @defer.inlineCallbacks | 231 async def _onChangeFile(self, client, command_elt, session_data, action, node): |
234 def _onChangeFile(self, client, command_elt, session_data, action, node): | |
235 try: | 232 try: |
236 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) | 233 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) |
237 command_form = data_form.Form.fromElement(x_elt) | 234 command_form = data_form.Form.fromElement(x_elt) |
238 except StopIteration: | 235 except StopIteration: |
239 command_form = None | 236 command_form = None |
242 requestor = session_data['requestor'] | 239 requestor = session_data['requestor'] |
243 requestor_bare = requestor.userhostJID() | 240 requestor_bare = requestor.userhostJID() |
244 | 241 |
245 if command_form is None or len(command_form.fields) == 0: | 242 if command_form is None or len(command_form.fields) == 0: |
246 # root request | 243 # root request |
247 defer.returnValue(self._getRootArgs()) | 244 return self._getRootArgs() |
248 | 245 |
249 elif found_file is None: | 246 elif found_file is None: |
250 # file selected, we retrieve it and ask for permissions | 247 # file selected, we retrieve it and ask for permissions |
251 try: | 248 try: |
252 found_file = yield self._getFileData(client, session_data, command_form) | 249 found_file = await self._getFileData(client, session_data, command_form) |
253 except WorkflowError as e: | 250 except WorkflowError as e: |
254 defer.returnValue(e.err_args) | 251 return e.err_args |
255 | 252 |
256 # management request | 253 # management request |
257 if found_file['type'] == C.FILE_TYPE_DIRECTORY: | 254 if found_file['type'] == C.FILE_TYPE_DIRECTORY: |
258 instructions = D_("Please select permissions for this directory") | 255 instructions = D_("Please select permissions for this directory") |
259 else: | 256 else: |
282 ) | 279 ) |
283 form.addField(field) | 280 form.addField(field) |
284 | 281 |
285 status = self._c.STATUS.EXECUTING | 282 status = self._c.STATUS.EXECUTING |
286 payload = form.toElement() | 283 payload = form.toElement() |
287 defer.returnValue((payload, status, None, None)) | 284 return (payload, status, None, None) |
288 | 285 |
289 else: | 286 else: |
290 # final phase, we'll do permission change here | 287 # final phase, we'll do permission change here |
291 try: | 288 try: |
292 read_allowed = command_form.fields['read_allowed'] | 289 read_allowed = command_form.fields['read_allowed'] |
305 log.warning(_("Can't use read_allowed values: {reason}").format( | 302 log.warning(_("Can't use read_allowed values: {reason}").format( |
306 reason=e)) | 303 reason=e)) |
307 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) | 304 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) |
308 | 305 |
309 if found_file['type'] == C.FILE_TYPE_FILE: | 306 if found_file['type'] == C.FILE_TYPE_FILE: |
310 yield self.host.memory.fileUpdate( | 307 await self.host.memory.fileUpdate( |
311 found_file['id'], 'access', | 308 found_file['id'], 'access', |
312 partial(self._updateReadPermission, allowed_jids=allowed_jids)) | 309 partial(self._updateReadPermission, allowed_jids=allowed_jids)) |
313 else: | 310 else: |
314 try: | 311 try: |
315 recursive = command_form.fields['recursive'] | 312 recursive = command_form.fields['recursive'] |
316 except KeyError: | 313 except KeyError: |
317 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) | 314 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) |
318 yield self.host.memory.fileUpdate( | 315 await self.host.memory.fileUpdate( |
319 found_file['id'], 'access', | 316 found_file['id'], 'access', |
320 partial(self._updateReadPermission, allowed_jids=allowed_jids)) | 317 partial(self._updateReadPermission, allowed_jids=allowed_jids)) |
321 if recursive: | 318 if recursive: |
322 # we set all file under the directory as public (if they haven't | 319 # we set all file under the directory as public (if they haven't |
323 # already a permission set), so allowed entities of root directory | 320 # already a permission set), so allowed entities of root directory |
324 # can read them. | 321 # can read them. |
325 namespace = session_data['namespace'] | 322 namespace = session_data['namespace'] |
326 yield self._updateDir( | 323 await self._updateDir( |
327 client, requestor_bare, namespace, found_file, 'PUBLIC') | 324 client, requestor_bare, namespace, found_file, 'PUBLIC') |
328 | 325 |
329 # job done, we can end the session | 326 # job done, we can end the session |
330 status = self._c.STATUS.COMPLETED | 327 status = self._c.STATUS.COMPLETED |
331 payload = None | 328 payload = None |
332 note = (self._c.NOTE.INFO, _("management session done")) | 329 note = (self._c.NOTE.INFO, _("management session done")) |
333 defer.returnValue((payload, status, None, note)) | 330 return (payload, status, None, note) |
334 | 331 |
335 @defer.inlineCallbacks | 332 async def _onDeleteFile(self, client, command_elt, session_data, action, node): |
336 def _onDeleteFile(self, client, command_elt, session_data, action, node): | |
337 try: | 333 try: |
338 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) | 334 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) |
339 command_form = data_form.Form.fromElement(x_elt) | 335 command_form = data_form.Form.fromElement(x_elt) |
340 except StopIteration: | 336 except StopIteration: |
341 command_form = None | 337 command_form = None |
344 requestor = session_data['requestor'] | 340 requestor = session_data['requestor'] |
345 requestor_bare = requestor.userhostJID() | 341 requestor_bare = requestor.userhostJID() |
346 | 342 |
347 if command_form is None or len(command_form.fields) == 0: | 343 if command_form is None or len(command_form.fields) == 0: |
348 # root request | 344 # root request |
349 defer.returnValue(self._getRootArgs()) | 345 return self._getRootArgs() |
350 | 346 |
351 elif found_file is None: | 347 elif found_file is None: |
352 # file selected, we need confirmation before actually deleting | 348 # file selected, we need confirmation before actually deleting |
353 try: | 349 try: |
354 found_file = yield self._getFileData(client, session_data, command_form) | 350 found_file = await self._getFileData(client, session_data, command_form) |
355 except WorkflowError as e: | 351 except WorkflowError as e: |
356 defer.returnValue(e.err_args) | 352 return e.err_args |
357 if found_file['type'] == C.FILE_TYPE_DIRECTORY: | 353 if found_file['type'] == C.FILE_TYPE_DIRECTORY: |
358 msg = D_("Are you sure to delete directory {name} and all files and " | 354 msg = D_("Are you sure to delete directory {name} and all files and " |
359 "directories under it?").format(name=found_file['name']) | 355 "directories under it?").format(name=found_file['name']) |
360 else: | 356 else: |
361 msg = D_("Are you sure to delete file {name}?" | 357 msg = D_("Are you sure to delete file {name}?" |
368 desc="check this box to confirm" | 364 desc="check this box to confirm" |
369 ) | 365 ) |
370 form.addField(field) | 366 form.addField(field) |
371 status = self._c.STATUS.EXECUTING | 367 status = self._c.STATUS.EXECUTING |
372 payload = form.toElement() | 368 payload = form.toElement() |
373 defer.returnValue((payload, status, None, None)) | 369 return (payload, status, None, None) |
374 | 370 |
375 else: | 371 else: |
376 # final phase, we'll do deletion here | 372 # final phase, we'll do deletion here |
377 try: | 373 try: |
378 confirmed = C.bool(command_form.fields['confirm'].value) | 374 confirmed = C.bool(command_form.fields['confirm'].value) |
380 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) | 376 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) |
381 if not confirmed: | 377 if not confirmed: |
382 note = None | 378 note = None |
383 else: | 379 else: |
384 recursive = found_file['type'] == C.FILE_TYPE_DIRECTORY | 380 recursive = found_file['type'] == C.FILE_TYPE_DIRECTORY |
385 yield self.host.memory.fileDelete( | 381 await self.host.memory.fileDelete( |
386 client, requestor_bare, found_file['id'], recursive) | 382 client, requestor_bare, found_file['id'], recursive) |
387 note = (self._c.NOTE.INFO, _("file deleted")) | 383 note = (self._c.NOTE.INFO, _("file deleted")) |
388 status = self._c.STATUS.COMPLETED | 384 status = self._c.STATUS.COMPLETED |
389 payload = None | 385 payload = None |
390 defer.returnValue((payload, status, None, note)) | 386 return (payload, status, None, note) |
391 | 387 |
392 def _updateThumbs(self, extra, thumbnails): | 388 def _updateThumbs(self, extra, thumbnails): |
393 extra[C.KEY_THUMBNAILS] = thumbnails | 389 extra[C.KEY_THUMBNAILS] = thumbnails |
394 | 390 |
395 @defer.inlineCallbacks | 391 async def _genThumbs(self, client, requestor, namespace, file_data): |
396 def _genThumbs(self, client, requestor, namespace, file_data): | |
397 """Recursively generate thumbnails | 392 """Recursively generate thumbnails |
398 | 393 |
399 @param file_data(dict): metadata of the file | 394 @param file_data(dict): metadata of the file |
400 """ | 395 """ |
401 if file_data['type'] == C.FILE_TYPE_DIRECTORY: | 396 if file_data['type'] == C.FILE_TYPE_DIRECTORY: |
402 sub_files_data = yield self.host.memory.getFiles( | 397 sub_files_data = await self.host.memory.getFiles( |
403 client, requestor, parent=file_data['id'], namespace=namespace) | 398 client, requestor, parent=file_data['id'], namespace=namespace) |
404 for sub_file_data in sub_files_data: | 399 for sub_file_data in sub_files_data: |
405 yield self._genThumbs(client, requestor, namespace, sub_file_data) | 400 await self._genThumbs(client, requestor, namespace, sub_file_data) |
406 | 401 |
407 elif file_data['type'] == C.FILE_TYPE_FILE: | 402 elif file_data['type'] == C.FILE_TYPE_FILE: |
408 media_type = file_data['media_type'] | 403 media_type = file_data['media_type'] |
409 file_path = os.path.join(self.files_path, file_data['file_hash']) | 404 file_path = os.path.join(self.files_path, file_data['file_hash']) |
410 if media_type == 'image': | 405 if media_type == 'image': |
411 thumbnails = [] | 406 thumbnails = [] |
412 | 407 |
413 for max_thumb_size in self._t.SIZES: | 408 for max_thumb_size in self._t.SIZES: |
414 try: | 409 try: |
415 thumb_size, thumb_id = yield self._t.generateThumbnail( | 410 thumb_size, thumb_id = await self._t.generateThumbnail( |
416 file_path, | 411 file_path, |
417 max_thumb_size, | 412 max_thumb_size, |
418 # we keep thumbnails for 6 months | 413 # we keep thumbnails for 6 months |
419 60 * 60 * 24 * 31 * 6, | 414 60 * 60 * 24 * 31 * 6, |
420 ) | 415 ) |
422 log.warning(_("Can't create thumbnail: {reason}") | 417 log.warning(_("Can't create thumbnail: {reason}") |
423 .format(reason=e)) | 418 .format(reason=e)) |
424 break | 419 break |
425 thumbnails.append({"id": thumb_id, "size": thumb_size}) | 420 thumbnails.append({"id": thumb_id, "size": thumb_size}) |
426 | 421 |
427 yield self.host.memory.fileUpdate( | 422 await self.host.memory.fileUpdate( |
428 file_data['id'], 'extra', | 423 file_data['id'], 'extra', |
429 partial(self._updateThumbs, thumbnails=thumbnails)) | 424 partial(self._updateThumbs, thumbnails=thumbnails)) |
430 | 425 |
431 log.info("thumbnails for [{file_name}] generated" | 426 log.info("thumbnails for [{file_name}] generated" |
432 .format(file_name=file_data['name'])) | 427 .format(file_name=file_data['name'])) |
433 | 428 |
434 else: | 429 else: |
435 log.warning("unmanaged file type: {type_}".format(type_=file_data['type'])) | 430 log.warning("unmanaged file type: {type_}".format(type_=file_data['type'])) |
436 | 431 |
437 @defer.inlineCallbacks | 432 async def _onGenThumbnails(self, client, command_elt, session_data, action, node): |
438 def _onGenThumbnails(self, client, command_elt, session_data, action, node): | |
439 try: | 433 try: |
440 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) | 434 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x")) |
441 command_form = data_form.Form.fromElement(x_elt) | 435 command_form = data_form.Form.fromElement(x_elt) |
442 except StopIteration: | 436 except StopIteration: |
443 command_form = None | 437 command_form = None |
445 found_file = session_data.get('found_file') | 439 found_file = session_data.get('found_file') |
446 requestor = session_data['requestor'] | 440 requestor = session_data['requestor'] |
447 | 441 |
448 if command_form is None or len(command_form.fields) == 0: | 442 if command_form is None or len(command_form.fields) == 0: |
449 # root request | 443 # root request |
450 defer.returnValue(self._getRootArgs()) | 444 return self._getRootArgs() |
451 | 445 |
452 elif found_file is None: | 446 elif found_file is None: |
453 # file selected, we retrieve it and ask for permissions | 447 # file selected, we retrieve it and ask for permissions |
454 try: | 448 try: |
455 found_file = yield self._getFileData(client, session_data, command_form) | 449 found_file = await self._getFileData(client, session_data, command_form) |
456 except WorkflowError as e: | 450 except WorkflowError as e: |
457 defer.returnValue(e.err_args) | 451 return e.err_args |
458 | 452 |
459 log.info("Generating thumbnails as requested") | 453 log.info("Generating thumbnails as requested") |
460 yield self._genThumbs(client, requestor, found_file['namespace'], found_file) | 454 await self._genThumbs(client, requestor, found_file['namespace'], found_file) |
461 | 455 |
462 # job done, we can end the session | 456 # job done, we can end the session |
463 status = self._c.STATUS.COMPLETED | 457 status = self._c.STATUS.COMPLETED |
464 payload = None | 458 payload = None |
465 note = (self._c.NOTE.INFO, _("thumbnails generated")) | 459 note = (self._c.NOTE.INFO, _("thumbnails generated")) |
466 defer.returnValue((payload, status, None, note)) | 460 return (payload, status, None, note) |
467 | 461 |
468 async def _onQuota(self, client, command_elt, session_data, action, node): | 462 async def _onQuota(self, client, command_elt, session_data, action, node): |
469 requestor = session_data['requestor'] | 463 requestor = session_data['requestor'] |
470 quota = self.host.plugins["file_sharing"].getQuota(client, requestor) | 464 quota = self.host.plugins["file_sharing"].getQuota(client, requestor) |
471 try: | 465 try: |