File Coverage

xs/ftpparse.c
Criterion Covered Total %
statement 222 409 54.2
branch 210 552 38.0
condition n/a
subroutine n/a
pod n/a
total 432 961 44.9


line stmt bran cond sub pod time code
1             /* ftpparse.c, ftpparse.h: library for parsing FTP LIST responses
2             *
3             * Written by Uwe Ohse, 2002-07-12.
4             * Strongly influences by Daniel J. Bernsteins ftpparse.c.
5             *
6             * placed in the public domain.
7             */
8              
9             /*
10             * Currently covered:
11             * EPLF.
12             * UNIX ls, with or without gid.
13             * different Windows and DOS FTP servers.
14             * VMS, but not CMS.
15             * NetPresenz (Mac).
16             * NetWare.
17             */
18              
19             #include /* gmtime, time_t, time() */
20             #include "ftpparse.h"
21             #include "bailout.h"
22             #include "str.h"
23             #include "case.h"
24             #include "utcdate2tai.h"
25              
26             static int my_byte_equal(const char *s, unsigned int n, const char *t)
27             {
28             unsigned int i;
29 24 0         for (i=0;i
    0          
    0          
    100          
30 20 0         if (s[i]!=t[i]) return 0;
    0          
    0          
    50          
31             return 1;
32             }
33             static int
34             fix_year(unsigned long *year)
35             {
36 18 0         if (*year<70) *year+=2000;
    0          
    50          
    50          
    50          
    50          
37 18 0         else if (*year<100) *year+=1900;
    0          
    50          
    50          
    50          
    50          
38 10 0         else if (*year<1970) return 0;
    0          
    50          
    50          
    0          
    50          
39             return 1;
40             }
41              
42             /* scan_ulong with bound-check */
43             static unsigned int
44             get_ulong(const char *p, unsigned int len, unsigned long *ul)
45             {
46             unsigned long u=0;
47             unsigned int i;
48 586 0         for (i=0;i
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    0          
49 483 0         if (p[i]>'9' || p[i]<'0')
    0          
    0          
    0          
    0          
    0          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    0          
50             break;
51 390           u*=10;
52 390           u+=p[i]-'0';
53             }
54 93           *ul=u;
55             return i;
56             }
57             static unsigned int
58             get_uint64(const char *p, unsigned int len, uint64 *ul)
59             {
60             uint64 u=0;
61             unsigned int i;
62 240 0         for (i=0;i
    0          
    0          
    100          
    100          
    100          
    0          
63 197 0         if (p[i]>'9' || p[i]<'0')
    0          
    0          
    50          
    50          
    100          
    0          
64             break;
65 155           u*=10;
66 155           u+=p[i]-'0';
67             }
68 0           *ul=u;
69             return i;
70             }
71              
72              
73             /* UNIX ls does not show the year for dates in the last six months. */
74             /* So we have to guess the year. */
75             /* Apparently NetWare uses ``twelve months'' instead of ``six months''; ugh. */
76             /* Some versions of ls also fail to show the year for future dates. */
77             static long
78 37           guess_year(unsigned long month,unsigned long day)
79             {
80             static long this_year;
81             static struct tai yearstart;
82             struct tai x;
83             struct tai now;
84 37           tai_now(&now);
85              
86 37 100         if (!this_year) {
87             struct tai n;
88 2           tai_now(&n);
89 2           this_year=1970;
90             while (1) {
91 100           utcdate2tai(&yearstart,this_year+1,0,1,0,0,0);
92 100 100         if (tai_less(&n,&yearstart)) break;
93 98           this_year++;
94 100           }
95             }
96 37           utcdate2tai(&x,this_year,month,day,0,0,0);
97 37 50         if (tai_less(&now,&x))
98 0           return this_year-1;
99 37           return this_year;
100             }
101              
102              
103              
104             static int
105 0           getmod (struct tai *t, const char *p, unsigned int l)
106             {
107             unsigned int i;
108             unsigned long year,mon,day,hour,min,sec;
109              
110             year=mon=day=hour=min=sec=0;
111              
112 0 0         if (l<14) return 0;
113              
114 0 0         for (i = 0; i < l; i++) {
115             unsigned int u;
116 0 0         if (p[i]<'0' || p[i]>'9') return 0;
117 0           u = (p[i] - '0');
118            
119 0           switch (i) {
120             case 0:
121             case 1:
122             case 2:
123             case 3:
124 0           year *= 10;
125 0           year += u;
126 0           break;
127             case 4:
128             case 5:
129 0           mon *= 10;
130 0           mon += u;
131 0           break;
132             case 6:
133             case 7:
134 0           day *= 10;
135 0           day += u;
136 0           break;
137             case 8:
138             case 9:
139 0           hour *= 10;
140 0           hour += u;
141 0           break;
142             case 10:
143             case 11:
144 0           min *= 10;
145 0           min += u;
146 0           break;
147             case 12:
148             case 13:
149 0           sec *= 10;
150 0           sec += u;
151 0           break;
152             }
153             }
154              
155 0           utcdate2tai(t,year,mon-1,day,hour,min,sec);
156 0           return 1;
157             }
158              
159              
160             static const char *months[12] =
161             {
162             "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"
163             } ;
164              
165 77           static int get_month(char *buf, unsigned int len)
166             {
167             int i;
168 77 100         if (len < 3) return -1;
169             #define CMP(x) \
170             (months[i][x]==buf[x] || months[i][x]==buf[x]+32)
171              
172 533 100         for (i = 0;i < 12;++i)
173 500 50         if (CMP(0) && CMP(1) && CMP(2))
    100          
    100          
    100          
    100          
    100          
174             return i;
175             return -1;
176             }
177              
178             /* see http://cr.yp.to/ftp/list/eplf.html */
179             static int
180 0           parse_eplf(struct ftpparse *f, char *buf, unsigned int len)
181             {
182             unsigned int start,pos;
183             unsigned long ul;
184 0 0         if (buf[0]!='+') return 0;
185              
186             start=1;
187 0 0         for (pos = 1;pos < len;pos++) {
188 0 0         if ('\t'==buf[pos]) {
189 0           f->name=buf+pos+1;
190 0           f->namelen=len-pos-1;
191 0 0         if (!f->namelen) return 0; /* huh? */
192 0           f->format=FTPPARSE_FORMAT_EPLF;
193 0           return 1;
194             }
195 0 0         if (',' != buf[pos])
196 0           continue;
197 0           switch(buf[start]) {
198 0           case '/': f->flagtrycwd=1; break;
199 0           case 'r': f->flagtryretr=1; break;
200             case 's':
201 0 0         if (pos-start-1==0) return 0;
202 0 0         if (get_uint64(buf+start+1,pos-start-1,&f->size)
203             !=pos-start-1) return 0;
204 0           f->sizetype=FTPPARSE_SIZE_BINARY;
205 0           break;
206             case 'm':
207 0 0         if (pos-start-1==0) return 0;
208 0 0         if (get_ulong(buf+start+1,pos-start-1,&ul)!=pos-start-1) return 0;
209 0           tai_unix(&f->mtime,ul);
210 0           f->mtimetype = FTPPARSE_MTIME_LOCAL;
211 0           break;
212             case 'i':
213             /* refuse zero bytes length ids */
214 0 0         if (pos-start-1==0) return 0;
215 0           f->idtype = FTPPARSE_ID_FULL;
216 0           f->id=buf+start+1;
217 0           f->idlen=pos-start-1;
218 0           break;
219             }
220 0           start=pos+1;
221             }
222             return 0;
223             }
224              
225 46           static int scan_time(const char *buf, const unsigned int len,
226             unsigned long *h, unsigned long *m, unsigned long *s, int *type)
227             {
228             /* 11:48:54 */
229             /* 01:48:54 */
230             /* 1:48:54 */
231             /* 11:48 */
232             /* 11:48PM */
233             /* 11:48AM */
234             /* 11:48:54PM */
235             /* 11:48:54AM */
236             unsigned int x;
237             unsigned int y;
238 46           *h=*m=*s=0;
239              
240             x=get_ulong(buf,len,h);
241 46 50         if (len==x) return 0;
242 46 50         if (!x || x>2) return 0;
243              
244 46 50         if (':' != buf[x]) return 0;
245 46 50         if (len==++x) return 0;
246              
247 46           y=get_ulong(buf+x,len-x,m);
248 46 50         if (y!=2) return 0;
249 46           x+=y;
250              
251 46 100         if (x!=len && ':' == buf[x]) {
    100          
252 1 50         if (len==++x) return 0;
253              
254 1           y=get_ulong(buf+x,len-x,s);
255 1 50         if (y!=2) return 0;
256 1           x+=y;
257 1           *type=FTPPARSE_MTIME_REMOTESECOND;
258             } else
259 45           *type=FTPPARSE_MTIME_REMOTEMINUTE;
260              
261 46 100         if (x!=len && ('A' == buf[x] || 'P' == buf[x])) {
    50          
262 8 50         if ('P' == buf [x])
263 0           *h+=12;
264 8           x++;
265 8 50         if (len==x) return 0;
266 8 50         if ('M' != buf[x]) return 0;
267 8           x++;
268 8 50         if (len==x) return 0;
269             }
270 46 100         if (len==x || buf[x]==' ') return x;
    50          
271             return 0;
272             }
273              
274             /* 04-27-00 09:09PM licensed */
275             /* 07-18-00 10:16AM pub */
276             /* 04-14-00 03:47PM 589 readme.htm */
277             /* note the mon-day-year! */
278             static int
279 8           parse_msdos(struct ftpparse *f, char *buf, unsigned int len)
280             {
281             unsigned int pos,start;
282             unsigned int state;
283             unsigned long mon;
284             unsigned long day;
285             unsigned long year;
286             unsigned long hour;
287             unsigned long min;
288             unsigned long sec;
289             int mtimetype;
290             unsigned int x;
291             uint64 size=0;
292             unsigned int flagtrycwd=0;
293             unsigned int flagtryretr=0;
294             int maxspaces=0; /* to keep leading spaces before dir/file name */
295              
296 264 50         for (state=start=pos=0;pos
297 264           switch(state) {
298             case 0: /* month */
299 24 100         if ('-'==buf[pos]) {
300 8           state++;
301 8 50         if (pos==start) return 0;
302 16 50         if (get_ulong(buf+start,pos-start,&mon)!=pos-start) return 0;
303 8           start=pos+1;
304             }
305             break;
306             case 1: /* day */
307 24 100         if ('-'==buf[pos]) {
308 8           state++;
309 8 50         if (pos==start) return 0;
310 16 50         if (get_ulong(buf+start,pos-start,&day)!=pos-start) return 0;
311 8           start=pos+1;
312             }
313             break;
314             case 2: /* year */
315 24 100         if (' '==buf[pos]) {
316 8           state++;
317 8 50         if (pos-start!=2 && pos-start!=4) return 0;
318 16 50         if (get_ulong(buf+start,pos-start,&year)!=pos-start) return 0;
319 8           start=pos+1;
320 8 50         if (!fix_year(&year)) return 0;
321             }
322             break;
323             case 3: /* spaces */
324 16 100         if (' ' == buf[pos]) continue;
325 8           state++;
326             start=pos;
327             /* FALL THROUGH */
328             case 4: /* time */
329 8           x=scan_time(buf+start,len-pos,&hour,&min,&sec,&mtimetype);
330 8 50         if (!x) return 0;
331 8           pos+=x;
332 8 50         if (pos==len) return 0;
333 8           state++;
334 8           break;
335             case 5: /* spaces */
336 96 100         if (' ' == buf[pos]) continue;
337 8           state++;
338             start=pos;
339             /* FALL THROUGH */
340             case 6: /* or length */
341 44 100         if (' ' == buf[pos]) {
342 16 100         if (get_uint64(buf+start,pos-start,&size)!=pos-start) {
343 4 50         if (pos-start < 5
344 4 50         || !my_byte_equal(buf+start,5,""))
345             return 0;
346             flagtrycwd=1;
347             maxspaces=10;
348             } else {
349             flagtryretr=1;
350             maxspaces=1;
351             }
352 8           state++;
353             start=pos;
354             }
355             break;
356             case 7: /* spaces */
357 44 100         if (' ' == buf[pos])
358 36 50         if (--maxspaces)
359 36           continue;
360             state++;
361             start=pos;
362             /* FALL THROUGH */
363             case 8: /* file / dir name */
364 8           f->name=buf+start;
365 8           f->namelen=len-pos;
366 8           f->flagtrycwd=flagtrycwd;
367 8           f->flagtryretr=flagtryretr;
368 8           f->mtimetype=mtimetype;
369 8 100         if (flagtryretr) {
370 4           f->size=size;
371 4           f->sizetype=FTPPARSE_SIZE_BINARY;
372             }
373 8 50         if (!fix_year(&year)) return 0;
374 8           utcdate2tai(&f->mtime,year,mon-1,day,hour,min,sec);
375 8           return 1;
376             }
377             }
378             return 0;
379             }
380              
381             #define MAXWORDS 10
382             static unsigned int
383 47           dosplit(char *buf, int len, char *p[], int l[])
384             {
385             unsigned int count=0;
386             int inword=0;
387             int pos;
388             int start;
389 2494 100         for (pos=start=0;pos
390 2448 100         if (inword) {
391 1576 100         if (' ' == buf[pos]) {
392             inword=0;
393 315           l[count++]=pos-start;
394 315 100         if (count==MAXWORDS) {
395 1           l[count-1]=len-start;
396 1           break;
397             }
398             }
399             } else {
400 872 100         if (' ' != buf[pos]) {
401             inword=1;
402             start=pos;
403 361           p[count]=buf+pos;
404             }
405             }
406             }
407 47 100         if (inword) {
408 46           l[count]=buf+pos-p[count];
409 46           count++;
410             }
411 47           return count;
412             }
413              
414 9           static int parse_multinet(struct ftpparse *f, char *p[], int l[],
415             unsigned int count)
416             {
417             /* "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)" */
418             /* "[VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09:" */
419             int mon;
420             unsigned long day;
421             unsigned long year;
422             unsigned long hour;
423             unsigned long min;
424             unsigned long sec;
425             int mtimetype;
426             int x;
427             char *q;
428             int m;
429 9 100         if (count<4) return 0;
430              
431 1           q=p[2];
432 1           m=l[2];
433              
434 1           x=get_ulong(q,m,&day);
435 1 50         if (!x || x>2 || day>31) return 0;
    50          
436 1 50         if (q[x] != '-') return 0;
437 1           q+=x+1;
438 1           m-=x+1;
439 1           mon=get_month(q,m);
440 1 50         if (-1==mon) return 0;
441 1 50         if (q[3]!='-') return 0;
442 1           q+=4;
443 1 50         if (m<5) return 0;
444 1           m-=4;
445 1           x=get_ulong(q,m,&year);
446 1 50         if (!x || q[x]!=' ') return 0;
    50          
447 1 50         if (!fix_year(&year)) return 0;
448              
449 1           x=scan_time(p[3],l[3],&hour,&min,&sec,&mtimetype);
450 1 50         if (x!=l[3]) return 0;
451              
452 1           f->mtimetype = mtimetype;;
453 1           utcdate2tai (&f->mtime,year,mon,day,hour,min,sec);
454              
455 11 50         for (x=0;x
456 11 100         if (p[0][x]==';')
457             break;
458 1 50         if (x>4)
459 1 50         if (p[0][x-4]=='.'
460 1 50         && p[0][x-3]=='D'
461 1 50         && p[0][x-2]=='I'
462 0 0         && p[0][x-1]=='R') {
463 0           x-=4;
464 0           f->flagtrycwd=1;
465             }
466 1 50         if (!f->flagtrycwd)
467 1           f->flagtryretr=1;
468              
469 1           f->namelen=x;
470 1           f->name=p[0];
471 1 50         if (f->name[0]=='[') {
472             /* [dir]file.maybe */
473             unsigned int y;
474 0 0         for (y=1;ynamelen;y++)
475 0 0         if (f->name[y]==']')
476             break;
477 0 0         if (y!=f->namelen) y++; /* skip ] */
478 0 0         if (y!=f->namelen) {
479 0           f->name+=y;
480 0           f->namelen-=y;
481             }
482             }
483             return 1;
484             }
485             static int
486 39           parse_unix(struct ftpparse *f, char *buf, int len,
487             char *p[], int l[], unsigned int count)
488             {
489              
490             /* the horror ... */
491              
492             /* this list has been taken from Daniel Bernsteins ftpparse.c */
493              
494             /* UNIX-style listing, without inum and without blocks */
495             /* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */
496             /* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */
497             /* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */
498             /* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */
499             /* Also produced by Microsoft's FTP servers for Windows: */
500             /* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */
501             /* "d--------- 1 owner group 0 May 9 19:45 Softlib" */
502             /* Also WFTPD for MSDOS: */
503             /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
504             /* Also NetWare: */
505             /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
506             /* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */
507             /* Also NetPresenz for the Mac: */
508             /* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
509             /* "drwxrwxr-x folder 2 May 10 1996 network" */
510             /*restructured: */
511             /* -PERM 1 user group 531 Jan 29 03:26 README */
512             /* dPERM 2 user group 512 Apr 8 1994 etc */
513             /* dPERM 2 user 512 Apr 8 1994 etc */
514             /* lPERM 1 user group 7 Jan 25 00:17 bin -> usr/bin */
515             /* -PERM 1 user group 1803128 Jul 10 10:18 ls-lR.Z */
516             /* dPERM 1 user group 0 May 9 19:45 Softlib */
517             /* -PERM 1 user group 322 Aug 19 1996 message.ftp */
518             /* d [R----F--] user 512 Jan 16 18:53 login */
519             /* - [R----F--] user 214059 Oct 20 15:27 cx.exe */
520             /* -PERM 326 NUMB NUMBER Nov 22 1995 MegaPhone.sit */
521             /* dPERM folder 2 May 10 1996 network */
522             /* handled as: */
523             /* dPERM folder 2 May 10 1996 network */
524             /* 0 1 2 3 4 5 6 7 8 */
525              
526             /* note the date system: MON DAY [YEAR|TIME] */
527              
528              
529             int mon=-1; /* keep gcc quiet */
530             unsigned long day;
531             unsigned long year;
532             unsigned long hour;
533             unsigned long min;
534             unsigned long sec;
535             uint64 size;
536             int flagtrycwd=0;
537             int flagtryretr=0;
538             unsigned int i;
539             int x;
540             int mtimetype;
541             int may_have_size=0;
542              
543 39           switch(p[0][0]) {
544 13           case 'd': flagtrycwd=1; break;
545 24           case '-': flagtryretr=1; break;
546 1           case 'l': flagtryretr=flagtrycwd=1; break;
547             }
548             i=3;
549 39 50         if (l[1]==6 && my_byte_equal(p[1],l[1],"folder"))
    0          
550             i=2;
551              
552 39           x=get_uint64(p[i],l[i],&size);
553 39 100         if (x==l[i]) may_have_size=1;
554 39           i++;
555              
556 77 100         while (i
557 76           mon=get_month(p[i],l[i]);
558 76 100         if (-1==mon) {
559             /* may be size */
560 38           x=get_uint64(p[i],l[i],&size);
561 38 50         if (x==l[i]) may_have_size=1;
562             }
563 76           i++;
564 76 100         if (-1!=mon) break;
565             }
566 39 50         if (i==count) return 0;
567              
568 39           x=get_ulong(p[i],l[i],&day);
569 39 100         if (!x) return 0;
570 38 50         if (p[i][x]!=' ') return 0;
571 38 50         if (++i==count) return 0;
572              
573 38           x=get_ulong(p[i],l[i],&year);
574 38 50         if (!x) return 0;
575 38 100         if (p[i][x]==':') {
576 37           x=scan_time(p[i],l[i],&hour,&min,&sec,&mtimetype);
577 37 50         if (x!=l[i]) return 0;
578 37           year=guess_year(mon,day);
579             } else {
580 1           mtimetype=FTPPARSE_MTIME_REMOTEDAY;
581 1           hour=min=sec=0;
582             /* may be this case: */
583             /* - [-RWCE-F-] mlm 11820 Feb 3 93 12:00 drivers.doc */
584 1 50         if (i+2
585 0           x=scan_time(p[i+1],l[i+1],&hour,&min,&sec,&mtimetype);
586 0 0         if (x!=l[i+1]) {
587 0           hour=min=sec=0;
588 0           mtimetype=FTPPARSE_MTIME_REMOTEDAY;
589             } else
590             i++;
591             }
592 1 50         if (!fix_year(&year)) return 0;
593             }
594 38 50         if (++i==count) return 0;
595             /* note: dosplit eats spaces - but we need them here. So go back. */
596 38           f->name=p[i];
597 38           f->namelen=buf+len-p[i];
598             /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
599             /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 spacy" */
600             /* but: */
601             /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
602 38 50         if (p[0][1]!=' ') {
603 38 50         while (f->name[-2]==' ') {
604 0           f->name--;
605 0           f->namelen++;
606             }
607             }
608 38 50         if (may_have_size) {
609 38           f->sizetype=FTPPARSE_SIZE_BINARY;
610 38           f->size=size;
611             }
612 38           f->flagtryretr=flagtryretr;
613 38           f->flagtrycwd=flagtrycwd;
614 38           utcdate2tai (&f->mtime,year,mon,day,hour,min,sec);
615 38           f->mtimetype=mtimetype;
616 38           f->format=FTPPARSE_FORMAT_LS; /* for programs dealing with symlinks */
617              
618 38 100         if ('l'==*buf) {
619             unsigned int j;
620 6 50         for (j=1;jnamelen-4;j++) /* 1, -4: no empty names, please */
621 6 100         if (f->name[j]==' '
622 1 50         && f->name[j+1]=='-'
623 1 50         && f->name[j+2]=='>'
624 1 50         && f->name[j+3]==' ') {
625 1           f->symlink=f->name+j+4;
626 1           f->symlinklen=f->namelen-j-4;
627 1           f->namelen=j;
628 1           break;
629             }
630             }
631             return 1;
632             }
633 8           static int parse_supertcp(struct ftpparse *f, char *p[], int l[],
634             unsigned int count)
635             {
636             unsigned long mon;
637             unsigned long day;
638             unsigned long year;
639             unsigned long hour;
640             unsigned long min;
641             unsigned long sec;
642             int mtimetype;
643             uint64 size=0; /* optional, dirs */
644             int x;
645             int dir=0;
646              
647             /* CMT 11-21-94 10:17 */
648             /* DESIGN1.DOC 11264 05-11-95 14:20 */
649              
650 8 50         if (count<4) return 0;
651 0           x=scan_time(p[3],l[3],&hour,&min,&sec,&mtimetype);
652 0 0         if (x!=l[3]) return 0;
653              
654              
655 0           x=get_ulong(p[2],l[2],&mon);
656 0 0         if (x!=2 || p[2][x]!='-') return 0;
    0          
657 0           x++;
658 0           x+=get_ulong(p[2]+x,l[2]-x,&day);
659 0 0         if (x!=5 || p[2][x]!='-') return 0;
    0          
660 0           x++;
661 0           x+=get_ulong(p[2]+x,l[2]-x,&year);
662 0 0         if ((x!=8 && x!=10) || p[2][x]!=' ') return 0;
    0          
663 0 0         if (!fix_year(&year)) return 0;
664 0 0         if (my_byte_equal(p[1],5,""))
665             dir=1;
666             else {
667 0           x=get_uint64(p[1],l[1],&size);
668 0 0         if (!x || p[1][x]!=' ') return 0;
    0          
669             }
670              
671 0           f->name=p[0];
672 0           f->namelen=l[0];
673 0           f->size=size;
674 0 0         if (!dir)
675 0           f->sizetype=FTPPARSE_SIZE_BINARY;
676 0           utcdate2tai (&f->mtime,year,mon,day,hour,min,sec);
677 0           f->mtimetype=mtimetype;
678 0 0         if (dir) f->flagtrycwd=1;
679 0           else f->flagtryretr=1;
680             return 1;
681             }
682              
683             /* another bright re-invention of a broken wheel from the people, who
684             * made an art of it.
685             */
686             static int
687 0           parse_os2(struct ftpparse *f, char *p[], int l[],
688             unsigned int count)
689             {
690             /* 0 DIR 04-11-95 16:26 ADDRESS
691             * 612 A 07-28-95 16:45 air_tra1.bag
692             * 310992 06-28-94 09:56 INSTALL.EXE
693             */
694             unsigned long mon;
695             unsigned long day;
696             unsigned long year;
697             unsigned long hour;
698             unsigned long min;
699             unsigned long sec;
700             int mtimetype;
701             uint64 size;
702             int x;
703             unsigned int i;
704             int dir=0;
705 0 0         if (count<4) return 0;
706              
707 0           x=get_uint64(p[0],l[0],&size);
708 0 0         if (!x || p[0][x]!=' ') return 0;
    0          
709            
710 0 0         for (i=1; i
711 0           x=get_ulong(p[i],l[i],&mon);
712 0 0         if (!x) continue;
713 0 0         if (x!=2 || p[i][x]!='-') return 0;
    0          
714 0           mon-=1;
715 0           x++;
716 0           x+=get_ulong(p[i]+x,l[i]-x,&day);
717 0 0         if (x!=5 || p[i][x]!='-') return 0;
    0          
718 0           x++;
719 0           x+=get_ulong(p[i]+x,l[i]-x,&year);
720 0 0         if (x!=8 || p[i][x]!=' ') return 0;
    0          
721 0 0         if (!fix_year(&year)) return 0;
722             break;
723             }
724 0 0         if (i>1)
725 0 0         if (my_byte_equal(p[i-1],3,"DIR"))
726             dir=1;
727 0           i++;
728              
729 0 0         if (i==count) return 0;
730 0           x=scan_time(p[i],l[i],&hour,&min,&sec,&mtimetype);
731 0 0         if (x!=l[i]) return 0;
732 0           i++;
733 0 0         if (i==count) return 0;
734              
735 0           f->name=p[i];
736 0           f->namelen=l[i];
737 0 0         if (dir) {
738 0           f->flagtrycwd=1;
739             } else {
740 0           f->flagtryretr=1;
741 0           f->sizetype=FTPPARSE_SIZE_BINARY;
742 0           f->size=size;
743             }
744 0           utcdate2tai (&f->mtime,year,mon,day,hour,min,sec);
745 0           f->mtimetype=mtimetype;
746 0           return 1;
747             }
748              
749             #define SETUP() do {\
750             fp->name = 0; \
751             fp->namelen = 0; \
752             fp->flagtrycwd = 0; \
753             fp->flagtryretr = 0; \
754             fp->sizetype = FTPPARSE_SIZE_UNKNOWN; \
755             fp->size = 0; \
756             fp->mtimetype = FTPPARSE_MTIME_UNKNOWN; \
757             tai_uint(&fp->mtime,0); \
758             fp->idtype = FTPPARSE_ID_UNKNOWN; \
759             fp->id = 0; \
760             fp->idlen = 0; \
761             fp->format = FTPPARSE_FORMAT_UNKNOWN; \
762             fp->flagbrokenmlsx=0; \
763             fp->symlink=0; \
764             fp->symlinklen=0; \
765             } while(0)
766              
767             int
768 0           ftpparse_mlsx (struct ftpparse *fp, char *x, int ll, int is_mlst)
769             {
770             int i;
771             uint64 size;
772             struct tai mtime;
773             int flagtryretr=0;
774             int flagtrycwd=0;
775             char *id=0;
776             int idlen=0;
777             int mtimetype=FTPPARSE_MTIME_UNKNOWN;
778             int sizetype=FTPPARSE_SIZE_UNKNOWN;
779             int flagbrokenmlsx=0;
780 0           SETUP();
781 0 0         if (is_mlst)
782 0 0         if (ll>1) {
783 0           ll--;
784 0           x++;
785             }
786 0 0         if (ll<2) /* empty facts, space, one-byte-filename */
787             return 0;
788              
789 0 0         for (i=0; i
790             int j=0,k=0;
791 0 0         if (x[i]==' ')
792             break; /* end of facts */
793 0 0         while (i+j
    0          
    0          
    0          
794 0           j++;
795 0 0         if (i+j==ll)
796             return 0;
797 0 0         if (x[i+j]==' ')
798             return 0;
799 0 0         if (x[i+j]==';')
800             return 0;
801             /* x[i+j] is now '=' */
802 0 0         while (i+j+k
    0          
    0          
803 0           k++;
804 0 0         if (i+j+k==ll)
805             return 0;
806             /* x[i+j+k] is space or semicolon, so use of getlong is safe */
807             #define ISFACT(name) (j==sizeof(name)-1 && case_startb(x+i,j,name))
808 0 0         if (ISFACT ("size")) {
    0          
809 0           get_uint64 (x + i + j + 1, k - 1,&size);
810             sizetype=FTPPARSE_SIZE_BINARY;
811 0 0         } else if (ISFACT ("modify")) {
    0          
812 0           getmod(&mtime,x + i + j + 1, k - 1);
813 0           mtimetype = FTPPARSE_MTIME_LOCAL;
814 0 0         } else if (ISFACT ("type")) {
    0          
815 0 0         if (k==5 && case_startb (x + i + j + 1, 4, "file"))
    0          
816             flagtryretr = 1;
817 0 0         else if (case_startb (x + i + j + 1, 3, "dir")) /* "current" */
818             flagtrycwd = 1;
819 0 0         else if (case_startb (x + i + j + 1, 4, "pdir")) /* "parent" */
820             flagtrycwd = 1;
821 0 0         else if (case_startb (x + i + j + 1, 4, "cdir"))
822             flagtrycwd = 1;
823             else {
824             flagtryretr = 1;
825             flagtrycwd = 1;
826             }
827 0 0         } else if (ISFACT ("unique")) {
    0          
828 0           id = x + i + j + 1;
829 0           idlen = k - 1;
830             }
831 0           i+=j+k;
832 0 0         if (x[i]==' ') {
833             flagbrokenmlsx=1;
834             break;
835             }
836             }
837 0 0         if (ll==i) return 0;
838 0           i++;
839 0 0         if (ll==i) return 0;
840 0           fp->name = x + i;
841 0           fp->namelen = ll - i;
842 0           fp->sizetype = sizetype;
843 0           fp->size=size;
844 0           fp->mtimetype = mtimetype;
845 0           fp->mtime=mtime;
846 0           fp->flagtrycwd=flagtrycwd;
847 0           fp->flagtryretr=flagtryretr;
848 0 0         if (id) {
849 0           fp->idtype = FTPPARSE_ID_FULL;
850 0           fp->id=id;
851 0           fp->idlen=idlen;
852             }
853 0           fp->flagbrokenmlsx=flagbrokenmlsx;
854 0           fp->format=FTPPARSE_FORMAT_MLSX;
855 0           return 1;
856             }
857              
858             static int
859 59           ftpparse_int(struct ftpparse *fp,char *buf,int len)
860             {
861             unsigned int count;
862             char *p[MAXWORDS];
863 59           int l[MAXWORDS] = { 0,0,0,0,0,0,0,0,0,0 };
864              
865 59           SETUP();
866              
867 59 100         if (len < 2) /* an empty name in EPLF, with no info, could be 2 chars */
868             return 0;
869              
870             /* cheap cases first */
871 55           switch (*buf) {
872             case '+':
873 0 0         if (parse_eplf(fp,buf,len))
874             return 1;
875             break;
876             case '0': case '1': case '2': case '3': case '4':
877             case '5': case '6': case '7': case '8': case '9':
878 8 50         if (parse_msdos(fp,buf,len)) return 1;
879             break;
880             }
881              
882 47           count=dosplit(buf, len, p,l);
883              
884 47 100         switch(*buf) {
885             case 'b': case 'c': case 'd': case 'l':
886             case 'p': case 's': case '-':
887 39 100         if (parse_unix(fp,buf,len,p,l,count)) return 1;
888             break;
889             }
890              
891 9 50         if (*buf==' ') {
892 0 0         switch(p[0][0]) {
893             case '0': case '1': case '2': case '3': case '4':
894             case '5': case '6': case '7': case '8': case '9':
895 0 0         if (parse_os2(fp,p,l,count)) return 1;
896             break;
897             }
898             }
899              
900 9 100         if (parse_multinet(fp,p,l,count)) return 1;
901 8 50         if (parse_supertcp(fp,p,l,count)) return 1;
902              
903 8           return 0;
904             }
905             int
906 59           ftpparse(struct ftpparse *fp,char *buf,int len, int eat_leading_spaces)
907             {
908 59           int x=ftpparse_int(fp,buf,len);
909 59 100         if (!x) return x;
910 47 50         if (eat_leading_spaces && fp->format!=FTPPARSE_FORMAT_EPLF
    0          
911 0 0         && fp->format!=FTPPARSE_FORMAT_MLSX)
912 0 0         while (fp->namelen > 1 && fp->name[0]==' ') {
    0          
913             /* leave at least a " " in the name */
914 0           fp->name++;
915 0           fp->namelen--;
916             }
917             return x;
918             }