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