diff options
Diffstat (limited to 'lib/ftplistparser.c')
-rw-r--r-- | lib/ftplistparser.c | 1009 |
1 files changed, 1009 insertions, 0 deletions
diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c new file mode 100644 index 000000000..9d42e8f37 --- /dev/null +++ b/lib/ftplistparser.c @@ -0,0 +1,1009 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/** + * Now implemented: + * + * 1) UNIX version 1 + * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog + * 2) UNIX version 2 + * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog + * 3) UNIX version 3 + * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog + * 4) UNIX symlink + * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000 + * 5) DOS style + * 01-29-97 11:32PM <DIR> prog + */ + +#include <time.h> + +#include "ftplistparser.h" +#include "curl_fnmatch.h" + +#include "urldata.h" +#include "ftp.h" +#include "fileinfo.h" +#include "llist.h" +#include "strtoofft.h" +#include "rawstr.h" +#include "ftp.h" + +/* allocs buffer which will contain one line of LIST command response */ +#define FTP_BUFFER_ALLOCSIZE 160 + +typedef enum { + PL_UNIX_FILETYPE = 0, + PL_UNIX_PERMISSION, + PL_UNIX_HLINKS, + PL_UNIX_USER, + PL_UNIX_GROUP, + PL_UNIX_SIZE, + PL_UNIX_TIME, + PL_UNIX_FILENAME, + PL_UNIX_SYMLINK +} pl_unix_mainstate; + +typedef union { + enum { + PL_UNIX_HLINKS_PRESPACE = 0, + PL_UNIX_HLINKS_NUMBER + } hlinks; + + enum { + PL_UNIX_USER_PRESPACE = 0, + PL_UNIX_USER_PARSING + } user; + + enum { + PL_UNIX_GROUP_PRESPACE = 0, + PL_UNIX_GROUP_NAME + } group; + + enum { + PL_UNIX_SIZE_PRESPACE = 0, + PL_UNIX_SIZE_NUMBER + } size; + + enum { + PL_UNIX_TIME_PREPART1 = 0, + PL_UNIX_TIME_PART1, + PL_UNIX_TIME_PREPART2, + PL_UNIX_TIME_PART2, + PL_UNIX_TIME_PREPART3, + PL_UNIX_TIME_PART3 + } time; + + enum { + PL_UNIX_FILENAME_PRESPACE = 0, + PL_UNIX_FILENAME_NAME, + PL_UNIX_FILENAME_WINDOWSEOL + } filename; + + enum { + PL_UNIX_SYMLINK_PRESPACE = 0, + PL_UNIX_SYMLINK_NAME, + PL_UNIX_SYMLINK_PRETARGET1, + PL_UNIX_SYMLINK_PRETARGET2, + PL_UNIX_SYMLINK_PRETARGET3, + PL_UNIX_SYMLINK_PRETARGET4, + PL_UNIX_SYMLINK_TARGET, + PL_UNIX_SYMLINK_WINDOWSEOL + } symlink; +} pl_unix_substate; + +typedef enum { + PL_WINNT_DATE = 0, + PL_WINNT_TIME, + PL_WINNT_DIRORSIZE, + PL_WINNT_FILENAME +} pl_winNT_mainstate; + +typedef union { + enum { + PL_WINNT_TIME_PRESPACE = 0, + PL_WINNT_TIME_TIME + } time; + enum { + PL_WINNT_DIRORSIZE_PRESPACE = 0, + PL_WINNT_DIRORSIZE_CONTENT + } dirorsize; + enum { + PL_WINNT_FILENAME_PRESPACE = 0, + PL_WINNT_FILENAME_CONTENT, + PL_WINNT_FILENAME_WINEOL + } filename; +} pl_winNT_substate; + +/* This struct is used in wildcard downloading - for parsing LIST response */ +struct ftp_parselist_data { + enum { + OS_TYPE_UNKNOWN = 0, + OS_TYPE_UNIX, + OS_TYPE_WIN_NT + } os_type; + + union { + struct { + pl_unix_mainstate main; + pl_unix_substate sub; + } UNIX; + + struct { + pl_winNT_mainstate main; + pl_winNT_substate sub; + } NT; + } state; + + struct { + char *buffer; + size_t bufferlength; /* how many bytes is allocated at *buffer */ + size_t bufferin; /* how many bytes is in buffer */ + } tmpdata; + + struct { + curl_write_callback old_fwritefunc; + FILE *old_file_descriptor; + } backup; + + CURLcode error; + struct curl_fileinfo *file_data; + unsigned int item_length; + size_t item_offset; + struct { + size_t filename; + size_t user; + size_t group; + size_t time; + size_t perm; + size_t symlink_target; + } offsets; +}; + +struct ftp_parselist_data *ftp_parselist_data_alloc(void) +{ + struct ftp_parselist_data *parselist_data = + malloc(sizeof(struct ftp_parselist_data)); + if(!parselist_data) + return ZERO_NULL; + memset(parselist_data, 0, sizeof(struct ftp_parselist_data)); + return parselist_data; +} + + +void ftp_parselist_data_free(struct ftp_parselist_data **pl_data) +{ + if(*pl_data) + free(*pl_data); + *pl_data = NULL; +} + + +CURLcode ftp_parselist_geterror(struct ftp_parselist_data *pl_data) +{ + return pl_data->error; +} + + +#define FTP_LP_MALFORMATED_PERM 0x01000000 + +static int ftp_pl_get_permission(const char *str) +{ + int permissions = 0; + /* USER */ + if(str[0] == 'r') + permissions |= 1 << 8; + else if(str[0] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[1] == 'w') + permissions |= 1 << 7; + else if(str[1] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + + if(str[2] == 'x') + permissions |= 1 << 6; + else if(str[2] == 's') { + permissions |= 1 << 6; + permissions |= 1 << 11; + } + else if(str[2] == 'S') + permissions |= 1 << 11; + else if(str[2] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + /* GROUP */ + if(str[3] == 'r') + permissions |= 1 << 5; + else if(str[3] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[4] == 'w') + permissions |= 1 << 4; + else if(str[4] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[5] == 'x') + permissions |= 1 << 3; + else if(str[5] == 's') { + permissions |= 1 << 3; + permissions |= 1 << 10; + } + else if(str[5] == 'S') + permissions |= 1 << 10; + else if(str[5] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + /* others */ + if(str[6] == 'r') + permissions |= 1 << 2; + else if(str[6] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[7] == 'w') + permissions |= 1 << 1; + else if(str[7] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + if(str[8] == 'x') + permissions |= 1; + else if(str[8] == 't') { + permissions |= 1; + permissions |= 1 << 9; + } + else if(str[8] == 'T') + permissions |= 1 << 9; + else if(str[8] != '-') + permissions |= FTP_LP_MALFORMATED_PERM; + + return permissions; +} + +static void PL_ERROR(struct connectdata *conn, CURLcode err) +{ + struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp; + struct ftp_parselist_data *parser = tmpdata->parser; + if(parser->file_data) + Curl_fileinfo_dtor(NULL, parser->file_data); + parser->file_data = NULL; + parser->error = err; +} + +static bool ftp_pl_gettime(struct ftp_parselist_data *parser, char *string) +{ + (void)parser; + (void)string; + /* TODO + * There could be possible parse timestamp from server. Leaving unimplemented + * for now. + * If you want implement this, please add CURLFINFOFLAG_KNOWN_TIME flag to + * parser->file_data->flags + * + * Ftp servers are giving usually these formats: + * Apr 11 1998 (unknown time.. set it to 00:00:00?) + * Apr 11 12:21 (unknown year -> set it to NOW() time?) + * 08-05-09 02:49PM (ms-dos format) + * 20100421092538 -> for MLST/MLSD response + */ + + return FALSE; +} + +static CURLcode ftp_pl_insert_finfo(struct connectdata *conn, + struct curl_fileinfo *finfo) +{ + curl_fnmatch_callback compare; + struct WildcardData *wc = &conn->data->wildcard; + struct ftp_wc_tmpdata *tmpdata = wc->tmp; + struct curl_llist *llist = wc->filelist; + struct ftp_parselist_data *parser = tmpdata->parser; + bool add = TRUE; + + /* move finfo pointers to b_data */ + char *str = finfo->b_data; + finfo->filename = str + parser->offsets.filename; + finfo->strings.group = parser->offsets.group ? + str + parser->offsets.group : NULL; + finfo->strings.perm = parser->offsets.perm ? + str + parser->offsets.perm : NULL; + finfo->strings.target = parser->offsets.symlink_target ? + str + parser->offsets.symlink_target : NULL; + finfo->strings.time = str + parser->offsets.time; + finfo->strings.user = parser->offsets.user ? + str + parser->offsets.user : NULL; + + /* get correct fnmatch callback */ + compare = conn->data->set.fnmatch; + if(!compare) + compare = Curl_fnmatch; + + /* filter pattern-corresponding filenames */ + if(compare(wc->pattern, finfo->filename) == 0) { + /* discard symlink which is containing multiple " -> " */ + if((finfo->filetype == CURLFILETYPE_SYMLINK) && + (strstr(finfo->strings.target, " -> "))) { + add = FALSE; + } + } + else { + add = FALSE; + } + + if(add) { + if(!Curl_llist_insert_next(llist, llist->tail, finfo)) { + Curl_fileinfo_dtor(NULL, finfo); + tmpdata->parser->file_data = NULL; + return CURLE_OUT_OF_MEMORY; + } + } + else { + Curl_fileinfo_dtor(NULL, finfo); + } + + tmpdata->parser->file_data = NULL; + return CURLE_OK; +} + +size_t ftp_parselist(char *buffer, size_t size, size_t nmemb, void *connptr) +{ + size_t bufflen = size*nmemb; + struct connectdata *conn = (struct connectdata *)connptr; + struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp; + struct ftp_parselist_data *parser = tmpdata->parser; + struct curl_fileinfo *finfo; + unsigned long i = 0; + CURLcode rc; + + if(parser->error) { /* error in previous call */ + /* scenario: + * 1. call => OK.. + * 2. call => OUT_OF_MEMORY (or other error) + * 3. (last) call => is skipped RIGHT HERE and the error is hadled later + * in wc_statemach() + */ + return bufflen; + } + + if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { + /* considering info about FILE response format */ + parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ? + OS_TYPE_WIN_NT : OS_TYPE_UNIX; + } + + while(i < bufflen) { /* FSM */ + + char c = buffer[i]; + if(!parser->file_data) { /* tmp file data is not allocated yet */ + parser->file_data = Curl_fileinfo_alloc(); + if(!parser->file_data) { + parser->error = CURLE_OUT_OF_MEMORY; + return bufflen; + } + parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE); + if(!parser->file_data->b_data) { + PL_ERROR(conn, CURLE_OUT_OF_MEMORY); + return bufflen; + } + parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE; + parser->item_offset = 0; + parser->item_length = 0; + } + + finfo = parser->file_data; + finfo->b_data[finfo->b_used++] = buffer[i]; + + if(finfo->b_used >= finfo->b_size - 1) { + /* if it is important, extend buffer space for file data */ + char *tmp = realloc(finfo->b_data, + finfo->b_size + FTP_BUFFER_ALLOCSIZE); + if(tmp) { + finfo->b_size += FTP_BUFFER_ALLOCSIZE; + finfo->b_data = tmp; + } + else { + Curl_fileinfo_dtor(NULL, parser->file_data); + parser->file_data = NULL; + } + } + + switch (parser->os_type) { + case OS_TYPE_UNIX: + switch (parser->state.UNIX.main) { + case PL_UNIX_FILETYPE: + switch (c) { + case '-': + finfo->filetype = CURLFILETYPE_FILE; + break; + case 'd': + finfo->filetype = CURLFILETYPE_DIRECTORY; + break; + case 'l': + finfo->filetype = CURLFILETYPE_SYMLINK; + break; + case 'p': + finfo->filetype = CURLFILETYPE_NAMEDPIPE; + break; + case 's': + finfo->filetype = CURLFILETYPE_SOCKET; + break; + case 'c': + finfo->filetype = CURLFILETYPE_DEVICE_CHAR; + break; + case 'b': + finfo->filetype = CURLFILETYPE_DEVICE_BLOCK; + break; + case 'D': + finfo->filetype = CURLFILETYPE_DOOR; + break; + default: + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + break; + } + parser->state.UNIX.main = PL_UNIX_PERMISSION; + parser->item_length = 0; + parser->item_offset = 1; + break; + case PL_UNIX_PERMISSION: + parser->item_length++; + if(parser->item_length <= 9) { + if(!strchr("rwx-tTsS", c)) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else if(parser->item_length == 10) { + int32_t perm; + if(c != ' ') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + finfo->b_data[10] = 0; /* terminate permissions */ + perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); + if(perm & FTP_LP_MALFORMATED_PERM) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM; + parser->file_data->perm = perm; + parser->offsets.perm = parser->item_offset; + + parser->item_length = 0; + parser->state.UNIX.main = PL_UNIX_HLINKS; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; + } + break; + case PL_UNIX_HLINKS: + switch(parser->state.UNIX.sub.hlinks) { + case PL_UNIX_HLINKS_PRESPACE: + if(c != ' ') { + if(c >= '0' && c <= '9') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + break; + case PL_UNIX_HLINKS_NUMBER: + parser->item_length ++; + if(c == ' ') { + char *p; + long int hlinks; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10); + if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; + parser->file_data->hardlinks = hlinks; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_USER; + parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; + } + else if(c < '0' || c > '9') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + case PL_UNIX_USER: + switch(parser->state.UNIX.sub.user) { + case PL_UNIX_USER_PRESPACE: + if(c != ' ') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; + } + break; + case PL_UNIX_USER_PARSING: + parser->item_length++; + if(c == ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.user = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_GROUP; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + break; + case PL_UNIX_GROUP: + switch(parser->state.UNIX.sub.group) { + case PL_UNIX_GROUP_PRESPACE: + if(c != ' ') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; + } + break; + case PL_UNIX_GROUP_NAME: + parser->item_length++; + if(c == ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.group = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_SIZE; + parser->state.UNIX.sub.group = PL_UNIX_SIZE_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + break; + case PL_UNIX_SIZE: + switch(parser->state.UNIX.sub.size) { + case PL_UNIX_SIZE_PRESPACE: + if(c != ' ') { + if(c >= '0' && c <= '9') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + break; + case PL_UNIX_SIZE_NUMBER: + parser->item_length++; + if(c == ' ') { + char *p; + curl_off_t fsize; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10); + if(p[0] == '\0' && fsize != CURL_LLONG_MAX && + fsize != CURL_LLONG_MIN) { + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; + parser->file_data->size = fsize; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_TIME; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; + } + else if (!ISDIGIT(c)) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + case PL_UNIX_TIME: + switch(parser->state.UNIX.sub.time) { + case PL_UNIX_TIME_PREPART1: + if(c != ' ') { + if(ISALNUM(c)) { + parser->item_offset = finfo->b_used -1; + parser->item_length = 1; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + break; + case PL_UNIX_TIME_PART1: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.size = PL_UNIX_TIME_PREPART2; + } + else if(!ISALNUM(c) && c != '.') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_TIME_PREPART2: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + break; + case PL_UNIX_TIME_PART2: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.size = PL_UNIX_TIME_PREPART3; + } + else if(!ISALNUM(c) && c != '.') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_TIME_PREPART3: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + break; + case PL_UNIX_TIME_PART3: + parser->item_length++; + if(c == ' ') { + finfo->b_data[parser->item_offset + parser->item_length -1] = 0; + parser->offsets.time = parser->item_offset; + if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) { + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; + } + if(finfo->filetype == CURLFILETYPE_SYMLINK) { + parser->state.UNIX.main = PL_UNIX_SYMLINK; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; + } + else { + parser->state.UNIX.main = PL_UNIX_FILENAME; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; + } + } + else if(!ISALNUM(c) && c != '.' && c != ':') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + case PL_UNIX_FILENAME: + switch(parser->state.UNIX.sub.filename) { + case PL_UNIX_FILENAME_PRESPACE: + if(c != ' ') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; + } + break; + case PL_UNIX_FILENAME_NAME: + parser->item_length++; + if(c == '\r') { + parser->item_length--; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; + } + else if(c == '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + } + break; + case PL_UNIX_FILENAME_WINDOWSEOL: + if(c == '\n') { + finfo->b_data[parser->item_offset + parser->item_length] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + case PL_UNIX_SYMLINK: + switch(parser->state.UNIX.sub.symlink) { + case PL_UNIX_SYMLINK_PRESPACE: + if(c != ' ') { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_NAME: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; + } + else if(c == '\r' || c == '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_SYMLINK_PRETARGET1: + parser->item_length++; + if(c == '-') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; + } + else if(c == '\r' || c == '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET2: + parser->item_length++; + if(c == '>') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; + } + else if(c == '\r' || c == '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET3: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; + /* now place where is symlink following */ + finfo->b_data[parser->item_offset + parser->item_length - 4] = 0; + parser->offsets.filename = parser->item_offset; + parser->item_length = 0; + parser->item_offset = 0; + } + else if(c == '\r' || c == '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET4: + if(c != '\r' && c != '\n') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_SYMLINK_TARGET: + parser->item_length ++; + if(c == '\r') { + parser->item_length --; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; + } + else if(c == '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + break; + case PL_UNIX_SYMLINK_WINDOWSEOL: + if(c == '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + } + break; + case OS_TYPE_WIN_NT: + switch(parser->state.NT.main) { + case PL_WINNT_DATE: + parser->item_length++; + if(parser->item_length < 9) { + if(!strchr("0123456789-", c)) { /* only simple control */ + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else if(parser->item_length == 9) { + if(c == ' ') { + parser->state.NT.main = PL_WINNT_TIME; + parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_WINNT_TIME: + parser->item_length++; + switch(parser->state.NT.sub.time) { + case PL_WINNT_TIME_PRESPACE: + if(!ISSPACE(c)) { + parser->state.NT.sub.time = PL_WINNT_TIME_TIME; + } + break; + case PL_WINNT_TIME_TIME: + if(c == ' ') { + parser->offsets.time = parser->item_offset; + finfo->b_data[parser->item_offset + parser->item_length -1] = 0; + parser->state.NT.main = PL_WINNT_DIRORSIZE; + parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; + parser->item_length = 0; + } + else if(!strchr("APM0123456789:", c)) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + case PL_WINNT_DIRORSIZE: + switch(parser->state.NT.sub.dirorsize) { + case PL_WINNT_DIRORSIZE_PRESPACE: + if(c == ' ') { + + } + else { + parser->item_offset = finfo->b_used - 1; + parser->item_length = 1; + parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; + } + break; + case PL_WINNT_DIRORSIZE_CONTENT: + parser->item_length ++; + if(c == ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) { + finfo->filetype = CURLFILETYPE_DIRECTORY; + finfo->size = 0; + } + else { + char *endptr; + finfo->size = curlx_strtoofft(finfo->b_data + parser->item_offset, + &endptr, 10); + if(!*endptr) { + if(finfo->size < CURL_LLONG_MAX && + finfo->size > CURL_LLONG_MIN) { + + } + else if(finfo->size == CURL_LLONG_MAX || + finfo->size == CURL_LLONG_MIN) { + if(errno == ERANGE) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + /* correct file size */ + parser->file_data->filetype = CURLFILETYPE_FILE; + } + + parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; + parser->item_length = 0; + parser->state.NT.main = PL_WINNT_FILENAME; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; + } + break; + } + break; + case PL_WINNT_FILENAME: + switch (parser->state.NT.sub.filename) { + case PL_WINNT_FILENAME_PRESPACE: + if(c != ' ') { + parser->item_offset = finfo->b_used -1; + parser->item_length = 1; + parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; + } + break; + case PL_WINNT_FILENAME_CONTENT: + parser->item_length++; + if(c == '\r') { + parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; + finfo->b_data[finfo->b_used - 1] = 0; + } + else if(c == '\n') { + parser->offsets.filename = parser->item_offset; + finfo->b_data[finfo->b_used - 1] = 0; + parser->offsets.filename = parser->item_offset; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.NT.main = PL_WINNT_DATE; + parser->state.NT.sub.filename = 0; + } + break; + case PL_WINNT_FILENAME_WINEOL: + if(c == '\n') { + parser->offsets.filename = parser->item_offset; + rc = ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.NT.main = PL_WINNT_DATE; + parser->state.NT.sub.filename = 0; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + } + break; + default: + return bufflen+1; + break; + } + + i++; + } + + return bufflen; +} |