From 212c193f9f4636c244c42b1a5de5b29415b62056 Mon Sep 17 00:00:00 2001 From: egcmi Date: Tue, 7 Apr 2026 17:28:28 +0200 Subject: [PATCH 1/2] Implement ticket password feature in dl-cli.py Add ticket password generation using secrets module --- client/dl-cli.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/dl-cli.py b/client/dl-cli.py index baeb386..bc5324b 100755 --- a/client/dl-cli.py +++ b/client/dl-cli.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals, print_function, generators import argparse +import secrets import io, sys from datetime import datetime @@ -73,6 +74,10 @@ def progress(download_t, download_d, upload_t, upload_d): c.FORM_FILENAME, filename.encode('utf8'))), ("msg", json.dumps({}))]) + if 'ticket_pass' in params and params['ticket_pass'] == 1: + msg['pass'] = secrets.token_urlsafe(16) + print(msg['pass']) + try: c.perform() except pycurl.error as e: @@ -212,6 +217,13 @@ def main(): die("fingerprint doesn't look like a valid hex-encoded SHA256 hash") cfg['fingerprint'] = 'sha256//' + binascii.b2a_base64(binascii.a2b_hex(fp))[:-1] + # Ticket password parameter + if 'ticket_pass' in cfg: + try: + cfg['ticket_pass'] = v.check('boolean', cfg['ticket_pass']) + except validate.ValidateError: + die("Value for \"ticket_pass\" in configuration file is not a boolean.") + try: if args.file: if len(args.file) == 1: From d882708a80be99cc8f77630c8147f4ade9207198 Mon Sep 17 00:00:00 2001 From: egcmi Date: Tue, 21 Jul 2026 10:07:38 +0200 Subject: [PATCH 2/2] Implement ticket password reading from file Added functionality to read ticket password from a file or stdin. Updated HTTP POST message to include ticket password if provided. --- client/dl-cli.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/client/dl-cli.py b/client/dl-cli.py index bc5324b..3984ab0 100755 --- a/client/dl-cli.py +++ b/client/dl-cli.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals, print_function, generators import argparse -import secrets import io, sys from datetime import datetime @@ -69,14 +68,16 @@ def progress(download_t, download_d, upload_t, upload_d): if not filename: filename = os.path.basename(path) + + msg = {} + # add ticket parameters, if present/configured + if 'ticket_password' in params: + msg['pass'] = params['ticket_password'] + c.setopt(c.HTTPPOST, [ ("file", (c.FORM_FILE, path.encode(sys.getfilesystemencoding()), c.FORM_FILENAME, filename.encode('utf8'))), - ("msg", json.dumps({}))]) - - if 'ticket_pass' in params and params['ticket_pass'] == 1: - msg['pass'] = secrets.token_urlsafe(16) - print(msg['pass']) + ("msg", json.dumps(msg))]) try: c.perform() @@ -170,10 +171,28 @@ def die(descr, code=1): exit(code) +def read_ticket_password(password_file): + if password_file == '-': + password = sys.stdin.read() + else: + try: + with io.open(password_file, 'r', encoding='utf8') as f: + password = f.read() + except IOError as e: + die("cannot read password file \"{0}\": {1}".format(password_file, e)) + + password = password.rstrip('\r\n') + if not password: + die("ticket password is empty") + return password + + def main(): parser = argparse.ArgumentParser(description="Upload a file to DL", epilog=DL_AGENT) parser.add_argument('-r', metavar="file", dest="rc", default="~/.dl.rc", help="Use alternate RC file") + parser.add_argument('--password-file', metavar="file", dest="ticket_password_file", + help="Read download ticket password from file ('-' for stdin)") group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-g', metavar="email", dest="grant", nargs='?', default=False, help="Generate a grant with notification sent to 'email'") @@ -209,6 +228,11 @@ def main(): if not cfg['verify']: print("WARNING: SSL validation is disabled (use fingerprint for self-signed certs instead!)") + if args.ticket_password_file: + if not args.file: + die("--password-file can only be used when uploading files") + cfg['ticket_password'] = read_ticket_password(args.ticket_password_file) + # Pre-process the fingerprint if cfg['fingerprint'] and cfg['fingerprint'][0] not in '~./' \ and len(cfg['fingerprint']) in [64, 95]: @@ -217,13 +241,6 @@ def main(): die("fingerprint doesn't look like a valid hex-encoded SHA256 hash") cfg['fingerprint'] = 'sha256//' + binascii.b2a_base64(binascii.a2b_hex(fp))[:-1] - # Ticket password parameter - if 'ticket_pass' in cfg: - try: - cfg['ticket_pass'] = v.check('boolean', cfg['ticket_pass']) - except validate.ValidateError: - die("Value for \"ticket_pass\" in configuration file is not a boolean.") - try: if args.file: if len(args.file) == 1: