]> git.phdru.name Git - m_lib.git/blob - m_lib/net/ftp/ftpparse.py
cb4c569ddb2f2670ce9310c2a5ab3dbb9631cd94
[m_lib.git] / m_lib / net / ftp / ftpparse.py
1 #! /usr/bin/env python
2 """Parse output of FTP LIST command.
3    Pure python implementation.
4    Author: Oleg Broytman <phd@phd.pp.ru>.
5    Copyright (C) 2003-2005 PhiloSoft Design.
6    License: GPL.
7    See http://cr.yp.to/ftpparse.html, http://effbot.org/downloads#ftpparse,
8    http://c0re.23.nu/c0de/ftpparsemodule/ and http://www.ocgy.ubc.ca/~tang/treeftp
9
10 Currently covered formats:
11    UNIX ls, with or without gid;
12    Windoze FTP Servers;
13    VMS;
14    WFTPD;
15    NetPresenz (Mac);
16    NetWare;
17    DOS;
18
19 Definitely not covered:
20    EPLF (show me an FTP server first...);
21    Long VMS filenames, with information split across two lines;
22    NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
23 """
24
25
26 __version__ = "1.1.2"
27 __author__ = "Oleg Broytman <phd@phd.pp.ru>"
28 __copyright__ = "Copyright (C) 2003-2005 PhiloSoft Design"
29
30
31 # ChangeLog:
32 # 2005-04-26 version 1.1.2 [phd] Changed some comments and URLs.
33 # 2003-07-23 version 1.1.1 [phd] Upgrade to Python 2.2: 0/1 => False/True.
34 # 2003-07-17 version 1.1.0 [phd] Great renaming.
35 # 2003-07-17 version 1.0.1 [phd] Preserve leading spaces in UNIX format.
36 # 2003-02-17 version 1.0.0 [phd] First public version.
37 # 2003-02-07 version 0.0.1 [phd] started the project.
38
39
40 try:
41    from mx import DateTime
42 except ImportError:
43    _parse_datetime = False
44 else:
45    _parse_datetime = True
46
47
48 class parse_error(Exception): pass
49
50
51 class entry:
52    def __init__(self, name=None, perm=None, nlink=None, user=None, group=None, \
53          size=None, mtime=None, links_to=None, file_type=None):
54
55       if mtime:
56          mtime = ' '.join(mtime)
57          if _parse_datetime:
58             try:
59                mtime = DateTime.DateTimeFrom(mtime)
60             except DateTime.Error:
61                pass
62
63       self.name = name
64       self.perm = perm
65       self.nlink = nlink
66       self.user = user
67       self.group = group
68       self.size = size
69       self.mtime = mtime
70       self.links_to = links_to
71       self.file_type = file_type # f - regular file, d - directory, l - symlink
72
73
74    def __str__(self):
75       return """<%s: name=%s, perm=%s, nlink=%s, user=%s, group=%s, size=%s, mtime=%s, links-to=%s, type=%s at 0x%x>""" % (
76       self.__class__.__name__, self.name, self.perm, self.nlink,
77          self.user, self.group, self.size, self.mtime,
78          self.links_to, self.file_type, id(self))
79
80
81 def _parse_unix(line, parts):
82    name = None
83    perm = None
84    nlink = None
85    user = None
86    group = None
87    size = None
88    mtime = None
89    links_to = None
90    file_type = None
91
92    perm = parts[0]
93
94    if parts[1][0] == '[': # NetWare
95       if perm[0] == 'd':
96          file_type = 'd'
97       elif perm[0] == '-':
98          file_type = 'f'
99       else:
100          return None
101       perm = perm + ' ' + parts[1]
102       user = parts[2]
103       size = parts[3]
104       mtime = parts[4:7]
105
106       parts = line.split(None, 7) # resplit the original line...
107       name = parts[7]             # ...in case the filename contains whitespaces
108
109    elif parts[1] == "folder": # NetPresenz for the Mac
110       file_type = 'd'
111       size = parts[2]
112       mtime = parts[3:6]
113
114       parts = line.split(None, 6)
115       name = parts[6]
116
117    elif parts[0][0] == 'l': # symlink
118       file_type = 'l'
119       nlink = int(parts[1])
120       user = parts[2]
121       group = parts[3]
122       size = parts[4]
123       mtime = parts[5:8]
124
125       parts = line.split(None, 8)
126       link = parts[8]
127       parts = link.split(" -> ")
128       name = parts[0]
129       links_to = parts[1]
130
131    elif len(parts) > 8:
132       if perm[0] == 'd':
133          file_type = 'd'
134       elif perm[0] == '-':
135          file_type = 'f'
136       else:
137          return None
138       nlink = int(parts[1])
139       user = parts[2]
140       group = parts[3]
141       size = parts[4]
142       mtime = parts[5:8]
143
144       parts = line.split(None, 7)
145       name = parts[7]
146       parts = name.split(' ')[1:]
147       name = ' '.join(parts)
148
149    else:
150       if parts[2].isdigit(): # NetPresenz
151          file_type = 'f'
152          size = parts[3]
153          mtime = parts[4:7]
154       else:
155          if perm[0] == 'd':
156             file_type = 'd'
157          elif perm[0] == '-':
158             file_type = 'f'
159          else:
160             return None
161          nlink = int(parts[1])
162          user = parts[2]
163          size = parts[3]
164          mtime = parts[4:7]
165
166       parts = line.split(None, 7)
167       name = parts[7]
168
169    return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
170
171
172 def _parse_vms(parts):
173    name = parts[0]
174    perm = parts[5]
175    nlink = parts[1]
176    user = parts[4][1:-1]
177    group = None
178    size = None
179    mtime = parts[2:4]
180    links_to = None
181    file_type = None
182
183    if ',' in user:
184       parts = user.split(',')
185       user = parts[0]
186       group = parts[1]
187
188    return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
189
190
191 def _parse_dos(parts):
192    name = parts[3]
193    perm = None
194    nlink = None
195    user = None
196    group = None
197    size = None
198    links_to = None
199
200    if _parse_datetime:
201       # Change %m-%d-%y format to %y-%m-%d
202       date = parts[0]
203       date_parts = date.split('-')
204       date = "%s-%s-%s" % (date_parts[2], date_parts[0], date_parts[1])
205       time = parts[1]
206       mtime = [date, time]
207    else:
208       mtime = parts[0:2]
209
210    if parts[2] == "<DIR>":
211       file_type = 'd'
212    else:
213       file_type = 'f'
214       size = int(parts[2])
215
216    return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
217
218
219 def ftpparse(line):
220    parts = line.split()
221    c = parts[0][0]
222
223    if c == '+': # EPLF format is not supported
224       return None
225
226    if c in ('b', 'c', 'd', 'l', 'p', 's', '-'): # UNIX-like
227       return _parse_unix(line, parts)
228
229    if ';' in parts[0]: # VMS
230       return _parse_vms(parts)
231
232    if '0' <= c <= '9': # DOS
233       return _parse_dos(parts)
234
235
236    #Some useless lines, safely ignored:
237    #"Total of 11 Files, 10966 Blocks." (VMS)
238    #"total 14786" (UNIX)
239    #"DISK$ANONFTP:[ANONYMOUS]" (VMS)
240    #"Directory DISK$PCSA:[ANONYM]" (VMS)
241
242    return None
243
244
245 def test():
246    #UNIX-style listing, without inum and without blocks
247    print ftpparse("-rw-r--r--   1 root     other        531 Jan 29 03:26 README")
248    print ftpparse("dr-xr-xr-x   2 root     other        512 Apr  8  1994 etc")
249    print ftpparse("dr-xr-xr-x   2 root     512 Apr  8  1994 etc")
250    print ftpparse("lrwxrwxrwx   1 root     other          7 Jan 25 00:17 bin -> usr/bin")
251    #FTP servers for Windoze:
252    print ftpparse("----------   1 owner    group         1803128 Jul 10 10:18 ls-lR.Z")
253    print ftpparse("d---------   1 owner    group               0 May  9 19:45 Softlib")
254    #WFTPD for DOS:
255    print ftpparse("-rwxrwxrwx   1 noone    nogroup      322 Aug 19  1996 message.ftp")
256    #NetWare:
257    print ftpparse("d [R----F--] supervisor            512       Jan 16 18:53    login")
258    print ftpparse("- [R----F--] rhesus             214059       Oct 20 15:27    cx.exe")
259    #NetPresenz for the Mac:
260    print ftpparse("-------r--         326  1391972  1392298 Nov 22  1995 MegaPhone.sit")
261    print ftpparse("drwxrwxr-x               folder        2 May 10  1996 network")
262
263    #MultiNet (some spaces removed from examples)
264    print ftpparse("00README.TXT;1      2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)")
265    print ftpparse("CORE.DIR;1          1  8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)")
266    #non-MutliNet VMS:
267    print ftpparse("CII-MANUAL.TEX;1  213/216  29-JAN-1996 03:33:12  [ANONYMOU,ANONYMOUS]   (RWED,RWED,,)")
268
269    #DOS format
270    print ftpparse("04-27-00  09:09PM       <DIR>          licensed")
271    print ftpparse("07-18-00  10:16AM       <DIR>          pub")
272    print ftpparse("04-14-00  03:47PM                  589 readme.htm")
273
274    print ftpparse("-rw-r--r--   1 root     other        531 Jan 29 03:26 READ ME")
275    print ftpparse("-rw-r--r--   1 root     other        531 Jan 29 03:26  DO NOT READ ME ")
276
277 if __name__ == "__main__":
278    test()