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