Mercurial > libervia-backend
comparison sat_frontends/jp/cmd_file.py @ 3094:c3cb18236bdf
jp (file): new `get` command + encryption with upload:
- new file/get command let download a file from URL. It handles `aesgcm:` scheme by
thanks to backend decryption on the fly.
- new `--encrypt` option for upload. When used, the file will be encrypted using AES-GCM
algorithm, and the `aesgcm:` URL will be returned
- for both commands, the XMLUI note is displayed in case of error
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 20 Dec 2019 12:28:04 +0100 |
parents | e75024e41f81 |
children | 040ca99e25fe |
comparison
equal
deleted
inserted
replaced
3093:d909473a76cc | 3094:c3cb18236bdf |
---|---|
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
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 | 20 |
21 from . import base | 21 from . import base |
22 from . import xmlui_manager | |
22 import sys | 23 import sys |
23 import os | 24 import os |
24 import os.path | 25 import os.path |
25 import tarfile | 26 import tarfile |
26 from sat.core.i18n import _ | 27 from sat.core.i18n import _ |
27 from sat.tools.common import data_format | 28 from sat.tools.common import data_format |
28 from sat_frontends.jp.constants import Const as C | 29 from sat_frontends.jp.constants import Const as C |
29 from sat_frontends.jp import common | 30 from sat_frontends.jp import common |
30 from sat_frontends.tools import jid | 31 from sat_frontends.tools import jid |
31 from sat.tools.common.ansi import ANSI as A | 32 from sat.tools.common.ansi import ANSI as A |
33 from urllib.parse import urlparse | |
34 from pathlib import Path | |
32 import tempfile | 35 import tempfile |
33 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI | 36 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI |
34 import json | 37 import json |
35 | 38 |
36 __commands__ = ["File"] | 39 __commands__ = ["File"] |
252 path = os.path.join(path, self.filename) | 255 path = os.path.join(path, self.filename) |
253 else: | 256 else: |
254 path = os.path.abspath(self.filename) | 257 path = os.path.abspath(self.filename) |
255 | 258 |
256 if os.path.exists(path) and not self.args.force: | 259 if os.path.exists(path) and not self.args.force: |
257 message = _(f"File {path} already exists! Do you want to overwrite?") | 260 message = _("File {path} already exists! Do you want to overwrite?").format( |
261 path = path) | |
258 await self.host.confirmOrQuit(message, _("file request cancelled")) | 262 await self.host.confirmOrQuit(message, _("file request cancelled")) |
259 | 263 |
260 self.full_dest_jid = await self.host.get_full_jid(self.args.jid) | 264 self.full_dest_jid = await self.host.get_full_jid(self.args.jid) |
261 extra = {} | 265 extra = {} |
262 if self.args.path: | 266 if self.args.path: |
411 self.host.quit_on_progress_end = False | 415 self.host.quit_on_progress_end = False |
412 self.disp(_("waiting for incoming file request"), 2) | 416 self.disp(_("waiting for incoming file request"), 2) |
413 await self.start_answering() | 417 await self.start_answering() |
414 | 418 |
415 | 419 |
420 class Get(base.CommandBase): | |
421 | |
422 def __init__(self, host): | |
423 super(Get, self).__init__( | |
424 host, "get", use_progress=True, use_verbose=True, | |
425 help=_("download a file from URI") | |
426 ) | |
427 | |
428 def add_parser_options(self): | |
429 self.parser.add_argument( | |
430 '-o', '--dest_file', type=str, default='', | |
431 help=_("destination file (DEFAULT: filename from URL)") | |
432 ) | |
433 self.parser.add_argument( | |
434 "-f", | |
435 "--force", | |
436 action="store_true", | |
437 help=_("overwrite existing file without confirmation"), | |
438 ) | |
439 self.parser.add_argument("uri", type=str, help=_("URI of the file to retrieve")) | |
440 | |
441 async def onProgressStarted(self, metadata): | |
442 self.disp(_("File download started"), 2) | |
443 | |
444 async def onProgressFinished(self, metadata): | |
445 self.disp(_("File downloaded successfully"), 2) | |
446 | |
447 async def onProgressError(self, error_msg): | |
448 self.disp(_("Error while downloading file: {}").format(error_msg), error=True) | |
449 | |
450 async def gotId(self, data): | |
451 """Called when a progress id has been received""" | |
452 try: | |
453 await self.set_progress_id(data["progress"]) | |
454 except KeyError: | |
455 if 'xmlui' in data: | |
456 ui = xmlui_manager.create(self.host, data['xmlui']) | |
457 await ui.show() | |
458 else: | |
459 self.disp(_("Can't download file"), error=True) | |
460 self.host.quit(C.EXIT_ERROR) | |
461 | |
462 async def start(self): | |
463 uri = self.args.uri | |
464 dest_file = self.args.dest_file | |
465 if not dest_file: | |
466 parsed_uri = urlparse(uri) | |
467 dest_file = Path(parsed_uri.path).name.strip() or "downloaded_file" | |
468 | |
469 dest_file = Path(dest_file).expanduser().resolve() | |
470 if dest_file.exists() and not self.args.force: | |
471 message = _("File {path} already exists! Do you want to overwrite?").format( | |
472 path = dest_file) | |
473 await self.host.confirmOrQuit(message, _("file download cancelled")) | |
474 | |
475 options = {} | |
476 | |
477 try: | |
478 download_data = await self.host.bridge.fileDownload( | |
479 uri, | |
480 str(dest_file), | |
481 data_format.serialise(options), | |
482 self.profile, | |
483 ) | |
484 except Exception as e: | |
485 self.disp(f"error while trying to download a file: {e}", error=True) | |
486 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
487 else: | |
488 await self.gotId(download_data) | |
489 | |
490 | |
416 class Upload(base.CommandBase): | 491 class Upload(base.CommandBase): |
417 def __init__(self, host): | 492 def __init__(self, host): |
418 super(Upload, self).__init__( | 493 super(Upload, self).__init__( |
419 host, "upload", use_progress=True, use_verbose=True, help=_("upload a file") | 494 host, "upload", use_progress=True, use_verbose=True, help=_("upload a file") |
420 ) | 495 ) |
421 | 496 |
422 def add_parser_options(self): | 497 def add_parser_options(self): |
498 self.parser.add_argument( | |
499 "-e", | |
500 "--encrypt", | |
501 action="store_true", | |
502 help=_("encrypt file using AES-GCM"), | |
503 ) | |
423 self.parser.add_argument("file", type=str, help=_("file to upload")) | 504 self.parser.add_argument("file", type=str, help=_("file to upload")) |
424 self.parser.add_argument( | 505 self.parser.add_argument( |
425 "jid", | 506 "jid", |
426 nargs="?", | 507 nargs="?", |
427 help=_("jid of upload component (nothing to autodetect)"), | 508 help=_("jid of upload component (nothing to autodetect)"), |
428 ) | 509 ) |
429 self.parser.add_argument( | 510 self.parser.add_argument( |
430 "--ignore-tls-errors", | 511 "--ignore-tls-errors", |
431 action="store_true", | 512 action="store_true", |
432 help=_("ignore invalide TLS certificate"), | 513 help=_(r"ignore invalide TLS certificate (/!\ Dangerous /!\)"), |
433 ) | 514 ) |
434 | 515 |
435 async def onProgressStarted(self, metadata): | 516 async def onProgressStarted(self, metadata): |
436 self.disp(_("File upload started"), 2) | 517 self.disp(_("File upload started"), 2) |
437 | 518 |
441 url = metadata["url"] | 522 url = metadata["url"] |
442 except KeyError: | 523 except KeyError: |
443 self.disp("download URL not found in metadata") | 524 self.disp("download URL not found in metadata") |
444 else: | 525 else: |
445 self.disp(_("URL to retrieve the file:"), 1) | 526 self.disp(_("URL to retrieve the file:"), 1) |
446 # XXX: url is display alone on a line to make parsing easier | 527 # XXX: url is displayed alone on a line to make parsing easier |
447 self.disp(url) | 528 self.disp(url) |
448 | 529 |
449 async def onProgressError(self, error_msg): | 530 async def onProgressError(self, error_msg): |
450 self.disp(_("Error while uploading file: {}").format(error_msg), error=True) | 531 self.disp(_("Error while uploading file: {}").format(error_msg), error=True) |
451 | 532 |
456 @param file_(str): file path | 537 @param file_(str): file path |
457 """ | 538 """ |
458 try: | 539 try: |
459 await self.set_progress_id(data["progress"]) | 540 await self.set_progress_id(data["progress"]) |
460 except KeyError: | 541 except KeyError: |
461 # TODO: if 'xmlui' key is present, manage xmlui message display | 542 if 'xmlui' in data: |
462 self.disp(_("Can't upload file"), error=True) | 543 ui = xmlui_manager.create(self.host, data['xmlui']) |
544 await ui.show() | |
545 else: | |
546 self.disp(_("Can't upload file"), error=True) | |
463 self.host.quit(C.EXIT_ERROR) | 547 self.host.quit(C.EXIT_ERROR) |
464 | 548 |
465 async def start(self): | 549 async def start(self): |
466 file_ = self.args.file | 550 file_ = self.args.file |
467 if not os.path.exists(file_): | 551 if not os.path.exists(file_): |
477 self.full_dest_jid = await self.host.get_full_jid(self.args.jid) | 561 self.full_dest_jid = await self.host.get_full_jid(self.args.jid) |
478 | 562 |
479 options = {} | 563 options = {} |
480 if self.args.ignore_tls_errors: | 564 if self.args.ignore_tls_errors: |
481 options["ignore_tls_errors"] = True | 565 options["ignore_tls_errors"] = True |
566 if self.args.encrypt: | |
567 options["encryption"] = C.ENC_AES_GCM | |
482 | 568 |
483 path = os.path.abspath(file_) | 569 path = os.path.abspath(file_) |
484 try: | 570 try: |
485 upload_data = await self.host.bridge.fileUpload( | 571 upload_data = await self.host.bridge.fileUpload( |
486 path, | 572 path, |
488 self.full_dest_jid, | 574 self.full_dest_jid, |
489 data_format.serialise(options), | 575 data_format.serialise(options), |
490 self.profile, | 576 self.profile, |
491 ) | 577 ) |
492 except Exception as e: | 578 except Exception as e: |
493 self.disp(f"can't while trying to upload a file: {e}", error=True) | 579 self.disp(f"error while trying to upload a file: {e}", error=True) |
494 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 580 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
495 else: | 581 else: |
496 await self.gotId(upload_data, file_) | 582 await self.gotId(upload_data, file_) |
497 | 583 |
498 | 584 |
725 host, "share", use_profile=False, help=_("files sharing management") | 811 host, "share", use_profile=False, help=_("files sharing management") |
726 ) | 812 ) |
727 | 813 |
728 | 814 |
729 class File(base.CommandBase): | 815 class File(base.CommandBase): |
730 subcommands = (Send, Request, Receive, Upload, Share) | 816 subcommands = (Send, Request, Receive, Get, Upload, Share) |
731 | 817 |
732 def __init__(self, host): | 818 def __init__(self, host): |
733 super(File, self).__init__( | 819 super(File, self).__init__( |
734 host, "file", use_profile=False, help=_("files sending/receiving/management") | 820 host, "file", use_profile=False, help=_("files sending/receiving/management") |
735 ) | 821 ) |