The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ftpparse.c
Go to the documentation of this file.
1 /* ftpparse.c, ftpparse.h: library for parsing FTP LIST responses
2 20001223
3 D. J. Bernstein, djb@cr.yp.to
4 http://cr.yp.to/ftpparse.html
5 
6 Commercial use is fine, if you let me know what programs you're using this in.
7 
8 Currently covered formats:
9 EPLF.
10 UNIX ls, with or without gid.
11 Microsoft FTP Service.
12 Windows NT FTP Server.
13 VMS.
14 WFTPD.
15 NetPresenz (Mac).
16 NetWare.
17 MSDOS.
18 
19 Definitely not covered:
20 Long VMS filenames, with information split across two lines.
21 NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
22 */
23 
24 #include <ftpparse.h>
25 
26 static long totai(long year,long month,long mday)
27 {
28  long result;
29  if (month >= 2) month -= 2;
30  else { month += 10; --year; }
31  result = (mday - 1) * 10 + 5 + 306 * month;
32  result /= 10;
33  if (result == 365) { year -= 3; result = 1460; }
34  else result += 365 * (year % 4);
35  year /= 4;
36  result += 1461 * (year % 25);
37  year /= 25;
38  if (result == 36524) { year -= 3; result = 146096; }
39  else { result += 36524 * (year % 4); }
40  year /= 4;
41  result += 146097 * (year - 5);
42  result += 11017;
43  return result * 86400;
44 }
45 
46 static int flagneedbase = 1;
47 static time_t base; /* time() value on this OS at the beginning of 1970 TAI */
48 static long now; /* current time */
49 static int flagneedcurrentyear = 1;
50 static long currentyear; /* approximation to current year */
51 
52 static void initbase(void)
53 {
54  struct tm *t;
55  if (!flagneedbase) return;
56 
57  base = 0;
58  t = gmtime(&base);
59  base = -(totai(t->tm_year + 1900,t->tm_mon,t->tm_mday) + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec);
60  /* assumes the right time_t, counting seconds. */
61  /* base may be slightly off if time_t counts non-leap seconds. */
62  flagneedbase = 0;
63 }
64 
65 static void initnow(void)
66 {
67  long day;
68  long year;
69 
70  initbase();
71  now = time((time_t *) 0) - base;
72 
73  if (flagneedcurrentyear) {
74  day = now / 86400;
75  if ((now % 86400) < 0) --day;
76  day -= 11017;
77  year = 5 + day / 146097;
78  day = day % 146097;
79  if (day < 0) { day += 146097; --year; }
80  year *= 4;
81  if (day == 146096) { year += 3; day = 36524; }
82  else { year += day / 36524; day %= 36524; }
83  year *= 25;
84  year += day / 1461;
85  day %= 1461;
86  year *= 4;
87  if (day == 1460) { year += 3; day = 365; }
88  else { year += day / 365; day %= 365; }
89  day *= 10;
90  if ((day + 5) / 306 >= 10) ++year;
91  currentyear = year;
93  }
94 }
95 
96 /* UNIX ls does not show the year for dates in the last six months. */
97 /* So we have to guess the year. */
98 /* Apparently NetWare uses ``twelve months'' instead of ``six months''; ugh. */
99 /* Some versions of ls also fail to show the year for future dates. */
100 static long guesstai(long month,long mday)
101 {
102  long year;
103  long t;
104 
105  initnow();
106 
107  for (year = currentyear - 1;year < currentyear + 100;++year) {
108  t = totai(year,month,mday);
109  if (now - t < 350 * 86400)
110  return t;
111  }
112  return 0;
113 }
114 
115 static int check(char *buf,char *monthname)
116 {
117  if ((buf[0] != monthname[0]) && (buf[0] != monthname[0] - 32)) return 0;
118  if ((buf[1] != monthname[1]) && (buf[1] != monthname[1] - 32)) return 0;
119  if ((buf[2] != monthname[2]) && (buf[2] != monthname[2] - 32)) return 0;
120  return 1;
121 }
122 
123 static char *months[12] = {
124  "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"
125 } ;
126 
127 static int getmonth(char *buf,int len)
128 {
129  int i;
130  if (len == 3)
131  for (i = 0;i < 12;++i)
132  if (check(buf,months[i])) return i;
133  return -1;
134 }
135 
136 static long getlong(char *buf,int len)
137 {
138  long u = 0;
139  while (len-- > 0)
140  u = u * 10 + (*buf++ - '0');
141  return u;
142 }
143 
144 int ftpparse(struct ftpparse *fp,char *buf,int len)
145 {
146  int i;
147  int j;
148  int state;
149  long size = 0;
150  long year;
151  long month = 0;
152  long mday = 0;
153  long hour;
154  long minute;
155 
156  fp->name = 0;
157  fp->namelen = 0;
158  fp->flagtrycwd = 0;
159  fp->flagtryretr = 0;
161  fp->size = 0;
163  fp->mtime = 0;
165  fp->id = 0;
166  fp->idlen = 0;
167 
168  if (len < 2) /* an empty name in EPLF, with no info, could be 2 chars */
169  return 0;
170 
171  switch(*buf) {
172  /* see http://pobox.com/~djb/proto/eplf.txt */
173  /* "+i8388621.29609,m824255902,/,\tdev" */
174  /* "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF" */
175  case '+':
176  i = 1;
177  for (j = 1;j < len;++j) {
178  if (buf[j] == 9) {
179  fp->name = buf + j + 1;
180  fp->namelen = len - j - 1;
181  return 1;
182  }
183  if (buf[j] == ',') {
184  switch(buf[i]) {
185  case '/':
186  fp->flagtrycwd = 1;
187  break;
188  case 'r':
189  fp->flagtryretr = 1;
190  break;
191  case 's':
193  fp->size = getlong(buf + i + 1,j - i - 1);
194  break;
195  case 'm':
197  initbase();
198  fp->mtime = base + getlong(buf + i + 1,j - i - 1);
199  break;
200  case 'i':
201  fp->idtype = FTPPARSE_ID_FULL;
202  fp->id = buf + i + 1;
203  fp->idlen = j - i - 1;
204  }
205  i = j + 1;
206  }
207  }
208  return 0;
209 
210  /* UNIX-style listing, without inum and without blocks */
211  /* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */
212  /* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */
213  /* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */
214  /* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */
215  /* Also produced by Microsoft's FTP servers for Windows: */
216  /* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */
217  /* "d--------- 1 owner group 0 May 9 19:45 Softlib" */
218  /* Also WFTPD for MSDOS: */
219  /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
220  /* Also NetWare: */
221  /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
222  /* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */
223  /* Also NetPresenz for the Mac: */
224  /* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
225  /* "drwxrwxr-x folder 2 May 10 1996 network" */
226  case 'b':
227  case 'c':
228  case 'd':
229  case 'l':
230  case 'p':
231  case 's':
232  case '-':
233 
234  if (*buf == 'd') fp->flagtrycwd = 1;
235  if (*buf == '-') fp->flagtryretr = 1;
236  if (*buf == 'l') fp->flagtrycwd = fp->flagtryretr = 1;
237 
238  state = 1;
239  i = 0;
240  for (j = 1;j < len;++j)
241  if ((buf[j] == ' ') && (buf[j - 1] != ' ')) {
242  switch(state) {
243  case 1: /* skipping perm */
244  state = 2;
245  break;
246  case 2: /* skipping nlink */
247  state = 3;
248  if ((j - i == 6) && (buf[i] == 'f')) /* for NetPresenz */
249  state = 4;
250  break;
251  case 3: /* skipping uid */
252  state = 4;
253  break;
254  case 4: /* getting tentative size */
255  size = getlong(buf + i,j - i);
256  state = 5;
257  break;
258  case 5: /* searching for month, otherwise getting tentative size */
259  month = getmonth(buf + i,j - i);
260  if (month >= 0)
261  state = 6;
262  else
263  size = getlong(buf + i,j - i);
264  break;
265  case 6: /* have size and month */
266  mday = getlong(buf + i,j - i);
267  state = 7;
268  break;
269  case 7: /* have size, month, mday */
270  if ((j - i == 4) && (buf[i + 1] == ':')) {
271  hour = getlong(buf + i,1);
272  minute = getlong(buf + i + 2,2);
274  initbase();
275  fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
276  } else if ((j - i == 5) && (buf[i + 2] == ':')) {
277  hour = getlong(buf + i,2);
278  minute = getlong(buf + i + 3,2);
280  initbase();
281  fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
282  }
283  else if (j - i >= 4) {
284  year = getlong(buf + i,j - i);
286  initbase();
287  fp->mtime = base + totai(year,month,mday);
288  }
289  else
290  return 0;
291  fp->name = buf + j + 1;
292  fp->namelen = len - j - 1;
293  state = 8;
294  break;
295  case 8: /* twiddling thumbs */
296  break;
297  }
298  i = j + 1;
299  while ((i < len) && (buf[i] == ' ')) ++i;
300  }
301 
302  if (state != 8)
303  return 0;
304 
305  fp->size = size;
307 
308  if (*buf == 'l')
309  for (i = 0;i + 3 < fp->namelen;++i)
310  if (fp->name[i] == ' ')
311  if (fp->name[i + 1] == '-')
312  if (fp->name[i + 2] == '>')
313  if (fp->name[i + 3] == ' ') {
314  fp->namelen = i;
315  break;
316  }
317 
318  /* eliminate extra NetWare spaces */
319  if ((buf[1] == ' ') || (buf[1] == '['))
320  if (fp->namelen > 3)
321  if (fp->name[0] == ' ')
322  if (fp->name[1] == ' ')
323  if (fp->name[2] == ' ') {
324  fp->name += 3;
325  fp->namelen -= 3;
326  }
327 
328  return 1;
329  }
330 
331  /* MultiNet (some spaces removed from examples) */
332  /* "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)" */
333  /* "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)" */
334  /* and non-MutliNet VMS: */
335  /* "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)" */
336  for (i = 0;i < len;++i)
337  if (buf[i] == ';')
338  break;
339  if (i < len) {
340  fp->name = buf;
341  fp->namelen = i;
342  if (i > 4)
343  if (buf[i - 4] == '.')
344  if (buf[i - 3] == 'D')
345  if (buf[i - 2] == 'I')
346  if (buf[i - 1] == 'R') {
347  fp->namelen -= 4;
348  fp->flagtrycwd = 1;
349  }
350  if (!fp->flagtrycwd)
351  fp->flagtryretr = 1;
352  while (buf[i] != ' ') if (++i == len) return 0;
353  while (buf[i] == ' ') if (++i == len) return 0;
354  while (buf[i] != ' ') if (++i == len) return 0;
355  while (buf[i] == ' ') if (++i == len) return 0;
356  j = i;
357  while (buf[j] != '-') if (++j == len) return 0;
358  mday = getlong(buf + i,j - i);
359  while (buf[j] == '-') if (++j == len) return 0;
360  i = j;
361  while (buf[j] != '-') if (++j == len) return 0;
362  month = getmonth(buf + i,j - i);
363  if (month < 0) return 0;
364  while (buf[j] == '-') if (++j == len) return 0;
365  i = j;
366  while (buf[j] != ' ') if (++j == len) return 0;
367  year = getlong(buf + i,j - i);
368  while (buf[j] == ' ') if (++j == len) return 0;
369  i = j;
370  while (buf[j] != ':') if (++j == len) return 0;
371  hour = getlong(buf + i,j - i);
372  while (buf[j] == ':') if (++j == len) return 0;
373  i = j;
374  while ((buf[j] != ':') && (buf[j] != ' ')) if (++j == len) return 0;
375  minute = getlong(buf + i,j - i);
376 
378  initbase();
379  fp->mtime = base + totai(year,month,mday) + hour * 3600 + minute * 60;
380 
381  return 1;
382  }
383 
384  /* MSDOS format */
385  /* 04-27-00 09:09PM <DIR> licensed */
386  /* 07-18-00 10:16AM <DIR> pub */
387  /* 04-14-00 03:47PM 589 readme.htm */
388  if ((*buf >= '0') && (*buf <= '9')) {
389  i = 0;
390  j = 0;
391  while (buf[j] != '-') if (++j == len) return 0;
392  month = getlong(buf + i,j - i) - 1;
393  while (buf[j] == '-') if (++j == len) return 0;
394  i = j;
395  while (buf[j] != '-') if (++j == len) return 0;
396  mday = getlong(buf + i,j - i);
397  while (buf[j] == '-') if (++j == len) return 0;
398  i = j;
399  while (buf[j] != ' ') if (++j == len) return 0;
400  year = getlong(buf + i,j - i);
401  if (year < 50) year += 2000;
402  if (year < 1000) year += 1900;
403  while (buf[j] == ' ') if (++j == len) return 0;
404  i = j;
405  while (buf[j] != ':') if (++j == len) return 0;
406  hour = getlong(buf + i,j - i);
407  while (buf[j] == ':') if (++j == len) return 0;
408  i = j;
409  while ((buf[j] != 'A') && (buf[j] != 'P')) if (++j == len) return 0;
410  minute = getlong(buf + i,j - i);
411  if (hour == 12) hour = 0;
412  if (buf[j] == 'A') if (++j == len) return 0;
413  if (buf[j] == 'P') { hour += 12; if (++j == len) return 0; }
414  if (buf[j] == 'M') if (++j == len) return 0;
415 
416  while (buf[j] == ' ') if (++j == len) return 0;
417  if (buf[j] == '<') {
418  fp->flagtrycwd = 1;
419  while (buf[j] != ' ') if (++j == len) return 0;
420  }
421  else {
422  i = j;
423  while (buf[j] != ' ') if (++j == len) return 0;
424  fp->size = getlong(buf + i,j - i);
426  fp->flagtryretr = 1;
427  }
428  while (buf[j] == ' ') if (++j == len) return 0;
429 
430  fp->name = buf + j;
431  fp->namelen = len - j;
432 
434  initbase();
435  fp->mtime = base + totai(year,month,mday) + hour * 3600 + minute * 60;
436 
437  return 1;
438  }
439 
440  /* Some useless lines, safely ignored: */
441  /* "Total of 11 Files, 10966 Blocks." (VMS) */
442  /* "total 14786" (UNIX) */
443  /* "DISK$ANONFTP:[ANONYMOUS]" (VMS) */
444  /* "Directory DISK$PCSA:[ANONYM]" (VMS) */
445 
446  return 0;
447 }
#define FTPPARSE_MTIME_REMOTEDAY
Definition: ftpparse.h:41
char * id
Definition: ftpparse.h:30
static int check(char *buf, char *monthname)
Definition: ftpparse.c:115
static long totai(long year, long month, long mday)
Definition: ftpparse.c:26
int flagtrycwd
Definition: ftpparse.h:23
char * name
Definition: ftpparse.h:21
int mtimetype
Definition: ftpparse.h:27
int idlen
Definition: ftpparse.h:31
#define FTPPARSE_MTIME_UNKNOWN
Definition: ftpparse.h:38
#define FTPPARSE_SIZE_UNKNOWN
Definition: ftpparse.h:34
static long getlong(char *buf, int len)
Definition: ftpparse.c:136
int sizetype
Definition: ftpparse.h:25
int namelen
Definition: ftpparse.h:22
static int flagneedcurrentyear
Definition: ftpparse.c:49
int fp
Definition: gbfidx.cpp:49
int flagtryretr
Definition: ftpparse.h:24
static int flagneedbase
Definition: ftpparse.c:46
#define FTPPARSE_ID_FULL
Definition: ftpparse.h:49
#define FTPPARSE_MTIME_LOCAL
Definition: ftpparse.h:39
static long guesstai(long month, long mday)
Definition: ftpparse.c:100
int size
Definition: regex.c:5043
time_t mtime
Definition: ftpparse.h:28
static long now
Definition: ftpparse.c:48
static int getmonth(char *buf, int len)
Definition: ftpparse.c:127
int idtype
Definition: ftpparse.h:29
#define FTPPARSE_SIZE_BINARY
Definition: ftpparse.h:35
static long currentyear
Definition: ftpparse.c:50
result
Definition: regex.c:5545
int ftpparse(struct ftpparse *fp, char *buf, int len)
Definition: ftpparse.c:144
static void initbase(void)
Definition: ftpparse.c:52
#define FTPPARSE_ID_UNKNOWN
Definition: ftpparse.h:48
long size
Definition: ftpparse.h:26
#define FTPPARSE_MTIME_REMOTEMINUTE
Definition: ftpparse.h:40
static char * months[12]
Definition: ftpparse.c:123
static void initnow(void)
Definition: ftpparse.c:65
static time_t base
Definition: ftpparse.c:47