Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, 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 curl_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 void PL_ERROR(struct connectdata *conn, CURLcode err)
    268 {
    269   struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
    270   struct ftp_parselist_data *parser = tmpdata->parser;
    271   if(parser->file_data)
    272     Curl_fileinfo_dtor(NULL, parser->file_data);
    273   parser->file_data = NULL;
    274   parser->error = err;
    275 }
    276 
    277 static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
    278                                     struct curl_fileinfo *finfo)
    279 {
    280   curl_fnmatch_callback compare;
    281   struct WildcardData *wc = &conn->data->wildcard;
    282   struct ftp_wc_tmpdata *tmpdata = wc->tmp;
    283   struct curl_llist *llist = wc->filelist;
    284   struct ftp_parselist_data *parser = tmpdata->parser;
    285   bool add = TRUE;
    286 
    287   /* move finfo pointers to b_data */
    288   char *str = finfo->b_data;
    289   finfo->filename       = str + parser->offsets.filename;
    290   finfo->strings.group  = parser->offsets.group ?
    291                           str + parser->offsets.group : NULL;
    292   finfo->strings.perm   = parser->offsets.perm ?
    293                           str + parser->offsets.perm : NULL;
    294   finfo->strings.target = parser->offsets.symlink_target ?
    295                           str + parser->offsets.symlink_target : NULL;
    296   finfo->strings.time   = str + parser->offsets.time;
    297   finfo->strings.user   = parser->offsets.user ?
    298                           str + parser->offsets.user : NULL;
    299 
    300   /* get correct fnmatch callback */
    301   compare = conn->data->set.fnmatch;
    302   if(!compare)
    303     compare = Curl_fnmatch;
    304 
    305   /* filter pattern-corresponding filenames */
    306   if(compare(conn->data->set.fnmatch_data, wc->pattern,
    307              finfo->filename) == 0) {
    308     /* discard symlink which is containing multiple " -> " */
    309     if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
    310        (strstr(finfo->strings.target, " -> "))) {
    311       add = FALSE;
    312     }
    313   }
    314   else {
    315     add = FALSE;
    316   }
    317 
    318   if(add) {
    319     if(!Curl_llist_insert_next(llist, llist->tail, finfo)) {
    320       Curl_fileinfo_dtor(NULL, finfo);
    321       tmpdata->parser->file_data = NULL;
    322       return CURLE_OUT_OF_MEMORY;
    323     }
    324   }
    325   else {
    326     Curl_fileinfo_dtor(NULL, finfo);
    327   }
    328 
    329   tmpdata->parser->file_data = NULL;
    330   return CURLE_OK;
    331 }
    332 
    333 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
    334                           void *connptr)
    335 {
    336   size_t bufflen = size*nmemb;
    337   struct connectdata *conn = (struct connectdata *)connptr;
    338   struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
    339   struct ftp_parselist_data *parser = tmpdata->parser;
    340   struct curl_fileinfo *finfo;
    341   unsigned long i = 0;
    342   CURLcode result;
    343 
    344   if(parser->error) { /* error in previous call */
    345     /* scenario:
    346      * 1. call => OK..
    347      * 2. call => OUT_OF_MEMORY (or other error)
    348      * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
    349      *    in wc_statemach()
    350      */
    351     return bufflen;
    352   }
    353 
    354   if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
    355     /* considering info about FILE response format */
    356     parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
    357                        OS_TYPE_WIN_NT : OS_TYPE_UNIX;
    358   }
    359 
    360   while(i < bufflen) { /* FSM */
    361 
    362     char c = buffer[i];
    363     if(!parser->file_data) { /* tmp file data is not allocated yet */
    364       parser->file_data = Curl_fileinfo_alloc();
    365       if(!parser->file_data) {
    366         parser->error = CURLE_OUT_OF_MEMORY;
    367         return bufflen;
    368       }
    369       parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
    370       if(!parser->file_data->b_data) {
    371         PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
    372         return bufflen;
    373       }
    374       parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
    375       parser->item_offset = 0;
    376       parser->item_length = 0;
    377     }
    378 
    379     finfo = parser->file_data;
    380     finfo->b_data[finfo->b_used++] = c;
    381 
    382     if(finfo->b_used >= finfo->b_size - 1) {
    383       /* if it is important, extend buffer space for file data */
    384       char *tmp = realloc(finfo->b_data,
    385                           finfo->b_size + FTP_BUFFER_ALLOCSIZE);
    386       if(tmp) {
    387         finfo->b_size += FTP_BUFFER_ALLOCSIZE;
    388         finfo->b_data = tmp;
    389       }
    390       else {
    391         Curl_fileinfo_dtor(NULL, parser->file_data);
    392         parser->file_data = NULL;
    393         parser->error = CURLE_OUT_OF_MEMORY;
    394         PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
    395         return bufflen;
    396       }
    397     }
    398 
    399     switch (parser->os_type) {
    400     case OS_TYPE_UNIX:
    401       switch (parser->state.UNIX.main) {
    402       case PL_UNIX_TOTALSIZE:
    403         switch(parser->state.UNIX.sub.total_dirsize) {
    404         case PL_UNIX_TOTALSIZE_INIT:
    405           if(c == 't') {
    406             parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
    407             parser->item_length++;
    408           }
    409           else {
    410             parser->state.UNIX.main = PL_UNIX_FILETYPE;
    411             /* start FSM again not considering size of directory */
    412             finfo->b_used = 0;
    413             i--;
    414           }
    415           break;
    416         case PL_UNIX_TOTALSIZE_READING:
    417           parser->item_length++;
    418           if(c == '\r') {
    419             parser->item_length--;
    420             finfo->b_used--;
    421           }
    422           else if(c == '\n') {
    423             finfo->b_data[parser->item_length - 1] = 0;
    424             if(strncmp("total ", finfo->b_data, 6) == 0) {
    425               char *endptr = finfo->b_data+6;
    426               /* here we can deal with directory size, pass the leading white
    427                  spaces and then the digits */
    428               while(ISSPACE(*endptr))
    429                 endptr++;
    430               while(ISDIGIT(*endptr))
    431                 endptr++;
    432               if(*endptr != 0) {
    433                 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    434                 return bufflen;
    435               }
    436               else {
    437                 parser->state.UNIX.main = PL_UNIX_FILETYPE;
    438                 finfo->b_used = 0;
    439               }
    440             }
    441             else {
    442               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    443               return bufflen;
    444             }
    445           }
    446           break;
    447         }
    448         break;
    449       case PL_UNIX_FILETYPE:
    450         switch (c) {
    451         case '-':
    452           finfo->filetype = CURLFILETYPE_FILE;
    453           break;
    454         case 'd':
    455           finfo->filetype = CURLFILETYPE_DIRECTORY;
    456           break;
    457         case 'l':
    458           finfo->filetype = CURLFILETYPE_SYMLINK;
    459           break;
    460         case 'p':
    461           finfo->filetype = CURLFILETYPE_NAMEDPIPE;
    462           break;
    463         case 's':
    464           finfo->filetype = CURLFILETYPE_SOCKET;
    465           break;
    466         case 'c':
    467           finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
    468           break;
    469         case 'b':
    470           finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
    471           break;
    472         case 'D':
    473           finfo->filetype = CURLFILETYPE_DOOR;
    474           break;
    475         default:
    476           PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    477           return bufflen;
    478         }
    479         parser->state.UNIX.main = PL_UNIX_PERMISSION;
    480         parser->item_length = 0;
    481         parser->item_offset = 1;
    482         break;
    483       case PL_UNIX_PERMISSION:
    484         parser->item_length++;
    485         if(parser->item_length <= 9) {
    486           if(!strchr("rwx-tTsS", c)) {
    487             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    488             return bufflen;
    489           }
    490         }
    491         else if(parser->item_length == 10) {
    492           unsigned int perm;
    493           if(c != ' ') {
    494             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    495             return bufflen;
    496           }
    497           finfo->b_data[10] = 0; /* terminate permissions */
    498           perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
    499           if(perm & FTP_LP_MALFORMATED_PERM) {
    500             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    501             return bufflen;
    502           }
    503           parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM;
    504           parser->file_data->perm = perm;
    505           parser->offsets.perm = parser->item_offset;
    506 
    507           parser->item_length = 0;
    508           parser->state.UNIX.main = PL_UNIX_HLINKS;
    509           parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
    510         }
    511         break;
    512       case PL_UNIX_HLINKS:
    513         switch(parser->state.UNIX.sub.hlinks) {
    514         case PL_UNIX_HLINKS_PRESPACE:
    515           if(c != ' ') {
    516             if(c >= '0' && c <= '9') {
    517               parser->item_offset = finfo->b_used - 1;
    518               parser->item_length = 1;
    519               parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
    520             }
    521             else {
    522               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    523               return bufflen;
    524             }
    525           }
    526           break;
    527         case PL_UNIX_HLINKS_NUMBER:
    528           parser->item_length ++;
    529           if(c == ' ') {
    530             char *p;
    531             long int hlinks;
    532             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    533             hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
    534             if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
    535               parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
    536               parser->file_data->hardlinks = hlinks;
    537             }
    538             parser->item_length = 0;
    539             parser->item_offset = 0;
    540             parser->state.UNIX.main = PL_UNIX_USER;
    541             parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
    542           }
    543           else if(c < '0' || c > '9') {
    544             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    545             return bufflen;
    546           }
    547           break;
    548         }
    549         break;
    550       case PL_UNIX_USER:
    551         switch(parser->state.UNIX.sub.user) {
    552         case PL_UNIX_USER_PRESPACE:
    553           if(c != ' ') {
    554             parser->item_offset = finfo->b_used - 1;
    555             parser->item_length = 1;
    556             parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
    557           }
    558           break;
    559         case PL_UNIX_USER_PARSING:
    560           parser->item_length++;
    561           if(c == ' ') {
    562             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    563             parser->offsets.user = parser->item_offset;
    564             parser->state.UNIX.main = PL_UNIX_GROUP;
    565             parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
    566             parser->item_offset = 0;
    567             parser->item_length = 0;
    568           }
    569           break;
    570         }
    571         break;
    572       case PL_UNIX_GROUP:
    573         switch(parser->state.UNIX.sub.group) {
    574         case PL_UNIX_GROUP_PRESPACE:
    575           if(c != ' ') {
    576             parser->item_offset = finfo->b_used - 1;
    577             parser->item_length = 1;
    578             parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
    579           }
    580           break;
    581         case PL_UNIX_GROUP_NAME:
    582           parser->item_length++;
    583           if(c == ' ') {
    584             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    585             parser->offsets.group = parser->item_offset;
    586             parser->state.UNIX.main = PL_UNIX_SIZE;
    587             parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
    588             parser->item_offset = 0;
    589             parser->item_length = 0;
    590           }
    591           break;
    592         }
    593         break;
    594       case PL_UNIX_SIZE:
    595         switch(parser->state.UNIX.sub.size) {
    596         case PL_UNIX_SIZE_PRESPACE:
    597           if(c != ' ') {
    598             if(c >= '0' && c <= '9') {
    599               parser->item_offset = finfo->b_used - 1;
    600               parser->item_length = 1;
    601               parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
    602             }
    603             else {
    604               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    605               return bufflen;
    606             }
    607           }
    608           break;
    609         case PL_UNIX_SIZE_NUMBER:
    610           parser->item_length++;
    611           if(c == ' ') {
    612             char *p;
    613             curl_off_t fsize;
    614             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    615             fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10);
    616             if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
    617                                fsize != CURL_OFF_T_MIN) {
    618               parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
    619               parser->file_data->size = fsize;
    620             }
    621             parser->item_length = 0;
    622             parser->item_offset = 0;
    623             parser->state.UNIX.main = PL_UNIX_TIME;
    624             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
    625           }
    626           else if(!ISDIGIT(c)) {
    627             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    628             return bufflen;
    629           }
    630           break;
    631         }
    632         break;
    633       case PL_UNIX_TIME:
    634         switch(parser->state.UNIX.sub.time) {
    635         case PL_UNIX_TIME_PREPART1:
    636           if(c != ' ') {
    637             if(ISALNUM(c)) {
    638               parser->item_offset = finfo->b_used -1;
    639               parser->item_length = 1;
    640               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
    641             }
    642             else {
    643               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    644               return bufflen;
    645             }
    646           }
    647           break;
    648         case PL_UNIX_TIME_PART1:
    649           parser->item_length++;
    650           if(c == ' ') {
    651             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
    652           }
    653           else if(!ISALNUM(c) && c != '.') {
    654             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    655             return bufflen;
    656           }
    657           break;
    658         case PL_UNIX_TIME_PREPART2:
    659           parser->item_length++;
    660           if(c != ' ') {
    661             if(ISALNUM(c)) {
    662               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
    663             }
    664             else {
    665               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    666               return bufflen;
    667             }
    668           }
    669           break;
    670         case PL_UNIX_TIME_PART2:
    671           parser->item_length++;
    672           if(c == ' ') {
    673             parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
    674           }
    675           else if(!ISALNUM(c) && c != '.') {
    676             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    677             return bufflen;
    678           }
    679           break;
    680         case PL_UNIX_TIME_PREPART3:
    681           parser->item_length++;
    682           if(c != ' ') {
    683             if(ISALNUM(c)) {
    684               parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
    685             }
    686             else {
    687               PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    688               return bufflen;
    689             }
    690           }
    691           break;
    692         case PL_UNIX_TIME_PART3:
    693           parser->item_length++;
    694           if(c == ' ') {
    695             finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
    696             parser->offsets.time = parser->item_offset;
    697             /*
    698               if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
    699                 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
    700               }
    701             */
    702             if(finfo->filetype == CURLFILETYPE_SYMLINK) {
    703               parser->state.UNIX.main = PL_UNIX_SYMLINK;
    704               parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
    705             }
    706             else {
    707               parser->state.UNIX.main = PL_UNIX_FILENAME;
    708               parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
    709             }
    710           }
    711           else if(!ISALNUM(c) && c != '.' && c != ':') {
    712             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    713             return bufflen;
    714           }
    715           break;
    716         }
    717         break;
    718       case PL_UNIX_FILENAME:
    719         switch(parser->state.UNIX.sub.filename) {
    720         case PL_UNIX_FILENAME_PRESPACE:
    721           if(c != ' ') {
    722             parser->item_offset = finfo->b_used - 1;
    723             parser->item_length = 1;
    724             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
    725           }
    726           break;
    727         case PL_UNIX_FILENAME_NAME:
    728           parser->item_length++;
    729           if(c == '\r') {
    730             parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
    731           }
    732           else if(c == '\n') {
    733             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    734             parser->offsets.filename = parser->item_offset;
    735             parser->state.UNIX.main = PL_UNIX_FILETYPE;
    736             result = ftp_pl_insert_finfo(conn, finfo);
    737             if(result) {
    738               PL_ERROR(conn, result);
    739               return bufflen;
    740             }
    741           }
    742           break;
    743         case PL_UNIX_FILENAME_WINDOWSEOL:
    744           if(c == '\n') {
    745             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    746             parser->offsets.filename = parser->item_offset;
    747             parser->state.UNIX.main = PL_UNIX_FILETYPE;
    748             result = ftp_pl_insert_finfo(conn, finfo);
    749             if(result) {
    750               PL_ERROR(conn, result);
    751               return bufflen;
    752             }
    753           }
    754           else {
    755             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    756             return bufflen;
    757           }
    758           break;
    759         }
    760         break;
    761       case PL_UNIX_SYMLINK:
    762         switch(parser->state.UNIX.sub.symlink) {
    763         case PL_UNIX_SYMLINK_PRESPACE:
    764           if(c != ' ') {
    765             parser->item_offset = finfo->b_used - 1;
    766             parser->item_length = 1;
    767             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
    768           }
    769           break;
    770         case PL_UNIX_SYMLINK_NAME:
    771           parser->item_length++;
    772           if(c == ' ') {
    773             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
    774           }
    775           else if(c == '\r' || c == '\n') {
    776             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    777             return bufflen;
    778           }
    779           break;
    780         case PL_UNIX_SYMLINK_PRETARGET1:
    781           parser->item_length++;
    782           if(c == '-') {
    783             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
    784           }
    785           else if(c == '\r' || c == '\n') {
    786             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    787             return bufflen;
    788           }
    789           else {
    790             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
    791           }
    792           break;
    793         case PL_UNIX_SYMLINK_PRETARGET2:
    794           parser->item_length++;
    795           if(c == '>') {
    796             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
    797           }
    798           else if(c == '\r' || c == '\n') {
    799             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    800             return bufflen;
    801           }
    802           else {
    803             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
    804           }
    805           break;
    806         case PL_UNIX_SYMLINK_PRETARGET3:
    807           parser->item_length++;
    808           if(c == ' ') {
    809             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
    810             /* now place where is symlink following */
    811             finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
    812             parser->offsets.filename = parser->item_offset;
    813             parser->item_length = 0;
    814             parser->item_offset = 0;
    815           }
    816           else if(c == '\r' || c == '\n') {
    817             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    818             return bufflen;
    819           }
    820           else {
    821             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
    822           }
    823           break;
    824         case PL_UNIX_SYMLINK_PRETARGET4:
    825           if(c != '\r' && c != '\n') {
    826             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
    827             parser->item_offset = finfo->b_used - 1;
    828             parser->item_length = 1;
    829           }
    830           else {
    831             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    832             return bufflen;
    833           }
    834           break;
    835         case PL_UNIX_SYMLINK_TARGET:
    836           parser->item_length++;
    837           if(c == '\r') {
    838             parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
    839           }
    840           else if(c == '\n') {
    841             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    842             parser->offsets.symlink_target = parser->item_offset;
    843             result = ftp_pl_insert_finfo(conn, finfo);
    844             if(result) {
    845               PL_ERROR(conn, result);
    846               return bufflen;
    847             }
    848             parser->state.UNIX.main = PL_UNIX_FILETYPE;
    849           }
    850           break;
    851         case PL_UNIX_SYMLINK_WINDOWSEOL:
    852           if(c == '\n') {
    853             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    854             parser->offsets.symlink_target = parser->item_offset;
    855             result = ftp_pl_insert_finfo(conn, finfo);
    856             if(result) {
    857               PL_ERROR(conn, result);
    858               return bufflen;
    859             }
    860             parser->state.UNIX.main = PL_UNIX_FILETYPE;
    861           }
    862           else {
    863             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    864             return bufflen;
    865           }
    866           break;
    867         }
    868         break;
    869       }
    870       break;
    871     case OS_TYPE_WIN_NT:
    872       switch(parser->state.NT.main) {
    873       case PL_WINNT_DATE:
    874         parser->item_length++;
    875         if(parser->item_length < 9) {
    876           if(!strchr("0123456789-", c)) { /* only simple control */
    877             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    878             return bufflen;
    879           }
    880         }
    881         else if(parser->item_length == 9) {
    882           if(c == ' ') {
    883             parser->state.NT.main = PL_WINNT_TIME;
    884             parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
    885           }
    886           else {
    887             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    888             return bufflen;
    889           }
    890         }
    891         else {
    892           PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    893           return bufflen;
    894         }
    895         break;
    896       case PL_WINNT_TIME:
    897         parser->item_length++;
    898         switch(parser->state.NT.sub.time) {
    899         case PL_WINNT_TIME_PRESPACE:
    900           if(!ISSPACE(c)) {
    901             parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
    902           }
    903           break;
    904         case PL_WINNT_TIME_TIME:
    905           if(c == ' ') {
    906             parser->offsets.time = parser->item_offset;
    907             finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
    908             parser->state.NT.main = PL_WINNT_DIRORSIZE;
    909             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
    910             parser->item_length = 0;
    911           }
    912           else if(!strchr("APM0123456789:", c)) {
    913             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    914             return bufflen;
    915           }
    916           break;
    917         }
    918         break;
    919       case PL_WINNT_DIRORSIZE:
    920         switch(parser->state.NT.sub.dirorsize) {
    921         case PL_WINNT_DIRORSIZE_PRESPACE:
    922           if(c == ' ') {
    923 
    924           }
    925           else {
    926             parser->item_offset = finfo->b_used - 1;
    927             parser->item_length = 1;
    928             parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
    929           }
    930           break;
    931         case PL_WINNT_DIRORSIZE_CONTENT:
    932           parser->item_length ++;
    933           if(c == ' ') {
    934             finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
    935             if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
    936               finfo->filetype = CURLFILETYPE_DIRECTORY;
    937               finfo->size = 0;
    938             }
    939             else {
    940               char *endptr;
    941               finfo->size = curlx_strtoofft(finfo->b_data +
    942                                             parser->item_offset,
    943                                             &endptr, 10);
    944               if(!*endptr) {
    945                 if(finfo->size == CURL_OFF_T_MAX ||
    946                    finfo->size == CURL_OFF_T_MIN) {
    947                   if(errno == ERANGE) {
    948                     PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    949                     return bufflen;
    950                   }
    951                 }
    952               }
    953               else {
    954                 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
    955                 return bufflen;
    956               }
    957               /* correct file type */
    958               parser->file_data->filetype = CURLFILETYPE_FILE;
    959             }
    960 
    961             parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
    962             parser->item_length = 0;
    963             parser->state.NT.main = PL_WINNT_FILENAME;
    964             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
    965           }
    966           break;
    967         }
    968         break;
    969       case PL_WINNT_FILENAME:
    970         switch (parser->state.NT.sub.filename) {
    971         case PL_WINNT_FILENAME_PRESPACE:
    972           if(c != ' ') {
    973             parser->item_offset = finfo->b_used -1;
    974             parser->item_length = 1;
    975             parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
    976           }
    977           break;
    978         case PL_WINNT_FILENAME_CONTENT:
    979           parser->item_length++;
    980           if(c == '\r') {
    981             parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
    982             finfo->b_data[finfo->b_used - 1] = 0;
    983           }
    984           else if(c == '\n') {
    985             parser->offsets.filename = parser->item_offset;
    986             finfo->b_data[finfo->b_used - 1] = 0;
    987             parser->offsets.filename = parser->item_offset;
    988             result = ftp_pl_insert_finfo(conn, finfo);
    989             if(result) {
    990               PL_ERROR(conn, result);
    991               return bufflen;
    992             }
    993             parser->state.NT.main = PL_WINNT_DATE;
    994             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
    995           }
    996           break;
    997         case PL_WINNT_FILENAME_WINEOL:
    998           if(c == '\n') {
    999             parser->offsets.filename = parser->item_offset;
   1000             result = ftp_pl_insert_finfo(conn, finfo);
   1001             if(result) {
   1002               PL_ERROR(conn, result);
   1003               return bufflen;
   1004             }
   1005             parser->state.NT.main = PL_WINNT_DATE;
   1006             parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
   1007           }
   1008           else {
   1009             PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
   1010             return bufflen;
   1011           }
   1012           break;
   1013         }
   1014         break;
   1015       }
   1016       break;
   1017     default:
   1018       return bufflen + 1;
   1019     }
   1020 
   1021     i++;
   1022   }
   1023 
   1024   return bufflen;
   1025 }
   1026 
   1027 #endif /* CURL_DISABLE_FTP */
   1028