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