# Copyright (C) 2009-2014 Bastian Kleineidam
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Python implementation of a part of Dan Bernstein's ftpparse library.
See also http://cr.yp.to/ftpparse.html
"""
months = (
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
)
[docs]
def ismonth(txt):
"""Check if given text is a month name."""
return txt.lower() in months
[docs]
def ftpparse(line):
"""Parse a FTP list line into a dictionary with attributes:
name - name of file (string)
trycwd - False if cwd is definitely pointless, True otherwise
tryretr - False if retr is definitely pointless, True otherwise
If the line has no file information, None is returned
"""
if len(line) < 2:
# an empty name in EPLF, with no info, could be 2 chars
return None
info = dict(name=None, trycwd=False, tryretr=False)
# EPLF format
# http://pobox.com/~djb/proto/eplf.html
# "+i8388621.29609,m824255902,/,\tdev"
# "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF"
if line[0] == '+':
if '\t' in line:
flags, name = line.split('\t', 1)
info['name'] = name
flags = flags.split(',')
info['trycwd'] = '/' in flags
info['tryretr'] = 'r' in flags
return info
# UNIX-style listing, without inum and without blocks
# "-rw-r--r-- 1 root other 531 Jan 29 03:26 README"
# "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc"
# "dr-xr-xr-x 2 root 512 Apr 8 1994 etc"
# "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin"
# Also produced by Microsoft's FTP servers for Windows:
# "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z"
# "d--------- 1 owner group 0 May 9 19:45 Softlib"
# Also WFTPD for MS-DOS:
# "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp"
# Also NetWare:
# "d [R----F--] supervisor 512 Jan 16 18:53 login"
# "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe"
# Also NetPresenz for the Mac:
# "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit"
# "drwxrwxr-x folder 2 May 10 1996 network"
if line[0] in 'bcdlps-':
if line[0] == 'd':
info['trycwd'] = True
if line[0] == '-':
info['tryretr'] = True
if line[0] == 'l':
info['trycwd'] = info['tryretr'] = True
parts = line.split()
if len(parts) < 7:
return None
del parts[0] # skip permissions
if parts[0] != 'folder':
del parts[0] # skip nlink
del parts[0] # skip uid
del parts[0] # skip gid or size
if not ismonth(parts[0]):
del parts[0] # skip size
if not ismonth(parts[0]):
return None
del parts[0] # skip month
del parts[0] # skip day
if not parts:
return None
del parts[0] # skip year or time
name = " ".join(parts)
# resolve links
if line[0] == 'l' and ' -> ' in name:
name = name.split(' -> ', 1)[1]
# eliminate extra NetWare spaces
if line[1] in ' [' and name.startswith(' '):
name = name[3:]
info["name"] = name
return info
# MultiNet (some spaces removed from examples)
# "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)"
# "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)"
# and non-MutliNet VMS:
# "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)"
i = line.find(';')
if i != -1:
name = line[:i]
if name.endswith(".DIR"):
name = name[:-4]
info["trycwd"] = True
else:
info["tryretr"] = True
info["name"] = name
return info
# MS-DOS format
# 04-27-00 09:09PM <DIR> licensed
# 07-18-00 10:16AM <DIR> pub
# 04-14-00 03:47PM 589 readme.htm
if line[0].isdigit():
parts = line.split()
if len(parts) != 4:
return None
info['name'] = parts[3]
if parts[2][0] == '<':
info['trycwd'] = True
else:
info['tryretr'] = True
return info
# Some useless lines, safely ignored:
# "Total of 11 Files, 10966 Blocks." (VMS)
# "total 14786" (UNIX)
# "DISK$ANONFTP:[ANONYMOUS]" (VMS)
# "Directory DISK$PCSA:[ANONYM]" (VMS)
return None