#!/usr/bin/python # PYTHON_ARGCOMPLETE_OK from __future__ import print_function import argparse import getpass import os import platform import shlex import struct import subprocess import sys from base64 import b64decode, b64encode from distutils.util import strtobool import argcomplete from webdav3.client import Client, WebDavException, NotConnection, Urn def get_terminal_size(): current_os = platform.system() tuple_xy = None if current_os == 'Windows': tuple_xy = _get_terminal_size_windows() if tuple_xy is None: tuple_xy = _get_terminal_size_input() if current_os in ['Linux', 'Darwin'] or current_os.startswith('CYGWIN'): tuple_xy = _get_terminal_size_linux() if tuple_xy is None: tuple_xy = 80, 25 return tuple_xy def _get_terminal_size_windows(): try: from ctypes import windll, create_string_buffer h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) if res: (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 return sizex, sizey except: pass def _get_terminal_size_input(): try: cols = int(subprocess.check_call(shlex.split('tput cols'))) rows = int(subprocess.check_call(shlex.split('tput lines'))) return (cols, rows) except: pass def _get_terminal_size_linux(): def ioctl_GWINSZ(fd): try: import fcntl import termios return struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) except: pass cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: fd = os.open(os.ctermid(), os.O_RDONLY) cr = ioctl_GWINSZ(fd) os.close(fd) except: pass if not cr: try: cr = (os.environ['LINES'], os.environ['COLUMNS']) except: return None return int(cr[1]), int(cr[0]) class ProgressBar: def __init__(self): self.precise = 0 self.total = 0 def show(self): progress_line = self._progress_line() import sys if self.precise == 100: print(progress_line, end="") else: print(progress_line, end="") sys.stdout.write("\r") def _progress_line(self): sizex, sizey = get_terminal_size() available_width = sizex precise = self._get_field_precise() ratio = self._get_field_ratio() available_width -= len(precise) + len(ratio) progress = self._get_field_progress(available_width-2) return "{precise} {progress} {ratio}".format(precise=precise, progress=progress, ratio=ratio) def _get_field_precise(self): line = "{precise}%".format(precise=self.precise) return "{:<4}".format(line) def _get_field_ratio(self): current = float(self.precise * self.total / 100) total = float(self.total) current_line = "{:.2f}".format(current) total_line = "{:.2f}".format(total) line = "{current}/{total}".format(current=current_line, total=total_line) available = len(total_line) * 2 + 1 format_line = "{prefix}{value}{sufix}".format(prefix="{:>", value=available, sufix="}") line = format_line.format(line) return line def _get_field_progress(self, width): available_width = width - 2 current = int(self.precise * available_width / 100) available_width -= current + 1 tip = ">" if not self.precise == 100 else "" progress = "{arrow}{tip}{space}".format(arrow="=" * current, tip=tip, space=" " * available_width) return "[{progress}]".format(progress=progress) def callback(self, current, total): if total and not self.total: self.total = total if not total: return precise = int(float(current) * 100 / total) if self.precise == precise: return else: self.precise = precise self.show() setting_keys = ['webdav_hostname', 'webdav_root', 'webdav_login', 'webdav_password', 'webdav_token', 'proxy_hostname', 'proxy_login', 'proxy_password', 'cert_path', 'key_path'] crypto_keys = ['webdav_password', 'webdav_token', 'proxy_password'] def encoding(source): if not source: return "" return b64encode(source.encode('utf-8')) def decoding(source): if not source: return "" return b64decode(source).decode('utf-8') def import_options(): options = dict() for setting_key in setting_keys: options[setting_key] = os.environ.get(setting_key.upper()) for crypto_key in crypto_keys: if not options[crypto_key]: continue options[crypto_key] = decoding(options[crypto_key]) return options def valid(options): if 'webdav_hostname' not in options: return False if not options['webdav_token'] and not options['webdav_login']: return False if options['webdav_login'] and not options['webdav_password']: return False return True class Formatter(argparse.RawTextHelpFormatter): def _get_default_metavar_for_optional(self, action): if not action.option_strings: return action.dest else: return "" def _format_action_invocation(self, action): if not action.option_strings: default = self._get_default_metavar_for_optional(action) metavar, = self._metavar_formatter(action, default)(1) return metavar else: parts = [] if action.nargs == 0: parts.extend(action.option_strings) else: default = self._get_default_metavar_for_optional(action) args_string = self._format_args(action, default) for option_string in action.option_strings: parts.append(option_string) return '%s %s' % (', '.join(parts), args_string) return ', '.join(parts) def _metavar_formatter(self, action, default_metavar): if action.metavar is not None: result = action.metavar else: result = default_metavar def format(tuple_size): if isinstance(result, tuple): return result else: return (result, ) * tuple_size return format def logging_exception(exception): print(exception) def urn_completer(prefix, **kwargs): options = import_options() try: client = Client(options) prefix_urn = Urn(prefix) if prefix_urn.is_dir(): return (prefix+filename for filename in client.list(prefix_urn.path())) else: parent = prefix_urn.parent() prefix_filename = prefix_urn.filename() prefix_filename_length = len(prefix_filename) return (prefix + filename[prefix_filename_length:] for filename in client.list(parent) if filename.startswith(prefix_filename)) except WebDavException: pass return tuple() if __name__ == "__main__": epilog = """ Examples: -------- $ wdc login https://webdav.server.ru webdav_login: login webdav_password: password success $ wdc login https://webdav.server.ru --token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx not success $ wdc check success $ wdc check file1 not success $ wdc free 245234120344 $ wdc ls dir1 file1 ... fileN $ wdc mkdir dir2 $ wdc copy dir1/file1 -t dir2/file1 $ wdc move dir2/file1 -t dir2/file2 $ wdc download dir1/file1 -t ~/Downloads/file1 $ wdc download dir1/ -t ~/Downloads/dir1/ $ wdc upload dir2/file2 -f ~/Documents/file1 $ wdc upload dir2/ -f ~/Documents/ $ wdc publish di2/file2 https://yadi.sk/i/vWtTUcBucAc6k $ wdc unpublish dir2/file2 $ wdc pull dir1/ -t ~/Documents/dir1/ $ wdc push dir1/ -f ~/Documents/dir1/ $ wdc info dir1/file1 {'name': 'file1', 'modified': 'Thu, 23 Oct 2014 16:16:37 GMT', 'size': '3460064', 'created': '2014-10-23T16:16:37Z'} """ usage = """ wdc [-h] [-v] wdc login https://webdav.server.ru [--token] [-r] [-p] [-c] [-k] wdc [action] [path] [-t] [-f] """ actions = "login logout check info free ls clean mkdir copy move download upload publish unpublish push pull".split() actions_help = "check, info, free, ls, clean, mkdir, copy, move,\ndownload, upload, publish, unpublish, push, pull" parser = argparse.ArgumentParser(prog='wdc', formatter_class=Formatter, epilog=epilog, usage=usage) parser.add_argument("action", help=actions_help, choices=actions) from webdav3.client import __version__ as version version_text = "{name} {version}".format(name="%(prog)s", version=version) parser.add_argument("-v", '--version', action='version', version=version_text) parser.add_argument("-r", "--root", help="example: dir1/dir2") parser.add_argument("--token", help="example: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") parser.add_argument("-c", "--cert-path", help="example: /etc/ssl/certs/certificate.crt") parser.add_argument("-k", "--key-path", help="example: /etc/ssl/private/certificate.key") parser.add_argument("-p", "--proxy", help="example: http://127.0.0.1:8080") parser.add_argument("path", help="example: dir1/dir2/file1", nargs='?').completer = urn_completer parser.add_argument("-f", '--from-path', help="example: ~/Documents/file1") parser.add_argument("-t", "--to-path", help="example for download and pull: ~/Download/file1\nexample for copy and move: dir1/dir2").completer = urn_completer argcomplete.autocomplete(parser, exclude=("-h", "--help", "--proxy", "-p", "-r", "--root", "-c", "--cert-path", "-t", "--to-path", "-v", "--version", "-f", "--from-path", "-k", "--key-path")) args = parser.parse_args() action = args.action if action == 'login': env = dict() if not args.path: try: env['webdav_hostname'] = raw_input("webdav_hostname: ") except NameError: env['webdav_hostname'] = input("webdav_hostname: ") else: env['webdav_hostname'] = args.path if not args.token: try: env['webdav_login'] = raw_input("webdav_login: ") except NameError: env['webdav_login'] = input("webdav_login: ") env['webdav_password'] = getpass.getpass("webdav_password: ") else: env['webdav_token'] = args.token if args.proxy: env['proxy_hostname'] = args.proxy try: env['proxy_login'] = raw_input("proxy_login: ") except NameError: env['proxy_login'] = input("proxy_login: ") env['proxy_password'] = getpass.getpass("proxy_password: ") if args.root: env['webdav_root'] = args.root if args.cert_path: env['cert_path'] = args.cert_path if args.key_path: env['key_path'] = args.key_path try: client = Client(env) check = client.check() text = "success" if check else "not success" print(text) if check: for crypto_key in crypto_keys: if crypto_key not in env: continue if not env[crypto_key]: continue env[crypto_key] = encoding(env[crypto_key]) for (key, value) in env.items(): os.putenv(key.upper(), value) os.system('bash') except WebDavException as e: print("not success") sys.exit() else: options = import_options() if not valid(options): print("First log on webdav server using the following command: wdc login.") sys.exit() elif action == "logout": os.system("exit") elif action == 'check': options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) check = client.check(args.path) if args.path else client.check() text = "success" if check else "not success" print(text) except WebDavException as e: logging_exception(e) elif action == 'free': options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) free_size = client.free() print(free_size) except WebDavException as e: logging_exception(e) elif action == 'ls': options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) paths = client.list(args.path) if args.path else client.list() for path in paths: print(path) except WebDavException as e: logging_exception(e) elif action == 'clean': if not args.path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.clean(args.path) except WebDavException as e: logging_exception(e) elif action == 'mkdir': if not args.path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.mkdir(args.path) except WebDavException as e: logging_exception(e) elif action == 'copy': if not args.path or not args.to_path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.copy(remote_path_from=args.path, remote_path_to=args.to_path) except WebDavException as e: logging_exception(e) elif action == 'move': if not args.path or not args.to_path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.move(remote_path_from=args.path, remote_path_to=args.to_path) except WebDavException as e: logging_exception(e) elif action == 'download': if not args.path or not args.to_path: parser.print_help() else: options = import_options() progress_bar = ProgressBar() def download_progress(download_t, download_d, upload_t, upload_d): progress_bar.callback(current=download_d, total=download_t) try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) if not os.path.exists(path=args.to_path): client.download(remote_path=args.path, local_path=args.to_path, progress=download_progress) print("\n") else: try: choice = raw_input("Local path exists, do you want to overwrite it? [Y/n] ") except NameError: choice = input("Local path exists, do you want to overwrite it? [Y/n] ") try: yes = strtobool(choice.lower()) if yes: client.download(remote_path=args.path, local_path=args.to_path, progress=download_progress) print("\n") except ValueError: print("Incorrect answer") except WebDavException as e: logging_exception(e) elif action == 'upload': if not args.path or not args.from_path: parser.print_help() else: options = import_options() progress_bar = ProgressBar() def upload_progress(download_t, download_d, upload_t, upload_d): progress_bar.callback(current=upload_d, total=upload_t) try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) if not client.check(remote_path=args.path): client.upload(remote_path=args.path, local_path=args.from_path, progress=upload_progress) print("\n") else: try: choice = raw_input("Remote resource exists, do you want to overwrite it? [Y/n] ") except NameError: choice = input("Remote resource exists, do you want to overwrite it? [Y/n] ") try: yes = strtobool(choice.lower()) if yes: client.upload(remote_path=args.path, local_path=args.from_path, progress=upload_progress) print("\n") except ValueError: print("Incorrect answer") except WebDavException as e: logging_exception(e) elif action == 'publish': if not args.path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) link = client.publish(args.path) print(link) except WebDavException as e: logging_exception(e) elif action == 'unpublish': if not args.path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.unpublish(args.path) except WebDavException as e: logging_exception(e) elif action == 'push': if not args.path or not args.from_path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.push(remote_directory=args.path, local_directory=args.from_path) except WebDavException as e: logging_exception(e) elif action == 'pull': if not args.path or not args.to_path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) client.pull(remote_directory=args.path, local_directory=args.to_path) except WebDavException as e: logging_exception(e) elif action == 'info': if not args.path: parser.print_help() else: options = import_options() try: client = Client(options) connection = client.check() if not connection: raise NotConnection(options["webdav_hostname"]) info = client.info(args.path) print(info) except WebDavException as e: logging_exception(e) else: parser.print_help()