• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 /**
26  * Now implemented:
27  *
28  * 1) Unix version 1
29  * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
30  * 2) Unix version 2
31  * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
32  * 3) Unix version 3
33  * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
34  * 4) Unix symlink
35  * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
36  * 5) DOS style
37  * 01-29-97 11:32PM <DIR> prog
38  */
39 
40 #include "curl_setup.h"
41 
42 #ifndef CURL_DISABLE_FTP
43 
44 #include <curl/curl.h>
45 
46 #include "urldata.h"
47 #include "fileinfo.h"
48 #include "llist.h"
49 #include "strtoofft.h"
50 #include "ftp.h"
51 #include "ftplistparser.h"
52 #include "curl_fnmatch.h"
53 #include "curl_memory.h"
54 #include "multiif.h"
55 /* The last #include file should be: */
56 #include "memdebug.h"
57 
58 /* allocs buffer which will contain one line of LIST command response */
59 #define FTP_BUFFER_ALLOCSIZE 160
60 
61 typedef enum {
62   PL_UNIX_TOTALSIZE = 0,
63   PL_UNIX_FILETYPE,
64   PL_UNIX_PERMISSION,
65   PL_UNIX_HLINKS,
66   PL_UNIX_USER,
67   PL_UNIX_GROUP,
68   PL_UNIX_SIZE,
69   PL_UNIX_TIME,
70   PL_UNIX_FILENAME,
71   PL_UNIX_SYMLINK
72 } pl_unix_mainstate;
73 
74 typedef union {
75   enum {
76     PL_UNIX_TOTALSIZE_INIT = 0,
77     PL_UNIX_TOTALSIZE_READING
78   } total_dirsize;
79 
80   enum {
81     PL_UNIX_HLINKS_PRESPACE = 0,
82     PL_UNIX_HLINKS_NUMBER
83   } hlinks;
84 
85   enum {
86     PL_UNIX_USER_PRESPACE = 0,
87     PL_UNIX_USER_PARSING
88   } user;
89 
90   enum {
91     PL_UNIX_GROUP_PRESPACE = 0,
92     PL_UNIX_GROUP_NAME
93   } group;
94 
95   enum {
96     PL_UNIX_SIZE_PRESPACE = 0,
97     PL_UNIX_SIZE_NUMBER
98   } size;
99 
100   enum {
101     PL_UNIX_TIME_PREPART1 = 0,
102     PL_UNIX_TIME_PART1,
103     PL_UNIX_TIME_PREPART2,
104     PL_UNIX_TIME_PART2,
105     PL_UNIX_TIME_PREPART3,
106     PL_UNIX_TIME_PART3
107   } time;
108 
109   enum {
110     PL_UNIX_FILENAME_PRESPACE = 0,
111     PL_UNIX_FILENAME_NAME,
112     PL_UNIX_FILENAME_WINDOWSEOL
113   } filename;
114 
115   enum {
116     PL_UNIX_SYMLINK_PRESPACE = 0,
117     PL_UNIX_SYMLINK_NAME,
118     PL_UNIX_SYMLINK_PRETARGET1,
119     PL_UNIX_SYMLINK_PRETARGET2,
120     PL_UNIX_SYMLINK_PRETARGET3,
121     PL_UNIX_SYMLINK_PRETARGET4,
122     PL_UNIX_SYMLINK_TARGET,
123     PL_UNIX_SYMLINK_WINDOWSEOL
124   } symlink;
125 } pl_unix_substate;
126 
127 typedef enum {
128   PL_WINNT_DATE = 0,
129   PL_WINNT_TIME,
130   PL_WINNT_DIRORSIZE,
131   PL_WINNT_FILENAME
132 } pl_winNT_mainstate;
133 
134 typedef union {
135   enum {
136     PL_WINNT_TIME_PRESPACE = 0,
137     PL_WINNT_TIME_TIME
138   } time;
139   enum {
140     PL_WINNT_DIRORSIZE_PRESPACE = 0,
141     PL_WINNT_DIRORSIZE_CONTENT
142   } dirorsize;
143   enum {
144     PL_WINNT_FILENAME_PRESPACE = 0,
145     PL_WINNT_FILENAME_CONTENT,
146     PL_WINNT_FILENAME_WINEOL
147   } filename;
148 } pl_winNT_substate;
149 
150 /* This struct is used in wildcard downloading - for parsing LIST response */
151 struct ftp_parselist_data {
152   enum {
153     OS_TYPE_UNKNOWN = 0,
154     OS_TYPE_UNIX,
155     OS_TYPE_WIN_NT
156   } os_type;
157 
158   union {
159     struct {
160       pl_unix_mainstate main;
161       pl_unix_substate sub;
162     } UNIX;
163 
164     struct {
165       pl_winNT_mainstate main;
166       pl_winNT_substate sub;
167     } NT;
168   } state;
169 
170   CURLcode error;
171   struct fileinfo *file_data;
172   unsigned int item_length;
173   size_t item_offset;
174   struct {
175     size_t filename;
176     size_t user;
177     size_t group;
178     size_t time;
179     size_t perm;
180     size_t symlink_target;
181   } offsets;
182 };
183 
fileinfo_dtor(void * user,void * element)184 static void fileinfo_dtor(void *user, void *element)
185 {
186   (void)user;
187   Curl_fileinfo_cleanup(element);
188 }
189 
Curl_wildcard_init(struct WildcardData * wc)190 CURLcode Curl_wildcard_init(struct WildcardData *wc)
191 {
192   Curl_llist_init(&wc->filelist, fileinfo_dtor);
193   wc->state = CURLWC_INIT;
194 
195   return CURLE_OK;
196 }
197 
Curl_wildcard_dtor(struct WildcardData ** wcp)198 void Curl_wildcard_dtor(struct WildcardData **wcp)
199 {
200   struct WildcardData *wc = *wcp;
201   if(!wc)
202     return;
203 
204   if(wc->dtor) {
205     wc->dtor(wc->ftpwc);
206     wc->dtor = ZERO_NULL;
207     wc->ftpwc = NULL;
208   }
209   DEBUGASSERT(wc->ftpwc == NULL);
210 
211   Curl_llist_destroy(&wc->filelist, NULL);
212   free(wc->path);
213   wc->path = NULL;
214   free(wc->pattern);
215   wc->pattern = NULL;
216   wc->state = CURLWC_INIT;
217   free(wc);
218   *wcp = NULL;
219 }
220 
Curl_ftp_parselist_data_alloc(void)221 struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
222 {
223   return calloc(1, sizeof(struct ftp_parselist_data));
224 }
225 
226 
Curl_ftp_parselist_data_free(struct ftp_parselist_data ** parserp)227 void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
228 {
229   struct ftp_parselist_data *parser = *parserp;
230   if(parser)
231     Curl_fileinfo_cleanup(parser->file_data);
232   free(parser);
233   *parserp = NULL;
234 }
235 
236 
Curl_ftp_parselist_geterror(struct ftp_parselist_data * pl_data)237 CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
238 {
239   return pl_data->error;
240 }
241 
242 
243 #define FTP_LP_MALFORMATED_PERM 0x01000000
244 
ftp_pl_get_permission(const char * str)245 static unsigned int ftp_pl_get_permission(const char *str)
246 {
247   unsigned int permissions = 0;
248   /* USER */
249   if(str[0] == 'r')
250     permissions |= 1 << 8;
251   else if(str[0] != '-')
252     permissions |= FTP_LP_MALFORMATED_PERM;
253   if(str[1] == 'w')
254     permissions |= 1 << 7;
255   else if(str[1] != '-')
256     permissions |= FTP_LP_MALFORMATED_PERM;
257 
258   if(str[2] == 'x')
259     permissions |= 1 << 6;
260   else if(str[2] == 's') {
261     permissions |= 1 << 6;
262     permissions |= 1 << 11;
263   }
264   else if(str[2] == 'S')
265     permissions |= 1 << 11;
266   else if(str[2] != '-')
267     permissions |= FTP_LP_MALFORMATED_PERM;
268   /* GROUP */
269   if(str[3] == 'r')
270     permissions |= 1 << 5;
271   else if(str[3] != '-')
272     permissions |= FTP_LP_MALFORMATED_PERM;
273   if(str[4] == 'w')
274     permissions |= 1 << 4;
275   else if(str[4] != '-')
276     permissions |= FTP_LP_MALFORMATED_PERM;
277   if(str[5] == 'x')
278     permissions |= 1 << 3;
279   else if(str[5] == 's') {
280     permissions |= 1 << 3;
281     permissions |= 1 << 10;
282   }
283   else if(str[5] == 'S')
284     permissions |= 1 << 10;
285   else if(str[5] != '-')
286     permissions |= FTP_LP_MALFORMATED_PERM;
287   /* others */
288   if(str[6] == 'r')
289     permissions |= 1 << 2;
290   else if(str[6] != '-')
291     permissions |= FTP_LP_MALFORMATED_PERM;
292   if(str[7] == 'w')
293     permissions |= 1 << 1;
294   else if(str[7] != '-')
295       permissions |= FTP_LP_MALFORMATED_PERM;
296   if(str[8] == 'x')
297     permissions |= 1;
298   else if(str[8] == 't') {
299     permissions |= 1;
300     permissions |= 1 << 9;
301   }
302   else if(str[8] == 'T')
303     permissions |= 1 << 9;
304   else if(str[8] != '-')
305     permissions |= FTP_LP_MALFORMATED_PERM;
306 
307   return permissions;
308 }
309 
ftp_pl_insert_finfo(struct Curl_easy * data,struct fileinfo * infop)310 static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
311                                     struct fileinfo *infop)
312 {
313   curl_fnmatch_callback compare;
314   struct WildcardData *wc = data->wildcard;
315   struct ftp_wc *ftpwc = wc->ftpwc;
316   struct Curl_llist *llist = &wc->filelist;
317   struct ftp_parselist_data *parser = ftpwc->parser;
318   bool add = TRUE;
319   struct curl_fileinfo *finfo = &infop->info;
320 
321   /* set the finfo pointers */
322   char *str = Curl_dyn_ptr(&infop->buf);
323   finfo->filename       = str + parser->offsets.filename;
324   finfo->strings.group  = parser->offsets.group ?
325                           str + parser->offsets.group : NULL;
326   finfo->strings.perm   = parser->offsets.perm ?
327                           str + parser->offsets.perm : NULL;
328   finfo->strings.target = parser->offsets.symlink_target ?
329                           str + parser->offsets.symlink_target : NULL;
330   finfo->strings.time   = str + parser->offsets.time;
331   finfo->strings.user   = parser->offsets.user ?
332                           str + parser->offsets.user : NULL;
333 
334   /* get correct fnmatch callback */
335   compare = data->set.fnmatch;
336   if(!compare)
337     compare = Curl_fnmatch;
338 
339   /* filter pattern-corresponding filenames */
340   Curl_set_in_callback(data, true);
341   if(compare(data->set.fnmatch_data, wc->pattern,
342              finfo->filename) == 0) {
343     /* discard symlink which is containing multiple " -> " */
344     if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
345        (strstr(finfo->strings.target, " -> "))) {
346       add = FALSE;
347     }
348   }
349   else {
350     add = FALSE;
351   }
352   Curl_set_in_callback(data, false);
353 
354   if(add) {
355     Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
356   }
357   else {
358     Curl_fileinfo_cleanup(infop);
359   }
360 
361   ftpwc->parser->file_data = NULL;
362   return CURLE_OK;
363 }
364 
365 #define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
366 
Curl_ftp_parselist(char * buffer,size_t size,size_t nmemb,void * connptr)367 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
368                           void *connptr)
369 {
370   size_t bufflen = size*nmemb;
371   struct Curl_easy *data = (struct Curl_easy *)connptr;
372   struct ftp_wc *ftpwc = data->wildcard->ftpwc;
373   struct ftp_parselist_data *parser = ftpwc->parser;
374   size_t i = 0;
375   CURLcode result;
376   size_t retsize = bufflen;
377 
378   if(parser->error) { /* error in previous call */
379     /* scenario:
380      * 1. call => OK..
381      * 2. call => OUT_OF_MEMORY (or other error)
382      * 3. (last) call => is skipped RIGHT HERE and the error is handled later
383      *    in wc_statemach()
384      */
385     goto fail;
386   }
387 
388   if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
389     /* considering info about FILE response format */
390     parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
391   }
392 
393   while(i < bufflen) { /* FSM */
394     char *mem;
395     size_t len; /* number of bytes of data in the dynbuf */
396     char c = buffer[i];
397     struct fileinfo *infop;
398     struct curl_fileinfo *finfo;
399     if(!parser->file_data) { /* tmp file data is not allocated yet */
400       parser->file_data = Curl_fileinfo_alloc();
401       if(!parser->file_data) {
402         parser->error = CURLE_OUT_OF_MEMORY;
403         goto fail;
404       }
405       parser->item_offset = 0;
406       parser->item_length = 0;
407       Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
408     }
409 
410     infop = parser->file_data;
411     finfo = &infop->info;
412 
413     if(Curl_dyn_addn(&infop->buf, &c, 1)) {
414       parser->error = CURLE_OUT_OF_MEMORY;
415       goto fail;
416     }
417     len = Curl_dyn_len(&infop->buf);
418     mem = Curl_dyn_ptr(&infop->buf);
419 
420     switch(parser->os_type) {
421     case OS_TYPE_UNIX:
422       switch(parser->state.UNIX.main) {
423       case PL_UNIX_TOTALSIZE:
424         switch(parser->state.UNIX.sub.total_dirsize) {
425         case PL_UNIX_TOTALSIZE_INIT:
426           if(c == 't') {
427             parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
428             parser->item_length++;
429           }
430           else {
431             parser->state.UNIX.main = PL_UNIX_FILETYPE;
432             /* start FSM again not considering size of directory */
433             Curl_dyn_reset(&infop->buf);
434             continue;
435           }
436           break;
437         case PL_UNIX_TOTALSIZE_READING:
438           parser->item_length++;
439           if(c == '\r') {
440             parser->item_length--;
441             Curl_dyn_setlen(&infop->buf, --len);
442           }
443           else if(c == '\n') {
444             mem[parser->item_length - 1] = 0;
445             if(!strncmp("total ", mem, 6)) {
446               char *endptr = mem + 6;
447               /* here we can deal with directory size, pass the leading
448                  whitespace and then the digits */
449               while(ISBLANK(*endptr))
450                 endptr++;
451               while(ISDIGIT(*endptr))
452                 endptr++;
453               if(*endptr) {
454                 parser->error = CURLE_FTP_BAD_FILE_LIST;
455                 goto fail;
456               }
457               parser->state.UNIX.main = PL_UNIX_FILETYPE;
458               Curl_dyn_reset(&infop->buf);
459             }
460             else {
461               parser->error = CURLE_FTP_BAD_FILE_LIST;
462               goto fail;
463             }
464           }
465           break;
466         }
467         break;
468       case PL_UNIX_FILETYPE:
469         switch(c) {
470         case '-':
471           finfo->filetype = CURLFILETYPE_FILE;
472           break;
473         case 'd':
474           finfo->filetype = CURLFILETYPE_DIRECTORY;
475           break;
476         case 'l':
477           finfo->filetype = CURLFILETYPE_SYMLINK;
478           break;
479         case 'p':
480           finfo->filetype = CURLFILETYPE_NAMEDPIPE;
481           break;
482         case 's':
483           finfo->filetype = CURLFILETYPE_SOCKET;
484           break;
485         case 'c':
486           finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
487           break;
488         case 'b':
489           finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
490           break;
491         case 'D':
492           finfo->filetype = CURLFILETYPE_DOOR;
493           break;
494         default:
495           parser->error = CURLE_FTP_BAD_FILE_LIST;
496           goto fail;
497         }
498         parser->state.UNIX.main = PL_UNIX_PERMISSION;
499         parser->item_length = 0;
500         parser->item_offset = 1;
501         break;
502       case PL_UNIX_PERMISSION:
503         parser->item_length++;
504         if(parser->item_length <= 9) {
505           if(!strchr("rwx-tTsS", c)) {
506             parser->error = CURLE_FTP_BAD_FILE_LIST;
507             goto fail;
508           }
509         }
510         else if(parser->item_length == 10) {
511           unsigned int perm;
512           if(c != ' ') {
513             parser->error = CURLE_FTP_BAD_FILE_LIST;
514             goto fail;
515           }
516           mem[10] = 0; /* terminate permissions */
517           perm = ftp_pl_get_permission(mem + parser->item_offset);
518           if(perm & FTP_LP_MALFORMATED_PERM) {
519             parser->error = CURLE_FTP_BAD_FILE_LIST;
520             goto fail;
521           }
522           parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
523           parser->file_data->info.perm = perm;
524           parser->offsets.perm = parser->item_offset;
525 
526           parser->item_length = 0;
527           parser->state.UNIX.main = PL_UNIX_HLINKS;
528           parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
529         }
530         break;
531       case PL_UNIX_HLINKS:
532         switch(parser->state.UNIX.sub.hlinks) {
533         case PL_UNIX_HLINKS_PRESPACE:
534           if(c != ' ') {
535             if(ISDIGIT(c)) {
536               parser->item_offset = len - 1;
537               parser->item_length = 1;
538               parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
539             }
540             else {
541               parser->error = CURLE_FTP_BAD_FILE_LIST;
542               goto fail;
543             }
544           }
545           break;
546         case PL_UNIX_HLINKS_NUMBER:
547           parser->item_length ++;
548           if(c == ' ') {
549             char *p;
550             long int hlinks;
551             mem[parser->item_offset + parser->item_length - 1] = 0;
552             hlinks = strtol(mem + parser->item_offset, &p, 10);
553             if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
554               parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
555               parser->file_data->info.hardlinks = hlinks;
556             }
557             parser->item_length = 0;
558             parser->item_offset = 0;
559             parser->state.UNIX.main = PL_UNIX_USER;
560             parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
561           }
562           else if(!ISDIGIT(c)) {
563             parser->error = CURLE_FTP_BAD_FILE_LIST;
564             goto fail;
565           }
566           break;
567         }
568         break;
569       case PL_UNIX_USER:
570         switch(parser->state.UNIX.sub.user) {
571         case PL_UNIX_USER_PRESPACE:
572           if(c != ' ') {
573             parser->item_offset = len - 1;
574             parser->item_length = 1;
575             parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
576           }
577           break;
578         case PL_UNIX_USER_PARSING:
579           parser->item_length++;
580           if(c == ' ') {
581             mem[parser->item_offset + parser->item_length - 1] = 0;
582             parser->offsets.user = parser->item_offset;
583             parser->state.UNIX.main = PL_UNIX_GROUP;
584             parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
585             parser->item_offset = 0;
586             parser->item_length = 0;
587           }
588           break;
589         }
590         break;
591       case PL_UNIX_GROUP:
592         switch(parser->state.UNIX.sub.group) {
593         case PL_UNIX_GROUP_PRESPACE:
594           if(c != ' ') {
595             parser->item_offset = len - 1;
596             parser->item_length = 1;
597             parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
598           }
599           break;
600         case PL_UNIX_GROUP_NAME:
601           parser->item_length++;
602           if(c == ' ') {
603             mem[parser->item_offset + parser->item_length - 1] = 0;
604             parser->offsets.group = parser->item_offset;
605             parser->state.UNIX.main = PL_UNIX_SIZE;
606             parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
607             parser->item_offset = 0;
608             parser->item_length = 0;
609           }
610           break;
611         }
612         break;
613       case PL_UNIX_SIZE:
614         switch(parser->state.UNIX.sub.size) {
615         case PL_UNIX_SIZE_PRESPACE:
616           if(c != ' ') {
617             if(ISDIGIT(c)) {
618               parser->item_offset = len - 1;
619               parser->item_length = 1;
620               parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
621             }
622             else {
623               parser->error = CURLE_FTP_BAD_FILE_LIST;
624               goto fail;
625             }
626           }
627           break;
628         case PL_UNIX_SIZE_NUMBER:
629           parser->item_length++;
630           if(c == ' ') {
631             char *p;
632             curl_off_t fsize;
633             mem[parser->item_offset + parser->item_length - 1] = 0;
634             if(!curlx_strtoofft(mem + parser->item_offset,
635                                 &p, 10, &fsize)) {
636               if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
637                  fsize != CURL_OFF_T_MIN) {
638                 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
639                 parser->file_data->info.size = fsize;
640               }
641               parser->item_length = 0;
642               parser->item_offset = 0;
643               parser->state.UNIX.main = PL_UNIX_TIME;
644               parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
645             }
646           }
647           else if(!ISDIGIT(c)) {
648             parser->error = CURLE_FTP_BAD_FILE_LIST;
649             goto fail;
650           }
651           break;
652         }
653         break;
654       case PL_UNIX_TIME:
655         switch(parser->state.UNIX.sub.time) {
656         case PL_UNIX_TIME_PREPART1:
657           if(c != ' ') {
658             if(ISALNUM(c)) {
659               parser->item_offset = len -1;
660               parser->item_length = 1;
661               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
662             }
663             else {
664               parser->error = CURLE_FTP_BAD_FILE_LIST;
665               goto fail;
666             }
667           }
668           break;
669         case PL_UNIX_TIME_PART1:
670           parser->item_length++;
671           if(c == ' ') {
672             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
673           }
674           else if(!ISALNUM(c) && c != '.') {
675             parser->error = CURLE_FTP_BAD_FILE_LIST;
676             goto fail;
677           }
678           break;
679         case PL_UNIX_TIME_PREPART2:
680           parser->item_length++;
681           if(c != ' ') {
682             if(ISALNUM(c)) {
683               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
684             }
685             else {
686               parser->error = CURLE_FTP_BAD_FILE_LIST;
687               goto fail;
688             }
689           }
690           break;
691         case PL_UNIX_TIME_PART2:
692           parser->item_length++;
693           if(c == ' ') {
694             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
695           }
696           else if(!ISALNUM(c) && c != '.') {
697             parser->error = CURLE_FTP_BAD_FILE_LIST;
698             goto fail;
699           }
700           break;
701         case PL_UNIX_TIME_PREPART3:
702           parser->item_length++;
703           if(c != ' ') {
704             if(ISALNUM(c)) {
705               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
706             }
707             else {
708               parser->error = CURLE_FTP_BAD_FILE_LIST;
709               goto fail;
710             }
711           }
712           break;
713         case PL_UNIX_TIME_PART3:
714           parser->item_length++;
715           if(c == ' ') {
716             mem[parser->item_offset + parser->item_length -1] = 0;
717             parser->offsets.time = parser->item_offset;
718             /*
719               if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) {
720                 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
721               }
722             */
723             if(finfo->filetype == CURLFILETYPE_SYMLINK) {
724               parser->state.UNIX.main = PL_UNIX_SYMLINK;
725               parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
726             }
727             else {
728               parser->state.UNIX.main = PL_UNIX_FILENAME;
729               parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
730             }
731           }
732           else if(!ISALNUM(c) && c != '.' && c != ':') {
733             parser->error = CURLE_FTP_BAD_FILE_LIST;
734             goto fail;
735           }
736           break;
737         }
738         break;
739       case PL_UNIX_FILENAME:
740         switch(parser->state.UNIX.sub.filename) {
741         case PL_UNIX_FILENAME_PRESPACE:
742           if(c != ' ') {
743             parser->item_offset = len - 1;
744             parser->item_length = 1;
745             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
746           }
747           break;
748         case PL_UNIX_FILENAME_NAME:
749           parser->item_length++;
750           if(c == '\r') {
751             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
752           }
753           else if(c == '\n') {
754             mem[parser->item_offset + parser->item_length - 1] = 0;
755             parser->offsets.filename = parser->item_offset;
756             parser->state.UNIX.main = PL_UNIX_FILETYPE;
757             result = ftp_pl_insert_finfo(data, infop);
758             if(result) {
759               parser->error = result;
760               goto fail;
761             }
762           }
763           break;
764         case PL_UNIX_FILENAME_WINDOWSEOL:
765           if(c == '\n') {
766             mem[parser->item_offset + parser->item_length - 1] = 0;
767             parser->offsets.filename = parser->item_offset;
768             parser->state.UNIX.main = PL_UNIX_FILETYPE;
769             result = ftp_pl_insert_finfo(data, infop);
770             if(result) {
771               parser->error = result;
772               goto fail;
773             }
774           }
775           else {
776             parser->error = CURLE_FTP_BAD_FILE_LIST;
777             goto fail;
778           }
779           break;
780         }
781         break;
782       case PL_UNIX_SYMLINK:
783         switch(parser->state.UNIX.sub.symlink) {
784         case PL_UNIX_SYMLINK_PRESPACE:
785           if(c != ' ') {
786             parser->item_offset = len - 1;
787             parser->item_length = 1;
788             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
789           }
790           break;
791         case PL_UNIX_SYMLINK_NAME:
792           parser->item_length++;
793           if(c == ' ') {
794             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
795           }
796           else if(c == '\r' || c == '\n') {
797             parser->error = CURLE_FTP_BAD_FILE_LIST;
798             goto fail;
799           }
800           break;
801         case PL_UNIX_SYMLINK_PRETARGET1:
802           parser->item_length++;
803           if(c == '-') {
804             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
805           }
806           else if(c == '\r' || c == '\n') {
807             parser->error = CURLE_FTP_BAD_FILE_LIST;
808             goto fail;
809           }
810           else {
811             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
812           }
813           break;
814         case PL_UNIX_SYMLINK_PRETARGET2:
815           parser->item_length++;
816           if(c == '>') {
817             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
818           }
819           else if(c == '\r' || c == '\n') {
820             parser->error = CURLE_FTP_BAD_FILE_LIST;
821             goto fail;
822           }
823           else {
824             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
825           }
826           break;
827         case PL_UNIX_SYMLINK_PRETARGET3:
828           parser->item_length++;
829           if(c == ' ') {
830             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
831             /* now place where is symlink following */
832             mem[parser->item_offset + parser->item_length - 4] = 0;
833             parser->offsets.filename = parser->item_offset;
834             parser->item_length = 0;
835             parser->item_offset = 0;
836           }
837           else if(c == '\r' || c == '\n') {
838             parser->error = CURLE_FTP_BAD_FILE_LIST;
839             goto fail;
840           }
841           else {
842             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
843           }
844           break;
845         case PL_UNIX_SYMLINK_PRETARGET4:
846           if(c != '\r' && c != '\n') {
847             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
848             parser->item_offset = len - 1;
849             parser->item_length = 1;
850           }
851           else {
852             parser->error = CURLE_FTP_BAD_FILE_LIST;
853             goto fail;
854           }
855           break;
856         case PL_UNIX_SYMLINK_TARGET:
857           parser->item_length++;
858           if(c == '\r') {
859             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
860           }
861           else if(c == '\n') {
862             mem[parser->item_offset + parser->item_length - 1] = 0;
863             parser->offsets.symlink_target = parser->item_offset;
864             result = ftp_pl_insert_finfo(data, infop);
865             if(result) {
866               parser->error = result;
867               goto fail;
868             }
869             parser->state.UNIX.main = PL_UNIX_FILETYPE;
870           }
871           break;
872         case PL_UNIX_SYMLINK_WINDOWSEOL:
873           if(c == '\n') {
874             mem[parser->item_offset + parser->item_length - 1] = 0;
875             parser->offsets.symlink_target = parser->item_offset;
876             result = ftp_pl_insert_finfo(data, infop);
877             if(result) {
878               parser->error = result;
879               goto fail;
880             }
881             parser->state.UNIX.main = PL_UNIX_FILETYPE;
882           }
883           else {
884             parser->error = CURLE_FTP_BAD_FILE_LIST;
885             goto fail;
886           }
887           break;
888         }
889         break;
890       }
891       break;
892     case OS_TYPE_WIN_NT:
893       switch(parser->state.NT.main) {
894       case PL_WINNT_DATE:
895         parser->item_length++;
896         if(parser->item_length < 9) {
897           if(!strchr("0123456789-", c)) { /* only simple control */
898             parser->error = CURLE_FTP_BAD_FILE_LIST;
899             goto fail;
900           }
901         }
902         else if(parser->item_length == 9) {
903           if(c == ' ') {
904             parser->state.NT.main = PL_WINNT_TIME;
905             parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
906           }
907           else {
908             parser->error = CURLE_FTP_BAD_FILE_LIST;
909             goto fail;
910           }
911         }
912         else {
913           parser->error = CURLE_FTP_BAD_FILE_LIST;
914           goto fail;
915         }
916         break;
917       case PL_WINNT_TIME:
918         parser->item_length++;
919         switch(parser->state.NT.sub.time) {
920         case PL_WINNT_TIME_PRESPACE:
921           if(!ISBLANK(c)) {
922             parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
923           }
924           break;
925         case PL_WINNT_TIME_TIME:
926           if(c == ' ') {
927             parser->offsets.time = parser->item_offset;
928             mem[parser->item_offset + parser->item_length -1] = 0;
929             parser->state.NT.main = PL_WINNT_DIRORSIZE;
930             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
931             parser->item_length = 0;
932           }
933           else if(!strchr("APM0123456789:", c)) {
934             parser->error = CURLE_FTP_BAD_FILE_LIST;
935             goto fail;
936           }
937           break;
938         }
939         break;
940       case PL_WINNT_DIRORSIZE:
941         switch(parser->state.NT.sub.dirorsize) {
942         case PL_WINNT_DIRORSIZE_PRESPACE:
943           if(c != ' ') {
944             parser->item_offset = len - 1;
945             parser->item_length = 1;
946             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
947           }
948           break;
949         case PL_WINNT_DIRORSIZE_CONTENT:
950           parser->item_length ++;
951           if(c == ' ') {
952             mem[parser->item_offset + parser->item_length - 1] = 0;
953             if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
954               finfo->filetype = CURLFILETYPE_DIRECTORY;
955               finfo->size = 0;
956             }
957             else {
958               char *endptr;
959               if(curlx_strtoofft(mem +
960                                  parser->item_offset,
961                                  &endptr, 10, &finfo->size)) {
962                 parser->error = CURLE_FTP_BAD_FILE_LIST;
963                 goto fail;
964               }
965               /* correct file type */
966               parser->file_data->info.filetype = CURLFILETYPE_FILE;
967             }
968 
969             parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
970             parser->item_length = 0;
971             parser->state.NT.main = PL_WINNT_FILENAME;
972             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
973           }
974           break;
975         }
976         break;
977       case PL_WINNT_FILENAME:
978         switch(parser->state.NT.sub.filename) {
979         case PL_WINNT_FILENAME_PRESPACE:
980           if(c != ' ') {
981             parser->item_offset = len -1;
982             parser->item_length = 1;
983             parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
984           }
985           break;
986         case PL_WINNT_FILENAME_CONTENT:
987           parser->item_length++;
988           if(c == '\r') {
989             parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
990             mem[len - 1] = 0;
991           }
992           else if(c == '\n') {
993             parser->offsets.filename = parser->item_offset;
994             mem[len - 1] = 0;
995             result = ftp_pl_insert_finfo(data, infop);
996             if(result) {
997               parser->error = result;
998               goto fail;
999             }
1000             parser->state.NT.main = PL_WINNT_DATE;
1001             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1002           }
1003           break;
1004         case PL_WINNT_FILENAME_WINEOL:
1005           if(c == '\n') {
1006             parser->offsets.filename = parser->item_offset;
1007             result = ftp_pl_insert_finfo(data, infop);
1008             if(result) {
1009               parser->error = result;
1010               goto fail;
1011             }
1012             parser->state.NT.main = PL_WINNT_DATE;
1013             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1014           }
1015           else {
1016             parser->error = CURLE_FTP_BAD_FILE_LIST;
1017             goto fail;
1018           }
1019           break;
1020         }
1021         break;
1022       }
1023       break;
1024     default:
1025       retsize = bufflen + 1;
1026       goto fail;
1027     }
1028 
1029     i++;
1030   }
1031   return retsize;
1032 
1033 fail:
1034 
1035   /* Clean up any allocated memory. */
1036   if(parser->file_data) {
1037     Curl_fileinfo_cleanup(parser->file_data);
1038     parser->file_data = NULL;
1039   }
1040 
1041   return retsize;
1042 }
1043 
1044 #endif /* CURL_DISABLE_FTP */
1045