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