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