Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) 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 
    181 struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
    182 {
    183   return calloc(1, sizeof(struct ftp_parselist_data));
    184 }
    185 
    186 
    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 
    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 
    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 
    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 
    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