• 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   /* move finfo pointers to b_data */
322   char *str = finfo->b_data;
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 
Curl_ftp_parselist(char * buffer,size_t size,size_t nmemb,void * connptr)365 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
366                           void *connptr)
367 {
368   size_t bufflen = size*nmemb;
369   struct Curl_easy *data = (struct Curl_easy *)connptr;
370   struct ftp_wc *ftpwc = data->wildcard->ftpwc;
371   struct ftp_parselist_data *parser = ftpwc->parser;
372   struct fileinfo *infop;
373   struct curl_fileinfo *finfo;
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 hadled 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 = (buffer[0] >= '0' && buffer[0] <= '9') ?
391                        OS_TYPE_WIN_NT : OS_TYPE_UNIX;
392   }
393 
394   while(i < bufflen) { /* FSM */
395 
396     char c = buffer[i];
397     if(!parser->file_data) { /* tmp file data is not allocated yet */
398       parser->file_data = Curl_fileinfo_alloc();
399       if(!parser->file_data) {
400         parser->error = CURLE_OUT_OF_MEMORY;
401         goto fail;
402       }
403       parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE);
404       if(!parser->file_data->info.b_data) {
405         parser->error = CURLE_OUT_OF_MEMORY;
406         goto fail;
407       }
408       parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE;
409       parser->item_offset = 0;
410       parser->item_length = 0;
411     }
412 
413     infop = parser->file_data;
414     finfo = &infop->info;
415     finfo->b_data[finfo->b_used++] = c;
416 
417     if(finfo->b_used >= finfo->b_size - 1) {
418       /* if it is important, extend buffer space for file data */
419       char *tmp = realloc(finfo->b_data,
420                           finfo->b_size + FTP_BUFFER_ALLOCSIZE);
421       if(tmp) {
422         finfo->b_size += FTP_BUFFER_ALLOCSIZE;
423         finfo->b_data = tmp;
424       }
425       else {
426         Curl_fileinfo_cleanup(parser->file_data);
427         parser->file_data = NULL;
428         parser->error = CURLE_OUT_OF_MEMORY;
429         goto fail;
430       }
431     }
432 
433     switch(parser->os_type) {
434     case OS_TYPE_UNIX:
435       switch(parser->state.UNIX.main) {
436       case PL_UNIX_TOTALSIZE:
437         switch(parser->state.UNIX.sub.total_dirsize) {
438         case PL_UNIX_TOTALSIZE_INIT:
439           if(c == 't') {
440             parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
441             parser->item_length++;
442           }
443           else {
444             parser->state.UNIX.main = PL_UNIX_FILETYPE;
445             /* start FSM again not considering size of directory */
446             finfo->b_used = 0;
447             continue;
448           }
449           break;
450         case PL_UNIX_TOTALSIZE_READING:
451           parser->item_length++;
452           if(c == '\r') {
453             parser->item_length--;
454             finfo->b_used--;
455           }
456           else if(c == '\n') {
457             finfo->b_data[parser->item_length - 1] = 0;
458             if(strncmp("total ", finfo->b_data, 6) == 0) {
459               char *endptr = finfo->b_data + 6;
460               /* here we can deal with directory size, pass the leading
461                  whitespace and then the digits */
462               while(ISBLANK(*endptr))
463                 endptr++;
464               while(ISDIGIT(*endptr))
465                 endptr++;
466               if(*endptr) {
467                 parser->error = CURLE_FTP_BAD_FILE_LIST;
468                 goto fail;
469               }
470               parser->state.UNIX.main = PL_UNIX_FILETYPE;
471               finfo->b_used = 0;
472             }
473             else {
474               parser->error = CURLE_FTP_BAD_FILE_LIST;
475               goto fail;
476             }
477           }
478           break;
479         }
480         break;
481       case PL_UNIX_FILETYPE:
482         switch(c) {
483         case '-':
484           finfo->filetype = CURLFILETYPE_FILE;
485           break;
486         case 'd':
487           finfo->filetype = CURLFILETYPE_DIRECTORY;
488           break;
489         case 'l':
490           finfo->filetype = CURLFILETYPE_SYMLINK;
491           break;
492         case 'p':
493           finfo->filetype = CURLFILETYPE_NAMEDPIPE;
494           break;
495         case 's':
496           finfo->filetype = CURLFILETYPE_SOCKET;
497           break;
498         case 'c':
499           finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
500           break;
501         case 'b':
502           finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
503           break;
504         case 'D':
505           finfo->filetype = CURLFILETYPE_DOOR;
506           break;
507         default:
508           parser->error = CURLE_FTP_BAD_FILE_LIST;
509           goto fail;
510         }
511         parser->state.UNIX.main = PL_UNIX_PERMISSION;
512         parser->item_length = 0;
513         parser->item_offset = 1;
514         break;
515       case PL_UNIX_PERMISSION:
516         parser->item_length++;
517         if(parser->item_length <= 9) {
518           if(!strchr("rwx-tTsS", c)) {
519             parser->error = CURLE_FTP_BAD_FILE_LIST;
520             goto fail;
521           }
522         }
523         else if(parser->item_length == 10) {
524           unsigned int perm;
525           if(c != ' ') {
526             parser->error = CURLE_FTP_BAD_FILE_LIST;
527             goto fail;
528           }
529           finfo->b_data[10] = 0; /* terminate permissions */
530           perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
531           if(perm & FTP_LP_MALFORMATED_PERM) {
532             parser->error = CURLE_FTP_BAD_FILE_LIST;
533             goto fail;
534           }
535           parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
536           parser->file_data->info.perm = perm;
537           parser->offsets.perm = parser->item_offset;
538 
539           parser->item_length = 0;
540           parser->state.UNIX.main = PL_UNIX_HLINKS;
541           parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
542         }
543         break;
544       case PL_UNIX_HLINKS:
545         switch(parser->state.UNIX.sub.hlinks) {
546         case PL_UNIX_HLINKS_PRESPACE:
547           if(c != ' ') {
548             if(c >= '0' && c <= '9') {
549               parser->item_offset = finfo->b_used - 1;
550               parser->item_length = 1;
551               parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
552             }
553             else {
554               parser->error = CURLE_FTP_BAD_FILE_LIST;
555               goto fail;
556             }
557           }
558           break;
559         case PL_UNIX_HLINKS_NUMBER:
560           parser->item_length ++;
561           if(c == ' ') {
562             char *p;
563             long int hlinks;
564             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
565             hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
566             if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
567               parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
568               parser->file_data->info.hardlinks = hlinks;
569             }
570             parser->item_length = 0;
571             parser->item_offset = 0;
572             parser->state.UNIX.main = PL_UNIX_USER;
573             parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
574           }
575           else if(c < '0' || c > '9') {
576             parser->error = CURLE_FTP_BAD_FILE_LIST;
577             goto fail;
578           }
579           break;
580         }
581         break;
582       case PL_UNIX_USER:
583         switch(parser->state.UNIX.sub.user) {
584         case PL_UNIX_USER_PRESPACE:
585           if(c != ' ') {
586             parser->item_offset = finfo->b_used - 1;
587             parser->item_length = 1;
588             parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
589           }
590           break;
591         case PL_UNIX_USER_PARSING:
592           parser->item_length++;
593           if(c == ' ') {
594             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
595             parser->offsets.user = parser->item_offset;
596             parser->state.UNIX.main = PL_UNIX_GROUP;
597             parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
598             parser->item_offset = 0;
599             parser->item_length = 0;
600           }
601           break;
602         }
603         break;
604       case PL_UNIX_GROUP:
605         switch(parser->state.UNIX.sub.group) {
606         case PL_UNIX_GROUP_PRESPACE:
607           if(c != ' ') {
608             parser->item_offset = finfo->b_used - 1;
609             parser->item_length = 1;
610             parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
611           }
612           break;
613         case PL_UNIX_GROUP_NAME:
614           parser->item_length++;
615           if(c == ' ') {
616             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
617             parser->offsets.group = parser->item_offset;
618             parser->state.UNIX.main = PL_UNIX_SIZE;
619             parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
620             parser->item_offset = 0;
621             parser->item_length = 0;
622           }
623           break;
624         }
625         break;
626       case PL_UNIX_SIZE:
627         switch(parser->state.UNIX.sub.size) {
628         case PL_UNIX_SIZE_PRESPACE:
629           if(c != ' ') {
630             if(c >= '0' && c <= '9') {
631               parser->item_offset = finfo->b_used - 1;
632               parser->item_length = 1;
633               parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
634             }
635             else {
636               parser->error = CURLE_FTP_BAD_FILE_LIST;
637               goto fail;
638             }
639           }
640           break;
641         case PL_UNIX_SIZE_NUMBER:
642           parser->item_length++;
643           if(c == ' ') {
644             char *p;
645             curl_off_t fsize;
646             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
647             if(!curlx_strtoofft(finfo->b_data + parser->item_offset,
648                                 &p, 10, &fsize)) {
649               if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
650                  fsize != CURL_OFF_T_MIN) {
651                 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
652                 parser->file_data->info.size = fsize;
653               }
654               parser->item_length = 0;
655               parser->item_offset = 0;
656               parser->state.UNIX.main = PL_UNIX_TIME;
657               parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
658             }
659           }
660           else if(!ISDIGIT(c)) {
661             parser->error = CURLE_FTP_BAD_FILE_LIST;
662             goto fail;
663           }
664           break;
665         }
666         break;
667       case PL_UNIX_TIME:
668         switch(parser->state.UNIX.sub.time) {
669         case PL_UNIX_TIME_PREPART1:
670           if(c != ' ') {
671             if(ISALNUM(c)) {
672               parser->item_offset = finfo->b_used -1;
673               parser->item_length = 1;
674               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
675             }
676             else {
677               parser->error = CURLE_FTP_BAD_FILE_LIST;
678               goto fail;
679             }
680           }
681           break;
682         case PL_UNIX_TIME_PART1:
683           parser->item_length++;
684           if(c == ' ') {
685             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
686           }
687           else if(!ISALNUM(c) && c != '.') {
688             parser->error = CURLE_FTP_BAD_FILE_LIST;
689             goto fail;
690           }
691           break;
692         case PL_UNIX_TIME_PREPART2:
693           parser->item_length++;
694           if(c != ' ') {
695             if(ISALNUM(c)) {
696               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
697             }
698             else {
699               parser->error = CURLE_FTP_BAD_FILE_LIST;
700               goto fail;
701             }
702           }
703           break;
704         case PL_UNIX_TIME_PART2:
705           parser->item_length++;
706           if(c == ' ') {
707             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
708           }
709           else if(!ISALNUM(c) && c != '.') {
710             parser->error = CURLE_FTP_BAD_FILE_LIST;
711             goto fail;
712           }
713           break;
714         case PL_UNIX_TIME_PREPART3:
715           parser->item_length++;
716           if(c != ' ') {
717             if(ISALNUM(c)) {
718               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
719             }
720             else {
721               parser->error = CURLE_FTP_BAD_FILE_LIST;
722               goto fail;
723             }
724           }
725           break;
726         case PL_UNIX_TIME_PART3:
727           parser->item_length++;
728           if(c == ' ') {
729             finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
730             parser->offsets.time = parser->item_offset;
731             /*
732               if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
733                 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
734               }
735             */
736             if(finfo->filetype == CURLFILETYPE_SYMLINK) {
737               parser->state.UNIX.main = PL_UNIX_SYMLINK;
738               parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
739             }
740             else {
741               parser->state.UNIX.main = PL_UNIX_FILENAME;
742               parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
743             }
744           }
745           else if(!ISALNUM(c) && c != '.' && c != ':') {
746             parser->error = CURLE_FTP_BAD_FILE_LIST;
747             goto fail;
748           }
749           break;
750         }
751         break;
752       case PL_UNIX_FILENAME:
753         switch(parser->state.UNIX.sub.filename) {
754         case PL_UNIX_FILENAME_PRESPACE:
755           if(c != ' ') {
756             parser->item_offset = finfo->b_used - 1;
757             parser->item_length = 1;
758             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
759           }
760           break;
761         case PL_UNIX_FILENAME_NAME:
762           parser->item_length++;
763           if(c == '\r') {
764             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
765           }
766           else if(c == '\n') {
767             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
768             parser->offsets.filename = parser->item_offset;
769             parser->state.UNIX.main = PL_UNIX_FILETYPE;
770             result = ftp_pl_insert_finfo(data, infop);
771             if(result) {
772               parser->error = result;
773               goto fail;
774             }
775           }
776           break;
777         case PL_UNIX_FILENAME_WINDOWSEOL:
778           if(c == '\n') {
779             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
780             parser->offsets.filename = parser->item_offset;
781             parser->state.UNIX.main = PL_UNIX_FILETYPE;
782             result = ftp_pl_insert_finfo(data, infop);
783             if(result) {
784               parser->error = result;
785               goto fail;
786             }
787           }
788           else {
789             parser->error = CURLE_FTP_BAD_FILE_LIST;
790             goto fail;
791           }
792           break;
793         }
794         break;
795       case PL_UNIX_SYMLINK:
796         switch(parser->state.UNIX.sub.symlink) {
797         case PL_UNIX_SYMLINK_PRESPACE:
798           if(c != ' ') {
799             parser->item_offset = finfo->b_used - 1;
800             parser->item_length = 1;
801             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
802           }
803           break;
804         case PL_UNIX_SYMLINK_NAME:
805           parser->item_length++;
806           if(c == ' ') {
807             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
808           }
809           else if(c == '\r' || c == '\n') {
810             parser->error = CURLE_FTP_BAD_FILE_LIST;
811             goto fail;
812           }
813           break;
814         case PL_UNIX_SYMLINK_PRETARGET1:
815           parser->item_length++;
816           if(c == '-') {
817             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
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_PRETARGET2:
828           parser->item_length++;
829           if(c == '>') {
830             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
831           }
832           else if(c == '\r' || c == '\n') {
833             parser->error = CURLE_FTP_BAD_FILE_LIST;
834             goto fail;
835           }
836           else {
837             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
838           }
839           break;
840         case PL_UNIX_SYMLINK_PRETARGET3:
841           parser->item_length++;
842           if(c == ' ') {
843             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
844             /* now place where is symlink following */
845             finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
846             parser->offsets.filename = parser->item_offset;
847             parser->item_length = 0;
848             parser->item_offset = 0;
849           }
850           else if(c == '\r' || c == '\n') {
851             parser->error = CURLE_FTP_BAD_FILE_LIST;
852             goto fail;
853           }
854           else {
855             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
856           }
857           break;
858         case PL_UNIX_SYMLINK_PRETARGET4:
859           if(c != '\r' && c != '\n') {
860             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
861             parser->item_offset = finfo->b_used - 1;
862             parser->item_length = 1;
863           }
864           else {
865             parser->error = CURLE_FTP_BAD_FILE_LIST;
866             goto fail;
867           }
868           break;
869         case PL_UNIX_SYMLINK_TARGET:
870           parser->item_length++;
871           if(c == '\r') {
872             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
873           }
874           else if(c == '\n') {
875             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
876             parser->offsets.symlink_target = parser->item_offset;
877             result = ftp_pl_insert_finfo(data, infop);
878             if(result) {
879               parser->error = result;
880               goto fail;
881             }
882             parser->state.UNIX.main = PL_UNIX_FILETYPE;
883           }
884           break;
885         case PL_UNIX_SYMLINK_WINDOWSEOL:
886           if(c == '\n') {
887             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
888             parser->offsets.symlink_target = parser->item_offset;
889             result = ftp_pl_insert_finfo(data, infop);
890             if(result) {
891               parser->error = result;
892               goto fail;
893             }
894             parser->state.UNIX.main = PL_UNIX_FILETYPE;
895           }
896           else {
897             parser->error = CURLE_FTP_BAD_FILE_LIST;
898             goto fail;
899           }
900           break;
901         }
902         break;
903       }
904       break;
905     case OS_TYPE_WIN_NT:
906       switch(parser->state.NT.main) {
907       case PL_WINNT_DATE:
908         parser->item_length++;
909         if(parser->item_length < 9) {
910           if(!strchr("0123456789-", c)) { /* only simple control */
911             parser->error = CURLE_FTP_BAD_FILE_LIST;
912             goto fail;
913           }
914         }
915         else if(parser->item_length == 9) {
916           if(c == ' ') {
917             parser->state.NT.main = PL_WINNT_TIME;
918             parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
919           }
920           else {
921             parser->error = CURLE_FTP_BAD_FILE_LIST;
922             goto fail;
923           }
924         }
925         else {
926           parser->error = CURLE_FTP_BAD_FILE_LIST;
927           goto fail;
928         }
929         break;
930       case PL_WINNT_TIME:
931         parser->item_length++;
932         switch(parser->state.NT.sub.time) {
933         case PL_WINNT_TIME_PRESPACE:
934           if(!ISBLANK(c)) {
935             parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
936           }
937           break;
938         case PL_WINNT_TIME_TIME:
939           if(c == ' ') {
940             parser->offsets.time = parser->item_offset;
941             finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
942             parser->state.NT.main = PL_WINNT_DIRORSIZE;
943             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
944             parser->item_length = 0;
945           }
946           else if(!strchr("APM0123456789:", c)) {
947             parser->error = CURLE_FTP_BAD_FILE_LIST;
948             goto fail;
949           }
950           break;
951         }
952         break;
953       case PL_WINNT_DIRORSIZE:
954         switch(parser->state.NT.sub.dirorsize) {
955         case PL_WINNT_DIRORSIZE_PRESPACE:
956           if(c != ' ') {
957             parser->item_offset = finfo->b_used - 1;
958             parser->item_length = 1;
959             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
960           }
961           break;
962         case PL_WINNT_DIRORSIZE_CONTENT:
963           parser->item_length ++;
964           if(c == ' ') {
965             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
966             if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
967               finfo->filetype = CURLFILETYPE_DIRECTORY;
968               finfo->size = 0;
969             }
970             else {
971               char *endptr;
972               if(curlx_strtoofft(finfo->b_data +
973                                  parser->item_offset,
974                                  &endptr, 10, &finfo->size)) {
975                 parser->error = CURLE_FTP_BAD_FILE_LIST;
976                 goto fail;
977               }
978               /* correct file type */
979               parser->file_data->info.filetype = CURLFILETYPE_FILE;
980             }
981 
982             parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
983             parser->item_length = 0;
984             parser->state.NT.main = PL_WINNT_FILENAME;
985             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
986           }
987           break;
988         }
989         break;
990       case PL_WINNT_FILENAME:
991         switch(parser->state.NT.sub.filename) {
992         case PL_WINNT_FILENAME_PRESPACE:
993           if(c != ' ') {
994             parser->item_offset = finfo->b_used -1;
995             parser->item_length = 1;
996             parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
997           }
998           break;
999         case PL_WINNT_FILENAME_CONTENT:
1000           parser->item_length++;
1001           if(c == '\r') {
1002             parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
1003             finfo->b_data[finfo->b_used - 1] = 0;
1004           }
1005           else if(c == '\n') {
1006             parser->offsets.filename = parser->item_offset;
1007             finfo->b_data[finfo->b_used - 1] = 0;
1008             result = ftp_pl_insert_finfo(data, infop);
1009             if(result) {
1010               parser->error = result;
1011               goto fail;
1012             }
1013             parser->state.NT.main = PL_WINNT_DATE;
1014             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1015           }
1016           break;
1017         case PL_WINNT_FILENAME_WINEOL:
1018           if(c == '\n') {
1019             parser->offsets.filename = parser->item_offset;
1020             result = ftp_pl_insert_finfo(data, infop);
1021             if(result) {
1022               parser->error = result;
1023               goto fail;
1024             }
1025             parser->state.NT.main = PL_WINNT_DATE;
1026             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1027           }
1028           else {
1029             parser->error = CURLE_FTP_BAD_FILE_LIST;
1030             goto fail;
1031           }
1032           break;
1033         }
1034         break;
1035       }
1036       break;
1037     default:
1038       retsize = bufflen + 1;
1039       goto fail;
1040     }
1041 
1042     i++;
1043   }
1044   return retsize;
1045 
1046 fail:
1047 
1048   /* Clean up any allocated memory. */
1049   if(parser->file_data) {
1050     Curl_fileinfo_cleanup(parser->file_data);
1051     parser->file_data = NULL;
1052   }
1053 
1054   return retsize;
1055 }
1056 
1057 #endif /* CURL_DISABLE_FTP */
1058