quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ftplistparser.c (30078B)


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