1 /*
2 * lftp - file transfer program
3 *
4 * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21 #include "FtpListInfo.h"
22 #include "FileSet.h"
23 #include <assert.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include "xstring.h"
27 #include <ctype.h>
28 #include "misc.h"
29 #include "ftpclass.h"
30 #include "ascii_ctype.h"
31
32 #define number_of_parsers 7
33
Parse(const char * buf,int len)34 FileSet *FtpListInfo::Parse(const char *buf,int len)
35 {
36 if(mode==FA::LONG_LIST || mode==FA::MP_LIST)
37 {
38 if(len==0 && mode==FA::LONG_LIST
39 && !ResMgr::QueryBool("ftp:list-empty-ok",session->GetHostName()))
40 {
41 mode=FA::LIST;
42 return 0;
43 }
44 int err;
45 FileSet *set=session->ParseLongList(buf,len,&err);
46 if(!set || err>0)
47 {
48 if(mode==FA::MP_LIST)
49 mode=FA::LONG_LIST;
50 else
51 mode=FA::LIST;
52 }
53 return set;
54 }
55 else
56 {
57 return ParseShortList(buf,len);
58 }
59 }
60
ParseLongList(const char * buf,int len,int * err_ret) const61 FileSet *Ftp::ParseLongList(const char *buf,int len,int *err_ret) const
62 {
63 if(err_ret)
64 *err_ret=0;
65
66 int err[number_of_parsers];
67 FileSet *set[number_of_parsers];
68 int i;
69 for(i=0; i<number_of_parsers; i++)
70 {
71 err[i]=0;
72 set[i]=new FileSet;
73 }
74
75 xstring line;
76 xstring tmp_line;
77
78 FtpLineParser guessed_parser=0;
79 FileSet **the_set=0;
80 int *the_err=0;
81 int *best_err1=&err[0];
82 int *best_err2=&err[1];
83
84 const char *tz=Query("timezone",hostname);
85
86 for(;;)
87 {
88 const char *nl=(char*)memchr(buf,'\n',len);
89 if(!nl)
90 break;
91 line.nset(buf,nl-buf);
92 line.chomp('\r');
93 if(line.length()==0)
94 {
95 len-=nl+1-buf;
96 buf=nl+1;
97 continue;
98 }
99
100 len-=nl+1-buf;
101 buf=nl+1;
102
103 if(!guessed_parser)
104 {
105 for(i=0; i<number_of_parsers; i++)
106 {
107 tmp_line.set(line); // parser can clobber the line - work on a copy
108 FileInfo *info=(*line_parsers[i])(tmp_line.get_non_const(),&err[i],tz);
109 if(info && info->name.length()>1)
110 info->name.chomp('/');
111 if(info && !strchr(info->name,'/'))
112 set[i]->Add(info);
113 else
114 delete info;
115
116 if(*best_err1>err[i])
117 best_err1=&err[i];
118 if(*best_err2>err[i] && best_err1!=&err[i])
119 best_err2=&err[i];
120 if(*best_err1>16)
121 goto leave; // too many errors with best parser.
122 }
123 if(*best_err2 > (*best_err1+1)*16)
124 {
125 i=best_err1-err;
126 guessed_parser=line_parsers[i];
127 the_set=&set[i];
128 the_err=&err[i];
129 }
130 }
131 else
132 {
133 FileInfo *info=(*guessed_parser)(line.get_non_const(),the_err,tz);
134 if(info && info->name.length()>1)
135 info->name.chomp('/');
136 if(info && !strchr(info->name,'/'))
137 (*the_set)->Add(info);
138 else
139 delete info;
140 }
141 }
142 if(!the_set)
143 {
144 i=best_err1-err;
145 the_set=&set[i];
146 the_err=&err[i];
147 }
148 leave:
149 for(i=0; i<number_of_parsers; i++)
150 if(&set[i]!=the_set)
151 delete set[i];
152 if(err_ret && the_err)
153 *err_ret=*the_err;
154 return the_set?*the_set:0;
155 }
156
ParseShortList(const char * buf,int len)157 FileSet *FtpListInfo::ParseShortList(const char *buf,int len)
158 {
159 FileSet *set=new FileSet;
160 char *line=0;
161 int line_alloc=0;
162 int line_len;
163 for(;;)
164 {
165 // workaround for some ftp servers
166 if(len>=2 && buf[0]=='.' && buf[1]=='/')
167 {
168 buf+=2;
169 len-=2;
170 }
171 #if 0 // not possible here
172 if(len>=2 && buf[0]=='/' && buf[1]=='/')
173 {
174 buf++;
175 len--;
176 }
177 #endif
178
179 const char *nl=(const char*)memchr(buf,'\n',len);
180 if(!nl)
181 break;
182 line_len=nl-buf;
183 if(line_len>0 && buf[line_len-1]=='\r')
184 line_len--;
185 FileInfo::type type=FileInfo::UNKNOWN;
186 const char *slash=(const char*)memchr(buf,'/',line_len);
187 if(slash)
188 {
189 type=FileInfo::DIRECTORY;
190 line_len=slash-buf;
191 }
192 if(line_len==0)
193 {
194 len-=nl+1-buf;
195 buf=nl+1;
196 continue;
197 }
198 if(line_alloc<line_len+1)
199 line=string_alloca(line_alloc=line_len+128);
200 memcpy(line,buf,line_len);
201 line[line_len]=0;
202
203 len-=nl+1-buf;
204 buf=nl+1;
205
206 if(!strchr(line,'/'))
207 {
208 FileInfo *fi=new FileInfo(line);
209 if(type!=fi->UNKNOWN)
210 fi->SetType(type);
211 set->Add(fi);
212 }
213 }
214 return set;
215 }
216
217 static
ParseFtpLongList_UNIX(char * line,int * err,const char * tz)218 FileInfo *ParseFtpLongList_UNIX(char *line,int *err,const char *tz)
219 {
220 int tmp;
221 if(sscanf(line,"total %d",&tmp)==1)
222 return 0;
223 if(!strncasecmp(line,"Status of ",10))
224 return 0; // STAT output.
225 if(strchr("bcpsD",line[0])) // block, char, pipe, socket, Door.
226 return 0;
227
228 FileInfo *fi=FileInfo::parse_ls_line(line,tz);
229 if(!fi)
230 {
231 (*err)++;
232 return 0;
233 }
234 return fi;
235 }
236
237 #define FIRST_TOKEN strtok(line," \t")
238 #define NEXT_TOKEN strtok(NULL," \t")
239 #define ERR do{(*err)++;delete fi;return(0);}while(0)
240
241 /*
242 07-13-98 09:06PM <DIR> aix
243 07-13-98 09:06PM <DIR> hpux
244 07-13-98 09:06PM <DIR> linux
245 07-13-98 09:06PM <DIR> ncr
246 07-13-98 09:06PM <DIR> solaris
247 03-18-98 06:01AM 2109440 nlxb318e.tar
248 07-02-98 11:17AM 13844 Whatsnew.txt
249 */
250 static
ParseFtpLongList_NT(char * line,int * err,const char * tz)251 FileInfo *ParseFtpLongList_NT(char *line,int *err,const char *tz)
252 {
253 char *t = FIRST_TOKEN;
254 FileInfo *fi=0;
255 if(t==0)
256 ERR;
257 int month,day,year;
258 if(sscanf(t,"%2d-%2d-%2d",&month,&day,&year)!=3)
259 ERR;
260 if(year>=70)
261 year+=1900;
262 else
263 year+=2000;
264
265 t = NEXT_TOKEN;
266 if(t==0)
267 ERR;
268 int hour,minute;
269 char am='A'; // AM/PM is optional
270 if(sscanf(t,"%2d:%2d%c",&hour,&minute,&am)<2)
271 ERR;
272 t = NEXT_TOKEN;
273 if(t==0)
274 ERR;
275
276 if(am=='P') // PM - after noon
277 {
278 hour+=12;
279 if(hour==24)
280 hour=0;
281 }
282 struct tm tms;
283 tms.tm_sec=30; /* seconds after the minute [0, 61] */
284 tms.tm_min=minute; /* minutes after the hour [0, 59] */
285 tms.tm_hour=hour; /* hour since midnight [0, 23] */
286 tms.tm_mday=day; /* day of the month [1, 31] */
287 tms.tm_mon=month-1; /* months since January [0, 11] */
288 tms.tm_year=year-1900; /* years since 1900 */
289 tms.tm_isdst=-1;
290
291 fi=new FileInfo();
292 fi->SetDate(mktime_from_tz(&tms,tz),30);
293
294 long long size;
295 if(!strcmp(t,"<DIR>"))
296 fi->SetType(fi->DIRECTORY);
297 else
298 {
299 fi->SetType(fi->NORMAL);
300 if(sscanf(t,"%lld",&size)!=1)
301 ERR;
302 fi->SetSize(size);
303 }
304
305 t=strtok(NULL,"");
306 if(t==0)
307 ERR;
308 while(*t==' ')
309 t++;
310 if(*t==0)
311 ERR;
312 fi->SetName(t);
313
314 return fi;
315 }
316
317 /*
318 ASUSER 8192 04/26/05 13:54:16 *DIR dir/
319 ASUSER 8192 04/26/05 13:57:34 *DIR dir1/
320 ASUSER 365255 02/28/01 15:41:40 *STMF readme.txt
321 ASUSER 8489625 03/18/03 09:37:00 *STMF saved.zip
322 ASUSER 365255 02/28/01 15:41:40 *STMF unist.old
323 */
324 static
ParseFtpLongList_AS400(char * line,int * err,const char * tz)325 FileInfo *ParseFtpLongList_AS400(char *line,int *err,const char *tz)
326 {
327 char *t = FIRST_TOKEN;
328 FileInfo *fi=0;
329 if(t==0)
330 ERR;
331 char *user=t;
332
333 t = NEXT_TOKEN;
334 if(t==0)
335 ERR;
336 long long size;
337 if(sscanf(t,"%lld",&size)!=1)
338 ERR;
339
340 t = NEXT_TOKEN;
341 if(t==0)
342 ERR;
343 int month,day,year;
344 if(sscanf(t,"%2d/%2d/%2d",&month,&day,&year)!=3)
345 ERR;
346 if(year>=70)
347 year+=1900;
348 else
349 year+=2000;
350
351 t = NEXT_TOKEN;
352 if(t==0)
353 ERR;
354 int hour,minute,second;
355 if(sscanf(t,"%2d:%2d:%2d",&hour,&minute,&second)!=3)
356 ERR;
357 t = NEXT_TOKEN;
358 if(t==0)
359 ERR;
360
361 struct tm tms;
362 tms.tm_sec=second; /* seconds after the minute [0, 61] */
363 tms.tm_min=minute; /* minutes after the hour [0, 59] */
364 tms.tm_hour=hour; /* hour since midnight [0, 23] */
365 tms.tm_mday=day; /* day of the month [1, 31] */
366 tms.tm_mon=month-1; /* months since January [0, 11] */
367 tms.tm_year=year-1900; /* years since 1900 */
368 tms.tm_isdst=-1;
369 time_t mtime=mktime_from_tz(&tms,tz);
370
371 t = NEXT_TOKEN;
372 if(t==0)
373 ERR;
374 FileInfo::type type=FileInfo::UNKNOWN;
375 if(!strcmp(t,"*DIR"))
376 type=FileInfo::DIRECTORY;
377 else
378 type=FileInfo::NORMAL;
379
380 t=strtok(NULL,"");
381 if(t==0)
382 ERR;
383 while(*t==' ')
384 t++;
385 if(*t==0)
386 ERR;
387 char *slash=strchr(t,'/');
388 if(slash)
389 {
390 if(slash==t)
391 return 0;
392 *slash=0;
393 type=FileInfo::DIRECTORY;
394 if(slash[1])
395 {
396 fi=new FileInfo(t);
397 fi->SetType(type);
398 return fi;
399 }
400 }
401 fi=new FileInfo(t);
402 fi->SetType(type);
403 fi->SetSize(size);
404 fi->SetDate(mtime,0);
405 fi->SetUser(user);
406 return fi;
407 }
408
409 /*
410 +i774.71425,m951188401,/, users
411 +i774.49602,m917883130,r,s79126, jgr_www2.exe
412
413 starts with +
414 comma separated
415 first character of field is type:
416 i - ?
417 m - modification time
418 / - means directory
419 r - means plain file
420 s - size
421 up - permissions in octal
422 \t - file name follows.
423 */
ParseFtpLongList_EPLF(char * line,int * err,const char *)424 FileInfo *ParseFtpLongList_EPLF(char *line,int *err,const char *)
425 {
426 int len=strlen(line);
427 const char *b=line;
428 FileInfo *fi=0;
429
430 if(len<2 || b[0]!='+')
431 ERR;
432
433 const char *name=0;
434 int name_len=0;
435 off_t size=NO_SIZE;
436 time_t date=NO_DATE;
437 long date_l;
438 long long size_ll;
439 bool dir=false;
440 bool type_known=false;
441 int perms=-1;
442
443 const char *scan=b+1;
444 int scan_len=len-1;
445 while(scan && scan_len>0)
446 {
447 switch(*scan)
448 {
449 case '\t': // the rest is file name.
450 name=scan+1;
451 name_len=scan_len-1;
452 scan=0;
453 break;
454 case 's':
455 if(1 != sscanf(scan+1,"%lld",&size_ll))
456 break;
457 size = size_ll;
458 break;
459 case 'm':
460 if(1 != sscanf(scan+1,"%ld",&date_l))
461 break;
462 date = date_l;
463 break;
464 case '/':
465 dir=true;
466 type_known=true;
467 break;
468 case 'r':
469 dir=false;
470 type_known=true;
471 break;
472 case 'i':
473 break;
474 case 'u':
475 if(scan[1]=='p') // permissions.
476 if(sscanf(scan+2,"%o",&perms)!=1)
477 perms=-1;
478 break;
479 default:
480 name=0;
481 scan=0;
482 break;
483 }
484 if(scan==0 || scan_len==0)
485 break;
486 const char *comma=find_char(scan,scan_len,',');
487 if(comma)
488 {
489 scan_len-=comma+1-scan;
490 scan=comma+1;
491 }
492 else
493 break;
494 }
495 if(name==0 || !type_known)
496 ERR;
497
498 fi=new FileInfo(xstring::get_tmp(name,name_len));
499 if(size!=NO_SIZE)
500 fi->SetSize(size);
501 if(date!=NO_DATE)
502 fi->SetDate(date,0);
503 if(type_known)
504 {
505 if(dir)
506 fi->SetType(fi->DIRECTORY);
507 else
508 fi->SetType(fi->NORMAL);
509 }
510 if(perms!=-1)
511 fi->SetMode(perms);
512
513 return fi;
514 }
515
516 /*
517 0 DIR 06-27-96 11:57 PROTOCOL
518 169 11-29-94 09:20 SYSLEVEL.MPT
519 */
520 static
ParseFtpLongList_OS2(char * line,int * err,const char * tz)521 FileInfo *ParseFtpLongList_OS2(char *line,int *err,const char *tz)
522 {
523 FileInfo *fi=0;
524
525 char *t = FIRST_TOKEN;
526 if(t==0)
527 ERR;
528
529 long long size;
530 if(sscanf(t,"%lld",&size)!=1)
531 ERR;
532 fi=new FileInfo;
533 fi->SetSize(size);
534
535 t = NEXT_TOKEN;
536 if(t==0)
537 ERR;
538 fi->SetType(fi->NORMAL);
539 if(!strcmp(t,"DIR"))
540 {
541 fi->SetType(fi->DIRECTORY);
542 t = NEXT_TOKEN;
543 if(t==0)
544 ERR;
545 }
546 int month,day,year;
547 if(sscanf(t,"%2d-%2d-%2d",&month,&day,&year)!=3)
548 ERR;
549 if(year>=70)
550 year+=1900;
551 else
552 year+=2000;
553
554 t = NEXT_TOKEN;
555 if(t==0)
556 ERR;
557 int hour,minute;
558 if(sscanf(t,"%2d:%2d",&hour,&minute)!=3)
559 ERR;
560
561 struct tm tms;
562 tms.tm_sec=30; /* seconds after the minute [0, 61] */
563 tms.tm_min=minute; /* minutes after the hour [0, 59] */
564 tms.tm_hour=hour; /* hour since midnight [0, 23] */
565 tms.tm_mday=day; /* day of the month [1, 31] */
566 tms.tm_mon=month-1; /* months since January [0, 11] */
567 tms.tm_year=year-1900; /* years since 1900 */
568 tms.tm_isdst=-1;
569 fi->SetDate(mktime_from_tz(&tms,tz),30);
570
571 t=strtok(NULL,"");
572 if(t==0)
573 ERR;
574 while(*t==' ')
575 t++;
576 if(*t==0)
577 ERR;
578 fi->SetName(t);
579
580 return fi;
581 }
582
583 static
ParseFtpLongList_MacWebStar(char * line,int * err,const char * tz)584 FileInfo *ParseFtpLongList_MacWebStar(char *line,int *err,const char *tz)
585 {
586 FileInfo *fi=0;
587
588 char *t = FIRST_TOKEN;
589 if(t==0)
590 ERR;
591
592 fi=new FileInfo;
593 switch(t[0])
594 {
595 case('l'): // symlink
596 fi->SetType(fi->SYMLINK);
597 break;
598 case('d'): // directory
599 fi->SetType(fi->DIRECTORY);
600 break;
601 case('-'): // plain file
602 fi->SetType(fi->NORMAL);
603 break;
604 case('b'): // block
605 case('c'): // char
606 case('p'): // pipe
607 case('s'): // sock
608 return 0; // ignore
609 default:
610 ERR;
611 }
612 mode_t mode=parse_perms(t+1);
613 if(mode==(mode_t)-1)
614 ERR;
615 // permissions are meaningless here.
616
617 // "folder" or 0
618 t = NEXT_TOKEN;
619 if(!t)
620 ERR;
621
622 if(strcmp(t,"folder"))
623 {
624 // size?
625 t = NEXT_TOKEN;
626 if(!t)
627 ERR;
628 // size
629 t = NEXT_TOKEN;
630 if(!t)
631 ERR;
632 if(isdigit((unsigned char)*t))
633 {
634 long long size;
635 if(sscanf(t,"%lld",&size)==1)
636 fi->SetSize(size);
637 }
638 else
639 ERR;
640 }
641 else
642 {
643 // ??
644 t = NEXT_TOKEN;
645 if(!t)
646 ERR;
647 }
648
649 // month
650 t = NEXT_TOKEN;
651 if(!t)
652 ERR;
653
654 struct tm date;
655 memset(&date,0,sizeof(date));
656
657 date.tm_mon=parse_month(t);
658 if(date.tm_mon==-1)
659 ERR;
660
661 const char *day_of_month = NEXT_TOKEN;
662 if(!day_of_month)
663 ERR;
664 date.tm_mday=atoi(day_of_month);
665
666 // time or year
667 t = NEXT_TOKEN;
668 if(!t)
669 ERR;
670 if(parse_year_or_time(t,&date.tm_year,&date.tm_hour,&date.tm_min)==-1)
671 ERR;
672
673 date.tm_isdst=-1;
674 date.tm_sec=30;
675 int prec=30;
676
677 if(date.tm_year==-1)
678 date.tm_year=guess_year(date.tm_mon,date.tm_mday,date.tm_hour,date.tm_min) - 1900;
679 else
680 {
681 date.tm_hour=12;
682 prec=12*60*60;
683 }
684
685 fi->SetDate(mktime_from_tz(&date,tz),prec);
686
687 char *name=strtok(NULL,"");
688 if(!name)
689 ERR;
690
691 // no symlinks on Mac, but anyway.
692 if(fi->filetype==fi->SYMLINK)
693 {
694 char *arrow=name;
695 while((arrow=strstr(arrow," -> "))!=0)
696 {
697 if(arrow!=name && arrow[4]!=0)
698 {
699 *arrow=0;
700 fi->SetSymlink(arrow+4);
701 break;
702 }
703 arrow++;
704 }
705 }
706 fi->SetName(name);
707
708 return fi;
709 }
710
711 /*
712 Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; /
713 Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; ..
714 Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin
715 Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub
716 Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ
717 modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name
718 modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name
719 modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www
720 */
ParseFtpLongList_MLSD(char * line,int * err,const char *)721 FileInfo *ParseFtpLongList_MLSD(char *line,int *err,const char *)
722 {
723 FileInfo *fi=0;
724
725 const char *name=0;
726 off_t size=NO_SIZE;
727 time_t date=NO_DATE;
728 const char *owner=0;
729 const char *group=0;
730 FileInfo::type type=FileInfo::UNKNOWN;
731 int perms=-1;
732
733 char *space=strstr(line,"; ");
734 if(space) {
735 name=space+2;
736 *space=0;
737 } else {
738 /* NcFTPd does not put a semicolon after last fact, workaround it. */
739 space=strchr(line,' ');
740 if(!space)
741 ERR;
742 name=space+1;
743 *space=0;
744 }
745
746 for(char *tok=strtok(line,";"); tok; tok=strtok(0,";"))
747 {
748 if(!strcasecmp(tok,"Type=cdir")
749 || !strcasecmp(tok,"Type=pdir")
750 || !strcasecmp(tok,"Type=dir"))
751 {
752 type=FileInfo::DIRECTORY;
753 continue;
754 }
755 if(!strcasecmp(tok,"Type=file"))
756 {
757 type=FileInfo::NORMAL;
758 continue;
759 }
760 if(!strcasecmp(tok,"Type=OS.unix=symlink"))
761 {
762 type=FileInfo::SYMLINK;
763 continue;
764 }
765 if(!strncasecmp(tok,"Modify=",7))
766 {
767 date=Ftp::ConvertFtpDate(tok+7);
768 continue;
769 }
770 if(!strncasecmp(tok,"Size=",5))
771 {
772 long long size_ll;
773 if(sscanf(tok+5,"%lld",&size_ll)==1)
774 size=size_ll;
775 continue;
776 }
777 if(!strncasecmp(tok,"Perm=",5))
778 {
779 perms=0;
780 for(tok+=5; *tok; tok++)
781 {
782 switch(to_ascii_lower(*tok))
783 {
784 case 'e': perms|=0111; break;
785 case 'l': perms|=0444; break;
786 case 'r': perms|=0444; break;
787 case 'c': perms|=0200; break;
788 case 'w': perms|=0200; break;
789 }
790 }
791 continue;
792 }
793 if(!strncasecmp(tok,"UNIX.mode=",10))
794 {
795 if(sscanf(tok+10,"%o",&perms)!=1)
796 perms=-1;
797 continue;
798 }
799 if(!strncasecmp(tok,"UNIX.owner=",11))
800 {
801 owner=tok+11;
802 continue;
803 }
804 if(!strncasecmp(tok,"UNIX.group=",11))
805 {
806 group=tok+11;
807 continue;
808 }
809 if(!strncasecmp(tok,"UNIX.uid=",9))
810 {
811 if(!owner)
812 owner=tok+9;
813 continue;
814 }
815 if(!strncasecmp(tok,"UNIX.gid=",9))
816 {
817 if(!group)
818 group=tok+9;
819 continue;
820 }
821 }
822 if(name==0 || !*name || type==FileInfo::UNKNOWN)
823 ERR;
824
825 fi=new FileInfo(name);
826 if(size!=NO_SIZE)
827 fi->SetSize(size);
828 if(date!=NO_DATE)
829 fi->SetDate(date,0);
830 fi->SetType(type);
831 if(perms!=-1)
832 fi->SetMode(perms);
833 if(owner)
834 fi->SetUser(owner);
835 if(group)
836 fi->SetGroup(group);
837
838 return fi;
839 }
840
841 Ftp::FtpLineParser Ftp::line_parsers[number_of_parsers]={
842 ParseFtpLongList_UNIX,
843 ParseFtpLongList_NT,
844 ParseFtpLongList_EPLF,
845 ParseFtpLongList_MLSD,
846 ParseFtpLongList_AS400,
847 ParseFtpLongList_OS2,
848 ParseFtpLongList_MacWebStar,
849 };
850