From a9ae058b06ef0236fc64237e5c6dc7b779e13468 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sat, 13 Jul 2013 12:36:10 +0200 Subject: ftp*: python3 compat, SIZE fixes, MLSD support Set binary mode before requesting size, do not exit program if size() fails. Support MLSD mode for ftp-list.py (mostly copied from ftp.py) --- ftp-list.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 7 deletions(-) (limited to 'ftp-list.py') diff --git a/ftp-list.py b/ftp-list.py index 1b65832..3d7a21b 100755 --- a/ftp-list.py +++ b/ftp-list.py @@ -6,9 +6,14 @@ # Prepend directory name to files and list files: # awk '/^Directory/{if($0 != "Directory /")dir=substr($0, index($0, "/"))}/^-/{n=index($0,$9);print substr($0,1, n - 1) dir "/" substr($0,n)}' +from __future__ import print_function + import sys from ftplib import FTP import re +from datetime import datetime, timezone, date + +use_list = False user = "anonymous" passwd = "anon" @@ -71,23 +76,86 @@ patt_list = re.compile(r""" $ """, re.VERBOSE) +def queue_dir(cwd, name): + if cwd == "/": + dir = "/" + name + else: + dir = cwd + "/" + name + print("Queuing", dir, file=sys.stderr) + dirs.append(dir) + +# LIST handler def get_ls_processor(cwd): def process_dir(line): print(line) if line[0] == 'd': m = patt_list.match(line) name = m.group(1) - if cwd == "/": - dir = "/" + name - else: - dir = cwd + "/" + name - print("Queuing", dir, file=sys.stderr) - dirs.append(dir) + queue_dir(cwd, name) # elif line[0] == 'l': # m = patt_list.match(line) # print("Link", m.group(2)) return process_dir +# MLSD handler +def format_perms(mode): + """Turns a numeric UNIX mode into human-readable form + """ + str = "" + for i in range(0, 3): + o = 0o100 >> (3 * i) + str += "r" if mode & (4 * o) else "-" + str += "w" if mode & (2 * o) else "-" + if mode & (0o4000 >> i): # setuid, setgid or sticky bit + if o == 0o001: # "world" + str += "t" if mode & o else "T" + else: + str += "s" if mode & o else "S" + else: + str += "x" if mode & (1 * o) else "-" + return str + +def format_type_fact(type): + if type == "file": + return "-" + elif type in ("cdir", "pdir", "dir"): + return "d" + # TODO: handle OS.name=type + else: + return "?" + +def dt_from_ftp(timeval): + return datetime.strptime(timeval, "%Y%m%d%H%M%S").replace(tzinfo=timezone.utc) + +def format_mlsd(name, facts): + + if "type" in facts: + mode_desc = format_type_fact(facts["type"]) + else: + mode_desc = "?" + + if "unix.mode" in facts: + perm = int(facts["unix.mode"], 8) + mode_desc += format_perms(perm) + else: + mode_desc += "?" * 9 + + user = "?" if not "unix.owner" in facts else facts["unix.owner"] + group = "?" if not "unix.group" in facts else facts["unix.group"] + size = "" if not "size" in facts else int(facts["size"]) + + modtime = 0 if not "modify" in facts else dt_from_ftp(facts["modify"]) + if date.today().year == modtime.year: + date_str = modtime.strftime("%b %d %H:%M") + else: + date_str = modtime.strftime("%b %d %Y") + + line = mode_desc + " " + #line += " {links:4s}".format(links=-1) + line += " {user:8s} {group:8s} {size:8}".format(user=user, group=group, size=size) + line += " " + date_str + " " + name + return line + with FTP() as ftp: ftp.connect(host, port) ftp.login(user, passwd) @@ -96,5 +164,12 @@ with FTP() as ftp: dir = dirs.pop() ftp.cwd(dir) print("Directory", ftp.pwd()) - ftp.retrlines('LIST', get_ls_processor(dir)) + if use_list: + ftp.retrlines('LIST', get_ls_processor(dir)) + else: + for name, facts in ftp.mlsd(): + if name not in (".", ".."): + print(format_mlsd(name, facts)) + if facts["type"] == "dir": + queue_dir(dir, name) print("Queue size:", len(dirs), file=sys.stderr) -- cgit v1.2.1