curl-library
[PATCH] WILDCARDMATCH/CHUNKING/FNMATCH added
From: none <pavel_at_pavel.(none)>
Date: Wed, 12 May 2010 15:33:22 +0200
Date: Wed, 12 May 2010 15:33:22 +0200
--- docs/libcurl/curl_easy_setopt.3 | 85 ++++ docs/libcurl/libcurl-errors.3 | 10 + include/curl/curl.h | 109 +++++ lib/Makefile.inc | 2 + lib/curl_fnmatch.c | 410 ++++++++++++++++ lib/curl_fnmatch.h | 44 ++ lib/fileinfo.c | 66 +++ lib/fileinfo.h | 33 ++ lib/ftp.c | 261 ++++++++++- lib/ftp.h | 13 + lib/ftplistparser.c | 1009 ++++++++++++++++++++++++++++++++= +++++++ lib/ftplistparser.h | 38 ++ lib/multi.c | 38 ++- lib/strerror.c | 6 + lib/transfer.c | 49 ++- lib/url.c | 24 + lib/urldata.h | 8 + lib/wildcard.c | 66 +++ lib/wildcard.h | 58 +++ tests/data/Makefile.am | 1 + tests/data/test1113 | 71 +++ tests/data/test1114 | 136 ++++++ tests/data/test146 | 2 +- tests/data/test149 | 2 +- tests/data/test539 | 2 +- tests/data/test574 | 71 +++ tests/data/test575 | 79 +++ tests/data/test576 | 192 ++++++++ tests/data/test577 | 38 ++ tests/directories.pm | 266 ++++++++++ tests/ftpserver.pl | 107 ++++- tests/libtest/Makefile.inc | 9 + tests/libtest/lib574.c | 56 +++ tests/libtest/lib575.c | 109 +++++ tests/libtest/lib576.c | 107 ++++ tests/libtest/lib577.c | 217 +++++++++ 36 files changed, 3778 insertions(+), 16 deletions(-) create mode 100644 lib/curl_fnmatch.c create mode 100644 lib/curl_fnmatch.h create mode 100644 lib/fileinfo.c create mode 100644 lib/fileinfo.h create mode 100644 lib/ftplistparser.c create mode 100644 lib/ftplistparser.h create mode 100644 lib/wildcard.c create mode 100644 lib/wildcard.h create mode 100644 tests/data/test1113 create mode 100644 tests/data/test1114 create mode 100644 tests/data/test574 create mode 100644 tests/data/test575 create mode 100644 tests/data/test576 create mode 100644 tests/data/test577 create mode 100644 tests/directories.pm create mode 100644 tests/libtest/lib574.c create mode 100644 tests/libtest/lib575.c create mode 100644 tests/libtest/lib576.c create mode 100644 tests/libtest/lib577.c diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_se= topt.3 index 88b216c..295eb74 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -84,6 +84,54 @@ If this option is set and libcurl has been built with= the standard name resolver, timeouts will not occur while the name resolve takes place. Consider building libcurl with c-ares support to enable asynchronous DN= S lookups, which enables nice timeouts for name resolves without signals.= +.IP CURLOPT_WILDCARDMATCH +Set this option to 1 if you want to transfer multiple files according t= o a +file name pattern. The pattern can be specified as part of the \fICURLO= PT_URL\fP +option, using an fnmatch-like pattern (Shell Pattern Matching) in the l= ast part +of URL (file name). + +By default, libcurl uses its internal implementation of fnmatch(). You = can +provide your own matching function by the \fICURLOPT_FNMATCH_FUNCTION\f= R option. + +This feature is only supported by the FTP download for now. + +A brief introduction of its syntax follows: +.RS +.IP "\fB*\fR - ASTERISK" +\&ftp://example.com/some/path/\fB*.txt\fR (for all txt's from the root +directory) +.RE +.RS +.IP "\fB?\fR - QUESTION MARK" +Question mark matches any (exactly one) character. + +\&ftp://example.com/some/path/\fBphoto?.jpeg\fR +.RE +.RS +.IP "\fB[\fR - BRACKET EXPRESSION" +The left bracket opens a bracket expression. The question mark and aste= risk have +no special meaning in a bracket expression. Each bracket expression end= s by the +right bracket and matches exactly one character. Some examples follow: + +\fB[a-zA-Z0\-9]\fR or \fB[f\-gF\-G]\fR \- character interval + +\fB[abc]\fR - character enumeration + +\fB[^abc]\fR or \fB[!abc]\fR - negation + +\fB[[:\fR\fIname\fR\fB:]]\fR class expression. Supported classes are +\fBalnum\fR,\fBlower\fR, \fBspace\fR, \fBalpha\fR, \fBdigit\fR, \fBprin= t\fR, +\fBupper\fR, \fBblank\fR, \fBgraph\fR, \fBxdigit\fR. + +\fB[][-!^]\fR - special case \- matches only '\-', ']', '[', '!' or '^'= . These +characters have no special purpose. + +\fB[\\[\\]\\\\]\fR - escape syntax. Matches '[', ']' or '\\'. + +Using the rules above, a file name pattern can be constructed: + +\&ftp://example.com/some/path/\fB[a-z[:upper:]\\\\].jpeg\fR +.RE .PP .SH CALLBACK OPTIONS .IP CURLOPT_WRITEFUNCTION @@ -424,6 +472,43 @@ in 7.20.0) .IP CURLOPT_INTERLEAVEDATA This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION= \fP when interleaved RTP data is received. (Added in 7.20.0) +.IP CURLOPT_CHUNK_BGN_FUNCTION +Function pointer that should match the following prototype: \fBlong fun= ction +(const void *transfer_info, void *ptr, int remains)\fR. This function +gets called by libcurl before a part of the stream is going to be trans= ferred +(if the transfer supports chunks). + +This callback makes sense only when using the \fICURLOPT_WILDCARDMATCH\= fR +option for now. + +The target of transfer_info parameter is a "feature depended" structure= . For the +FTP wildcard download, the target is curl_fileinfo structure (see +\fIcurl/curl.h\fR). +The parameter ptr is a pointer given by \fICURLOPT_CHUNK_DATA\fR. The p= arameter +remains contains number of chunks remaining per the transfer. If the fe= ature is +not available, the parameter has zero value. + +Return \fICURL_CHUNK_BGN_FUNC_OK\fR if everything is fine, +\fICURL_CHUNK_BGN_FUNC_SKIP\fR if you want to skip the concrete chunk o= r +\fICURL_CHUNK_BGN_FUNC_FAIL\fR to tell libcurl to stop if some error oc= curred. +.IP CURLOPT_CHUNK_END_FUNCTION +Function pointer that should match the following prototype: +\fBlong function(void *ptr)\fR. This function gets called by libcurl as= soon as +a part of the stream has been transferred (or skipped). + +Return \fICURL_CHUNK_END_FUNC_OK\fR if everything is fine or +\fBCURL_CHUNK_END_FUNC_FAIL\fR to tell the lib to stop if some error oc= curred. +.IP CURLOPT_CHUNK_DATA +Pass a pointer that will be untouched by libcurl and passed as the ptr = argument +to the \fICURL_CHUNK_BGN_FUNTION\fR and \fICURL_CHUNK_END_FUNTION\fR. +.IP CURLOPT_FNMATCH_FUNCTION +Function pointer that should match \fBint function(const char *pattern,= const +char *string)\fR prototype (see \fIcurl/curl.h\fR). It is used internal= ly for +the wildcard matching feature. + +Return \fICURL_FNMATCHFUNC_MATCH\fR if pattern matches the string, +\fICURL_FNMATCHFUNC_NOMATCH\fR if not or \fICURL_FNMATCHFUNC_FAIL\fR if= an error +occurred. .SH ERROR OPTIONS .IP CURLOPT_ERRORBUFFER Pass a char * to a buffer that the libcurl may store human readable err= or diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors= .3 index b4f2940..c3c854e 100644 --- a/docs/libcurl/libcurl-errors.3 +++ b/docs/libcurl/libcurl-errors.3 @@ -218,6 +218,16 @@ return code is only returned from \fIcurl_easy_recv= (3)\fP and Failed to load CRL file (Added in 7.19.0) .IP "CURLE_SSL_ISSUER_ERROR (83)" Issuer check failed (Added in 7.19.0) +.IP "CURLE_FTP_PRET_FAILED (84)" +PRET command failed +.IP "CURLE_RTSP_CSEQ_ERROR (85)" +Mismatch of RTSP CSeq numbers. +.IP "CURLE_RTSP_SESSION_ERROR (86)" +Mismatch of RTSP Session Identifiers. +.IP "CURLE_FTP_BAD_FILE_LIST (87)" +Unable to parse FTP file list (during FTP wildcard downloading). +.IP "CURLE_CHUNK_FAILED (88)" +Chunk callback reported error. .IP "CURLE_OBSOLETE*" These error codes will never be returned. They were used in an old libc= url version and are currently unused. diff --git a/include/curl/curl.h b/include/curl/curl.h index e635968..a89c171 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -198,6 +198,96 @@ typedef size_t (*curl_write_callback)(char *buffer,= size_t nitems, void *outstream); = + + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE =3D 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Content of this structure depends on information which is known and = is + achievable (e.g. by FTP LIST parsing). Please see the url_easy_setop= t(3) man + page for callbacks returning this structure -- some fields are manda= tory, + some others are optional. The FLAG field has special meaning. */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; + int32_t perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. = */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + int32_t flags; + + /* used internally */ + char * b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task *= / +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called be= fore + download of an individual chunk started. Note that parameter "remain= s" works + only for FTP wildcard downloading (for now), otherwise is not used *= / +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task *= / + +/* If splitting of data transfer is enabled this callback is called aft= er + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL = chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we don't need "transfer_info" parameter in th= is + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the patter= n */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string= */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc= . */ +typedef int (*curl_fnmatch_callback)(const char *pattern, + const char *string); + /* These are the return codes for the seek callbacks */ #define CURL_SEEKFUNC_OK 0 #define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ @@ -409,6 +499,8 @@ typedef enum { CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers = */ CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ident= ifiers */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list = */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error = */ = CURL_LAST /* never use! */ } CURLcode; @@ -1316,6 +1408,23 @@ typedef enum { /* Let the application define a custom write method for RTP data */ CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), = + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; = diff --git a/lib/Makefile.inc b/lib/Makefile.inc index e35e8bb..0a5e26e 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -4,6 +4,7 @@ CSOURCES =3D file.c timeval.c base64.c hostip.c progress= .c formdata.c \ cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \ ldap.c ssluse.c version.c getenv.c escape.c mprintf.c telnet.c \ netrc.c getinfo.c transfer.c strequal.c easy.c security.c krb4.c \ + curl_fnmatch.c fileinfo.c ftplistparser.c wildcard.c \ krb5.c memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c \ multi.c content_encoding.c share.c http_digest.c md5.c curl_rand.c \ http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \ @@ -18,6 +19,7 @@ HHEADERS =3D arpa_telnet.h netrc.h file.h timeval.h qs= sl.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ if2ip.h speedcheck.h urldata.h curl_ldap.h ssluse.h escape.h telnet.h= \ getinfo.h strequal.h krb4.h memdebug.h http_chunks.h curl_rand.h \ + curl_fnmatch.h wildcard.h fileinfo.h ftplistparser.h \ strtok.h connect.h llist.h hash.h content_encoding.h share.h \ curl_md5.h http_digest.h http_negotiate.h http_ntlm.h inet_pton.h \ strtoofft.h strerror.h inet_ntop.h curlx.h curl_memory.h setup.h \ diff --git a/lib/curl_fnmatch.c b/lib/curl_fnmatch.c new file mode 100644 index 0000000..a7fe6c9 --- /dev/null +++ b/lib/curl_fnmatch.c @@ -0,0 +1,410 @@ +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include "curl_fnmatch.h" +#include "setup.h" + +#define CURLFNM_CHARSET_LEN (sizeof(char) * 256) +#define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15) + +#define CURLFNM_NEGATE CURLFNM_CHARSET_LEN + +#define CURLFNM_ALNUM (CURLFNM_CHARSET_LEN + 1) +#define CURLFNM_DIGIT (CURLFNM_CHARSET_LEN + 2) +#define CURLFNM_XDIGIT (CURLFNM_CHARSET_LEN + 3) +#define CURLFNM_ALPHA (CURLFNM_CHARSET_LEN + 4) +#define CURLFNM_PRINT (CURLFNM_CHARSET_LEN + 5) +#define CURLFNM_BLANK (CURLFNM_CHARSET_LEN + 6) +#define CURLFNM_LOWER (CURLFNM_CHARSET_LEN + 7) +#define CURLFNM_GRAPH (CURLFNM_CHARSET_LEN + 8) +#define CURLFNM_SPACE (CURLFNM_CHARSET_LEN + 9) +#define CURLFNM_UPPER (CURLFNM_CHARSET_LEN + 10) + +typedef enum { + CURLFNM_LOOP_DEFAULT =3D 0, + CURLFNM_LOOP_BACKSLASH +} loop_state; + +typedef enum { + CURLFNM_SCHS_DEFAULT =3D 0, + CURLFNM_SCHS_MAYRANGE, + CURLFNM_SCHS_MAYRANGE2, + CURLFNM_SCHS_RIGHTBR, + CURLFNM_SCHS_RIGHTBRLEFTBR +} setcharset_state; + +typedef enum { + CURLFNM_PKW_INIT =3D 0, + CURLFNM_PKW_DDOT +} parsekey_state; + +#define SETCHARSET_OK 1 +#define SETCHARSET_FAIL 0 + +static int parsekeyword(unsigned char **pattern, unsigned char *charset= ) +{ + parsekey_state state =3D CURLFNM_PKW_INIT; +#define KEYLEN 10 + char keyword[KEYLEN] =3D { 0 }; + int found =3D FALSE; + int i; + register unsigned char *p =3D *pattern; + for(i =3D 0; !found; i++) { + char c =3D *p++; + if(i >=3D KEYLEN) + return SETCHARSET_FAIL; + switch(state) { + case CURLFNM_PKW_INIT: + if(ISALPHA(c) && ISLOWER(c)) + keyword[i] =3D c; + else if(c =3D=3D ':') + state =3D CURLFNM_PKW_DDOT; + else + return 0; + break; + case CURLFNM_PKW_DDOT: + if(c =3D=3D ']') + found =3D TRUE; + else + return SETCHARSET_FAIL; + } + } +#undef KEYLEN + + *pattern =3D p; /* move caller's pattern pointer */ + if(strcmp(keyword, "digit") =3D=3D 0) + charset[CURLFNM_DIGIT] =3D 1; + else if(strcmp(keyword, "alnum") =3D=3D 0) + charset[CURLFNM_ALNUM] =3D 1; + else if(strcmp(keyword, "alpha") =3D=3D 0) + charset[CURLFNM_ALPHA] =3D 1; + else if(strcmp(keyword, "xdigit") =3D=3D 0) + charset[CURLFNM_XDIGIT] =3D 1; + else if(strcmp(keyword, "print") =3D=3D 0) + charset[CURLFNM_PRINT] =3D 1; + else if(strcmp(keyword, "graph") =3D=3D 0) + charset[CURLFNM_GRAPH] =3D 1; + else if(strcmp(keyword, "space") =3D=3D 0) + charset[CURLFNM_SPACE] =3D 1; + else if(strcmp(keyword, "blank") =3D=3D 0) + charset[CURLFNM_BLANK] =3D 1; + else if(strcmp(keyword, "upper") =3D=3D 0) + charset[CURLFNM_UPPER] =3D 1; + else if(strcmp(keyword, "lower") =3D=3D 0) + charset[CURLFNM_LOWER] =3D 1; + else + return SETCHARSET_FAIL; + return SETCHARSET_OK; +} + +/* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern point= er) */ +static int setcharset(unsigned char **p, unsigned char *charset) +{ + setcharset_state state =3D CURLFNM_SCHS_DEFAULT; + unsigned char rangestart =3D 0; + unsigned char lastchar =3D 0; + bool something_found =3D FALSE; + register unsigned char c; + for(;;) { + c =3D **p; + switch(state){ + case CURLFNM_SCHS_DEFAULT: + if(ISALNUM(c)) { /* ASCII value */ + rangestart =3D c; + charset[c] =3D 1; + (*p)++; + state =3D CURLFNM_SCHS_MAYRANGE; + something_found =3D TRUE; + } + else if(c =3D=3D ']') { + if(something_found) + return SETCHARSET_OK; + else + something_found =3D TRUE; + state =3D CURLFNM_SCHS_RIGHTBR; + charset[c] =3D 1; + (*p)++; + } + else if(c =3D=3D '[') { + char c2 =3D *((*p)+1); + if(c2 =3D=3D ':') { /* there has to be a keyword */ + (*p) +=3D 2; + if(parsekeyword(p, charset)) { + state =3D CURLFNM_SCHS_DEFAULT; + } + else + return SETCHARSET_FAIL; + } + else { + charset[c] =3D 1; + (*p)++; + } + something_found =3D TRUE; + } + else if(c =3D=3D '?' || c =3D=3D '*') { + something_found =3D TRUE; + charset[c] =3D 1; + (*p)++; + } + else if(c =3D=3D '^' || c =3D=3D '!') { + if(!something_found) { + if(charset[CURLFNM_NEGATE]) { + charset[c] =3D 1; + something_found =3D 1; + } + else + charset[CURLFNM_NEGATE] =3D 1; /* negate charset */ + } + else + charset[c] =3D 1; + (*p)++; + } + else if(c =3D=3D '\\') { + c =3D *(++(*p)); + if(ISPRINT((c))) { + something_found =3D TRUE; + state =3D CURLFNM_SCHS_MAYRANGE; + charset[c] =3D 1; + rangestart =3D c; + (*p)++; + } + else + return SETCHARSET_FAIL; + } + else if(c =3D=3D '\0') { + return SETCHARSET_FAIL; + } + else { + charset[c] =3D 1; + (*p)++; + something_found =3D TRUE; + } + break; + case CURLFNM_SCHS_MAYRANGE: + if(c =3D=3D '-'){ + charset[c] =3D 1; + (*p)++; + lastchar =3D '-'; + state =3D CURLFNM_SCHS_MAYRANGE2; + } + else if(c =3D=3D '[') { + state =3D CURLFNM_SCHS_DEFAULT; + } + else if(ISALNUM(c)) { + charset[c] =3D 1; + (*p)++; + } + else if(c =3D=3D '\\') { + c =3D *(++(*p)); + if(isprint(c)) { + charset[c] =3D 1; + (*p)++; + } + else + return SETCHARSET_FAIL; + } + else if(c =3D=3D ']') { + return SETCHARSET_OK; + } + else + return SETCHARSET_FAIL; + break; + case CURLFNM_SCHS_MAYRANGE2: + if(c =3D=3D '\\') { + c =3D *(++(*p)); + if(!ISPRINT(c)) + return SETCHARSET_FAIL; + } + if(c =3D=3D ']') { + return SETCHARSET_OK; + } + else if(c =3D=3D '\\') { + c =3D *(++(*p)); + if(ISPRINT(c)) { + charset[c] =3D 1; + state =3D CURLFNM_SCHS_DEFAULT; + (*p)++; + } + else + return SETCHARSET_FAIL; + } + if(c >=3D rangestart) { + if((ISLOWER(c) && ISLOWER(rangestart)) || + (ISDIGIT(c) && ISDIGIT(rangestart)) || + (ISUPPER(c) && ISUPPER(rangestart))) { + charset[lastchar] =3D 0; + rangestart++; + while(rangestart++ <=3D c) + charset[rangestart-1] =3D 1; + (*p)++; + state =3D CURLFNM_SCHS_DEFAULT; + } + else + return SETCHARSET_FAIL; + } + break; + case CURLFNM_SCHS_RIGHTBR: + if(c =3D=3D '[') { + state =3D CURLFNM_SCHS_RIGHTBRLEFTBR; + charset[c] =3D 1; + (*p)++; + } + else if(c =3D=3D ']') { + return SETCHARSET_OK; + } + else if(c =3D=3D '\0') { + return SETCHARSET_FAIL; + } + else if(ISPRINT(c)){ + charset[c] =3D 1; + (*p)++; + state =3D CURLFNM_SCHS_DEFAULT; + } + else + return SETCHARSET_FAIL; + break; + case CURLFNM_SCHS_RIGHTBRLEFTBR: + if(c =3D=3D ']') { + return SETCHARSET_OK; + } + else { + state =3D CURLFNM_SCHS_DEFAULT; + charset[c] =3D 1; + (*p)++; + } + break; + } + } + return SETCHARSET_FAIL; +} + +static int loop(const unsigned char *pattern, const unsigned char *stri= ng) +{ + loop_state state =3D CURLFNM_LOOP_DEFAULT; + register unsigned char *p =3D (unsigned char *)pattern; + register unsigned char *s =3D (unsigned char *)string; + unsigned char charset[CURLFNM_CHSET_SIZE] =3D { 0 }; + int rc =3D 0; + + for (;;) { + switch(state) { + case CURLFNM_LOOP_DEFAULT: + if(*p =3D=3D '*') { + while(*(p+1) =3D=3D '*') /* eliminate multiple stars */ + p++; + if(*s =3D=3D '\0' && *(p+1) =3D=3D '\0') + return CURL_FNMATCH_MATCH; + rc =3D loop(p + 1, s); /* *.txt matches .txt <=3D> .txt matches= .txt */ + if(rc =3D=3D CURL_FNMATCH_MATCH) + return CURL_FNMATCH_MATCH; + if(*s) /* let the star eat up one character */ + s++; + else + return CURL_FNMATCH_NOMATCH; + } + else if(*p =3D=3D '?') { + if(ISPRINT(*s)) { + s++; + p++; + } + else if(*s =3D=3D '\0') + return CURL_FNMATCH_NOMATCH; + else + return CURL_FNMATCH_FAIL; /* cannot deal with other character= */ + } + else if(*p =3D=3D '\0') { + if(*s =3D=3D '\0') + return CURL_FNMATCH_MATCH; + else + return CURL_FNMATCH_NOMATCH; + } + else if(*p =3D=3D '\\') { + state =3D CURLFNM_LOOP_BACKSLASH; + p++; + } + else if(*p =3D=3D '[') { + unsigned char *pp =3D p+1; /* cannot handle with pointer to reg= ister */ + if(setcharset(&pp, charset)) { + bool found =3D FALSE; + if(charset[(unsigned int)*s]) + found =3D TRUE; + else if(charset[CURLFNM_ALNUM]) + found =3D ISALNUM(*s); + else if(charset[CURLFNM_ALPHA]) + found =3D ISALPHA(*s); + else if(charset[CURLFNM_DIGIT]) + found =3D ISDIGIT(*s); + else if(charset[CURLFNM_XDIGIT]) + found =3D ISXDIGIT(*s); + else if(charset[CURLFNM_PRINT]) + found =3D ISPRINT(*s); + else if(charset[CURLFNM_SPACE]) + found =3D ISSPACE(*s); + else if(charset[CURLFNM_UPPER]) + found =3D ISUPPER(*s); + else if(charset[CURLFNM_LOWER]) + found =3D ISLOWER(*s); + else if(charset[CURLFNM_BLANK]) + found =3D ISBLANK(*s); + else if(charset[CURLFNM_GRAPH]) + found =3D ISGRAPH(*s); + + if(charset[CURLFNM_NEGATE]) + found =3D !found; + + if(found) { + p =3D pp+1; + s++; + memset(charset, 0, CURLFNM_CHSET_SIZE); + } + else + return CURL_FNMATCH_NOMATCH; + } + else + return CURL_FNMATCH_FAIL; + } + else { + if(*p++ !=3D *s++) + return CURL_FNMATCH_NOMATCH; + } + break; + case CURLFNM_LOOP_BACKSLASH: + if(ISPRINT(*p)) { + if(*p++ =3D=3D *s++) + state =3D CURLFNM_LOOP_DEFAULT; + else + return CURL_FNMATCH_NOMATCH; + } + else + return CURL_FNMATCH_FAIL; + break; + } + } +} + +int Curl_fnmatch(const char *pattern, const char *string) +{ + if(!pattern || !string) { + return CURL_FNMATCH_FAIL; + } + return loop((unsigned char *)pattern, (unsigned char *)string); +} diff --git a/lib/curl_fnmatch.h b/lib/curl_fnmatch.h new file mode 100644 index 0000000..3ffbc45 --- /dev/null +++ b/lib/curl_fnmatch.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_FNMATCH_H +#define HEADER_CURL_FNMATCH_H +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#define CURL_FNMATCH_MATCH 0 +#define CURL_FNMATCH_NOMATCH 1 +#define CURL_FNMATCH_FAIL 2 + +/* default pattern matching function + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Implemented with recursive backtracking, if you want to use Curl_fnm= atch, + * please note that there is not implemented UTF/UNICODE support. + * + * Implemented features: + * '?' notation, does not match UTF characters + * '*' can also work with UTF string + * [a-zA-Z0-9] enumeration support + * + * keywords: alnum, digit, xdigit, alpha, print, blank, lower, graph, s= pace + * and upper (use as "[[:alnum:]]") + */ +int Curl_fnmatch(const char *pattern, const char *string); + +#endif /* HEADER_CURL_FNMATCH_H */ diff --git a/lib/fileinfo.c b/lib/fileinfo.c new file mode 100644 index 0000000..2a184f7 --- /dev/null +++ b/lib/fileinfo.c @@ -0,0 +1,66 @@ +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include <stdlib.h> +#include "strdup.h" +#include "fileinfo.h" + +struct curl_fileinfo *Curl_fileinfo_alloc(void) +{ + struct curl_fileinfo *tmp =3D malloc(sizeof(struct curl_fileinfo)); + if(!tmp) + return NULL; + memset(tmp, 0, sizeof(struct curl_fileinfo)); + return tmp; +} + +void Curl_fileinfo_dtor(void *user, void *element) +{ + struct curl_fileinfo *finfo =3D element; + (void) user; + if(!finfo) + return; + + if(finfo->b_data){ + free(finfo->b_data); + } + + free(finfo); +} + +struct curl_fileinfo *Curl_fileinfo_dup(const struct curl_fileinfo *src= ) +{ + struct curl_fileinfo *ptr =3D malloc(sizeof(struct curl_fileinfo)); + if(!ptr) + return NULL; + *ptr =3D *src; + + ptr->b_data =3D malloc(src->b_size); + if(!ptr->b_data) { + free(ptr); + return NULL; + } + else { + memcpy(ptr->b_data, src->b_data, src->b_size); + return ptr; + } +} diff --git a/lib/fileinfo.h b/lib/fileinfo.h new file mode 100644 index 0000000..b040ef4 --- /dev/null +++ b/lib/fileinfo.h @@ -0,0 +1,33 @@ +#ifndef __FILEINFO_H +#define __FILEINFO_H +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include <curl/curl.h> + +struct curl_fileinfo *Curl_fileinfo_alloc(void); + +void Curl_fileinfo_dtor(void *, void *); + +struct curl_fileinfo *Curl_fileinfo_dup(const struct curl_fileinfo *src= ); + +#endif /* __FILEINFO_H */ diff --git a/lib/ftp.c b/lib/ftp.c index 95e2a12..afd15b4 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -71,6 +71,8 @@ #include "http.h" /* for HTTP proxy tunnel stuff */ #include "socks.h" #include "ftp.h" +#include "fileinfo.h" +#include "ftplistparser.h" = #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) #include "krb4.h" @@ -110,6 +112,7 @@ #define ftp_pasv_verbose(a,b,c,d) do { } while(0) #endif = +void Curl_ftp_wc_data_dtor(void *ptr); /* Local API functions */ static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote); @@ -144,6 +147,12 @@ static CURLcode ftp_doing(struct connectdata *conn,= bool *dophase_done); static CURLcode ftp_setup_connection(struct connectdata * conn); = +static CURLcode init_wc_data(struct connectdata *conn); +static CURLcode wc_statemach(struct connectdata *conn); + +static CURLcode ftp_state_post_retr_size(struct connectdata *conn, + curl_off_t filesize); + /* easy-to-use macro: */ #define FTPSENDF(x,y,z) if((result =3D Curl_ftpsendf(x,y,z)) !=3D CU= RLE_OK) \ return result @@ -1469,8 +1478,14 @@ static CURLcode ftp_state_quote(struct connectdat= a *conn, if(ftp->transfer !=3D FTPTRANSFER_BODY) state(conn, FTP_STOP); else { - PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file); - state(conn, FTP_RETR_SIZE); + if(ftpc->known_filesize !=3D -1) { + Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); + result =3D ftp_state_post_retr_size(conn, ftpc->known_filesiz= e); + } + else { + PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file); + state(conn, FTP_RETR_SIZE); + } } break; case FTP_STOR_PREQUOTE: @@ -2855,6 +2870,8 @@ static CURLcode ftp_init(struct connectdata *conn)= if(TRUE =3D=3D isBadFtpString(ftp->passwd)) return CURLE_URL_MALFORMAT; = + conn->proto.ftpc.known_filesize =3D -1; /* unknown size for now */ + return CURLE_OK; } = @@ -3018,6 +3035,13 @@ static CURLcode ftp_done(struct connectdata *conn= , CURLcode status, if(ftpc->prevpath) free(ftpc->prevpath); = + if(data->set.wildcardmatch) { + if(data->set.chunk_end && ftpc->file) { + data->set.chunk_end(data->wildcard.customptr); + } + ftpc->known_filesize =3D -1; + } + /* get the "raw" path */ path =3D curl_easy_unescape(data, path_to_use, 0, NULL); if(!path) { @@ -3445,6 +3469,221 @@ CURLcode ftp_perform(struct connectdata *conn, return result; } = +void Curl_ftp_wc_data_dtor(void *ptr) +{ + struct ftp_wc_tmpdata *tmp =3D ptr; + if(tmp) + ftp_parselist_data_free(&tmp->parser); + Curl_safefree(tmp); +} + +static CURLcode init_wc_data(struct connectdata *conn) +{ + char *last_slash; + char *path =3D conn->data->state.path; + struct WildcardData *wildcard =3D &(conn->data->wildcard); + CURLcode ret =3D CURLE_OK; + struct ftp_wc_tmpdata *ftp_tmp; + + last_slash =3D strrchr(conn->data->state.path, '/'); + if(last_slash) { + last_slash++; + if(last_slash[0] =3D=3D '\0') { + wildcard->state =3D CURLWC_CLEAN; + ret =3D ftp_parse_url_path(conn); + return ret; + } + else { + wildcard->pattern =3D strdup(last_slash); + if (!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + last_slash[0] =3D '\0'; /* cut file from path */ + } + } + else { /* there is only 'wildcard pattern' or nothing */ + if(path[0]) { + wildcard->pattern =3D strdup(path); + if (!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + path[0] =3D '\0'; + } + else { /* only list */ + conn->data->set.wildcardmatch =3D 0L; + ret =3D ftp_parse_url_path(conn); + return ret; + } + } + + /* program continues only if URL is not ending with slash, allocate n= eeded + resources for wildcard transfer */ + + /* allocate ftp protocol specific temporary wildcard data */ + ftp_tmp =3D malloc(sizeof(struct ftp_wc_tmpdata)); + if(!ftp_tmp) { + return CURLE_OUT_OF_MEMORY; + } + + /* INITIALIZE parselist structure */ + ftp_tmp->parser =3D ftp_parselist_data_alloc(); + if(!ftp_tmp->parser) + return CURLE_OUT_OF_MEMORY; + + wildcard->tmp =3D ftp_tmp; /* put it to the WildcardData tmp pointer = */ + wildcard->tmp_dtor =3D Curl_ftp_wc_data_dtor; + + /* wildcard does not support NOCWD option (assert it?) */ + if(conn->data->set.ftp_filemethod =3D=3D FTPFILE_NOCWD) + conn->data->set.ftp_filemethod =3D FTPFILE_MULTICWD; + + /* try to parse ftp url */ + ret =3D ftp_parse_url_path(conn); + if(ret) { + return ret; + } + + /* backup old write_function */ + ftp_tmp->backup.write_function =3D conn->data->set.fwrite_func; + /* parsing write function (callback included directly from ftplistpar= ser.c) */ + conn->data->set.fwrite_func =3D ftp_parselist; + /* backup old file descriptor */ + ftp_tmp->backup.file_descriptor =3D conn->data->set.out; + /* let the writefunc callback know what curl pointer is working with = */ + conn->data->set.out =3D conn; + + wildcard->path =3D strdup(conn->data->state.path); + if(!wildcard->path) { + return CURLE_OUT_OF_MEMORY; + } + + infof(conn->data, "Wildcard - Parsing started\n"); + return CURLE_OK; +} + +static CURLcode wc_statemach(struct connectdata *conn) +{ + struct ftp_conn *ftpc =3D &conn->proto.ftpc; + struct WildcardData *wildcard =3D &(conn->data->wildcard); + struct ftp_wc_tmpdata *ftp_tmp =3D wildcard->tmp; + char *tmp_path; + CURLcode ret =3D CURLE_OK; + long userresponse =3D 0; + switch (wildcard->state) { + case CURLWC_INIT: + ret =3D init_wc_data(conn); + if(wildcard->state =3D=3D CURLWC_CLEAN) + /* only listing! */ + break; + else + wildcard->state =3D ret ? CURLWC_ERROR : CURLWC_MATCHING; + break; + + case CURLWC_MATCHING: + /* In this state is LIST response successfully parsed, so lets rest= ore + previous WRITEFUNCTION callback and WRITEDATA pointer */ + ftp_tmp =3D wildcard->tmp; + conn->data->set.fwrite_func =3D ftp_tmp->backup.write_function; + conn->data->set.out =3D ftp_tmp->backup.file_descriptor; + wildcard->state =3D CURLWC_DOWNLOADING; + + if(ftp_parselist_geterror(ftp_tmp->parser)) { + /* error found in LIST parsing */ + wildcard->state =3D CURLWC_CLEAN; + return wc_statemach(conn); + } + else if(wildcard->filelist->size =3D=3D 0) { + /* no corresponding file */ + wildcard->state =3D CURLWC_CLEAN; + return CURLE_REMOTE_FILE_NOT_FOUND; + } + ret =3D wc_statemach(conn); + break; + + case CURLWC_DOWNLOADING: { + /* filelist has at least one file, lets get first one */ + struct curl_fileinfo *finfo =3D wildcard->filelist->head->ptr; + tmp_path =3D malloc(strlen(conn->data->state.path) + + strlen(finfo->filename) + 1); + if(!tmp_path) { + return CURLE_OUT_OF_MEMORY; + } + + tmp_path[0] =3D 0; + /* make full path to matched file */ + strcat(tmp_path, wildcard->path); + strcat(tmp_path, finfo->filename); + /* switch default "state.pathbuffer" and tmp_path, good to see + ftp_parse_url_path function to understand this trick */ + if(conn->data->state.pathbuffer) + free(conn->data->state.pathbuffer); + conn->data->state.pathbuffer =3D tmp_path; + conn->data->state.path =3D tmp_path; + + infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);= + if(conn->data->set.chunk_bgn) { + userresponse =3D conn->data->set.chunk_bgn( + finfo, wildcard->customptr, (int)wildcard->filelist->size); + switch(userresponse) { + case CURL_CHUNK_BGN_FUNC_SKIP: + infof(conn->data, "Wildcard - \"%s\" skipped by user\n", + finfo->filename); + wildcard->state =3D CURLWC_SKIP; + return wc_statemach(conn); + break; + case CURL_CHUNK_BGN_FUNC_FAIL: + return CURLE_CHUNK_FAILED; + break; + } + } + + if(finfo->filetype !=3D CURLFILETYPE_FILE) { + wildcard->state =3D CURLWC_SKIP; + return wc_statemach(conn); + } + + if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) + ftpc->known_filesize =3D finfo->size; + + ret =3D ftp_parse_url_path(conn); + if(ret) { + return ret; + } + + /* we don't need the Curl_fileinfo of first file anymore */ + Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NUL= L); + + if(wildcard->filelist->size =3D=3D 0) { /* remains only one file to= down. */ + wildcard->state =3D CURLWC_CLEAN; + /* after that will be ftp_do called once again and no transfer + will be done because of CURLWC_CLEAN state */ + return CURLE_OK; + } + } break; + + case CURLWC_SKIP: { + if(conn->data->set.chunk_end) + conn->data->set.chunk_end(conn->data->wildcard.customptr); + Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NUL= L); + wildcard->state =3D (wildcard->filelist->size =3D=3D 0) ? + CURLWC_CLEAN : CURLWC_DOWNLOADING; + ret =3D wc_statemach(conn); + } break; + + case CURLWC_CLEAN: + ret =3D CURLE_OK; + if(ftp_tmp) { + ret =3D ftp_parselist_geterror(ftp_tmp->parser); + } + wildcard->state =3D ret ? CURLWC_ERROR : CURLWC_DONE; + break; + + case CURLWC_DONE: + case CURLWC_ERROR: + break; + } + + return ret; +} + /**********************************************************************= * * * ftp_do() @@ -3471,9 +3710,21 @@ static CURLcode ftp_do(struct connectdata *conn, = bool *done) if(retcode) return retcode; = - retcode =3D ftp_parse_url_path(conn); - if(retcode) - return retcode; + if(conn->data->set.wildcardmatch) { + retcode =3D wc_statemach(conn); + if(conn->data->wildcard.state =3D=3D CURLWC_SKIP || + conn->data->wildcard.state =3D=3D CURLWC_DONE) { + /* do not call ftp_regular_transfer */ + return CURLE_OK; + } + if(retcode) /* error, loop or skipping the file */ + return retcode; + } + else { /* no wildcard FSM needed */ + retcode =3D ftp_parse_url_path(conn); + if(retcode) + return retcode; + } = retcode =3D ftp_regular_transfer(conn, done); = diff --git a/lib/ftp.h b/lib/ftp.h index 7a4f89e..d8ef348 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -79,6 +79,17 @@ typedef enum { FTP_LAST /* never used */ } ftpstate; = +struct ftp_parselist_data; /* defined later in ftplistparser.c */ + +struct ftp_wc_tmpdata { + struct ftp_parselist_data *parser; + + struct { + curl_write_callback write_function; + FILE *file_descriptor; + } backup; +}; + typedef enum { FTPFILE_MULTICWD =3D 1, /* as defined by RFC1738 */ FTPFILE_NOCWD =3D 2, /* use SIZE / RETR / STOR on the full path *= / @@ -135,6 +146,8 @@ struct ftp_conn { int count3; /* general purpose counter for the state machine */ ftpstate state; /* always use ftp.c:state() to change state! */ char * server_os; /* The target server operating system. */ + curl_off_t known_filesize; /* file size is different from -1, if wild= card + LIST parsing was done and wc_statemach = set it */ }; = #endif /* HEADER_CURL_FTP_H */ diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c new file mode 100644 index 0000000..9d42e8f --- /dev/null +++ b/lib/ftplistparser.c @@ -0,0 +1,1009 @@ +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel_at_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 O= F 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 =3D 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 =3D 0, + PL_UNIX_HLINKS_NUMBER + } hlinks; + + enum { + PL_UNIX_USER_PRESPACE =3D 0, + PL_UNIX_USER_PARSING + } user; + + enum { + PL_UNIX_GROUP_PRESPACE =3D 0, + PL_UNIX_GROUP_NAME + } group; + + enum { + PL_UNIX_SIZE_PRESPACE =3D 0, + PL_UNIX_SIZE_NUMBER + } size; + + enum { + PL_UNIX_TIME_PREPART1 =3D 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 =3D 0, + PL_UNIX_FILENAME_NAME, + PL_UNIX_FILENAME_WINDOWSEOL + } filename; + + enum { + PL_UNIX_SYMLINK_PRESPACE =3D 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 =3D 0, + PL_WINNT_TIME, + PL_WINNT_DIRORSIZE, + PL_WINNT_FILENAME +} pl_winNT_mainstate; + +typedef union { + enum { + PL_WINNT_TIME_PRESPACE =3D 0, + PL_WINNT_TIME_TIME + } time; + enum { + PL_WINNT_DIRORSIZE_PRESPACE =3D 0, + PL_WINNT_DIRORSIZE_CONTENT + } dirorsize; + enum { + PL_WINNT_FILENAME_PRESPACE =3D 0, + PL_WINNT_FILENAME_CONTENT, + PL_WINNT_FILENAME_WINEOL + } filename; +} pl_winNT_substate; + +/* This struct is used in wildcard downloading - for parsing LIST respo= nse */ +struct ftp_parselist_data { + enum { + OS_TYPE_UNKNOWN =3D 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 =3D + 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 =3D 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 =3D 0; + /* USER */ + if(str[0] =3D=3D 'r') + permissions |=3D 1 << 8; + else if(str[0] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + if(str[1] =3D=3D 'w') + permissions |=3D 1 << 7; + else if(str[1] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + + if(str[2] =3D=3D 'x') + permissions |=3D 1 << 6; + else if(str[2] =3D=3D 's') { + permissions |=3D 1 << 6; + permissions |=3D 1 << 11; + } + else if(str[2] =3D=3D 'S') + permissions |=3D 1 << 11; + else if(str[2] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + /* GROUP */ + if(str[3] =3D=3D 'r') + permissions |=3D 1 << 5; + else if(str[3] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + if(str[4] =3D=3D 'w') + permissions |=3D 1 << 4; + else if(str[4] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + if(str[5] =3D=3D 'x') + permissions |=3D 1 << 3; + else if(str[5] =3D=3D 's') { + permissions |=3D 1 << 3; + permissions |=3D 1 << 10; + } + else if(str[5] =3D=3D 'S') + permissions |=3D 1 << 10; + else if(str[5] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + /* others */ + if(str[6] =3D=3D 'r') + permissions |=3D 1 << 2; + else if(str[6] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + if(str[7] =3D=3D 'w') + permissions |=3D 1 << 1; + else if(str[7] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + if(str[8] =3D=3D 'x') + permissions |=3D 1; + else if(str[8] =3D=3D 't') { + permissions |=3D 1; + permissions |=3D 1 << 9; + } + else if(str[8] =3D=3D 'T') + permissions |=3D 1 << 9; + else if(str[8] !=3D '-') + permissions |=3D FTP_LP_MALFORMATED_PERM; + + return permissions; +} + +static void PL_ERROR(struct connectdata *conn, CURLcode err) +{ + struct ftp_wc_tmpdata *tmpdata =3D conn->data->wildcard.tmp; + struct ftp_parselist_data *parser =3D tmpdata->parser; + if(parser->file_data) + Curl_fileinfo_dtor(NULL, parser->file_data); + parser->file_data =3D NULL; + parser->error =3D err; +} + +static bool ftp_pl_gettime(struct ftp_parselist_data *parser, char *str= ing) +{ + (void)parser; + (void)string; + /* TODO + * There could be possible parse timestamp from server. Leaving unimp= lemented + * for now. + * If you want implement this, please add CURLFINFOFLAG_KNOWN_TIME fl= ag 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 =3D &conn->data->wildcard; + struct ftp_wc_tmpdata *tmpdata =3D wc->tmp; + struct curl_llist *llist =3D wc->filelist; + struct ftp_parselist_data *parser =3D tmpdata->parser; + bool add =3D TRUE; + + /* move finfo pointers to b_data */ + char *str =3D finfo->b_data; + finfo->filename =3D str + parser->offsets.filename; + finfo->strings.group =3D parser->offsets.group ? + str + parser->offsets.group : NULL; + finfo->strings.perm =3D parser->offsets.perm ? + str + parser->offsets.perm : NULL; + finfo->strings.target =3D parser->offsets.symlink_target ? + str + parser->offsets.symlink_target : NULL; + finfo->strings.time =3D str + parser->offsets.time; + finfo->strings.user =3D parser->offsets.user ? + str + parser->offsets.user : NULL; + + /* get correct fnmatch callback */ + compare =3D conn->data->set.fnmatch; + if(!compare) + compare =3D Curl_fnmatch; + + /* filter pattern-corresponding filenames */ + if(compare(wc->pattern, finfo->filename) =3D=3D 0) { + /* discard symlink which is containing multiple " -> " */ + if((finfo->filetype =3D=3D CURLFILETYPE_SYMLINK) && + (strstr(finfo->strings.target, " -> "))) { + add =3D FALSE; + } + } + else { + add =3D FALSE; + } + + if(add) { + if(!Curl_llist_insert_next(llist, llist->tail, finfo)) { + Curl_fileinfo_dtor(NULL, finfo); + tmpdata->parser->file_data =3D NULL; + return CURLE_OUT_OF_MEMORY; + } + } + else { + Curl_fileinfo_dtor(NULL, finfo); + } + + tmpdata->parser->file_data =3D NULL; + return CURLE_OK; +} + +size_t ftp_parselist(char *buffer, size_t size, size_t nmemb, void *con= nptr) +{ + size_t bufflen =3D size*nmemb; + struct connectdata *conn =3D (struct connectdata *)connptr; + struct ftp_wc_tmpdata *tmpdata =3D conn->data->wildcard.tmp; + struct ftp_parselist_data *parser =3D tmpdata->parser; + struct curl_fileinfo *finfo; + unsigned long i =3D 0; + CURLcode rc; + + if(parser->error) { /* error in previous call */ + /* scenario: + * 1. call =3D> OK.. + * 2. call =3D> OUT_OF_MEMORY (or other error) + * 3. (last) call =3D> is skipped RIGHT HERE and the error is hadle= d later + * in wc_statemach() + */ + return bufflen; + } + + if(parser->os_type =3D=3D OS_TYPE_UNKNOWN && bufflen > 0) { + /* considering info about FILE response format */ + parser->os_type =3D (buffer[0] >=3D '0' && buffer[0] <=3D '9') ? + OS_TYPE_WIN_NT : OS_TYPE_UNIX; + } + + while(i < bufflen) { /* FSM */ + + char c =3D buffer[i]; + if(!parser->file_data) { /* tmp file data is not allocated yet */ + parser->file_data =3D Curl_fileinfo_alloc(); + if(!parser->file_data) { + parser->error =3D CURLE_OUT_OF_MEMORY; + return bufflen; + } + parser->file_data->b_data =3D malloc(FTP_BUFFER_ALLOCSIZE); + if(!parser->file_data->b_data) { + PL_ERROR(conn, CURLE_OUT_OF_MEMORY); + return bufflen; + } + parser->file_data->b_size =3D FTP_BUFFER_ALLOCSIZE; + parser->item_offset =3D 0; + parser->item_length =3D 0; + } + + finfo =3D parser->file_data; + finfo->b_data[finfo->b_used++] =3D buffer[i]; + + if(finfo->b_used >=3D finfo->b_size - 1) { + /* if it is important, extend buffer space for file data */ + char *tmp =3D realloc(finfo->b_data, + finfo->b_size + FTP_BUFFER_ALLOCSIZE); + if(tmp) { + finfo->b_size +=3D FTP_BUFFER_ALLOCSIZE; + finfo->b_data =3D tmp; + } + else { + Curl_fileinfo_dtor(NULL, parser->file_data); + parser->file_data =3D NULL; + } + } + + switch (parser->os_type) { + case OS_TYPE_UNIX: + switch (parser->state.UNIX.main) { + case PL_UNIX_FILETYPE: + switch (c) { + case '-': + finfo->filetype =3D CURLFILETYPE_FILE; + break; + case 'd': + finfo->filetype =3D CURLFILETYPE_DIRECTORY; + break; + case 'l': + finfo->filetype =3D CURLFILETYPE_SYMLINK; + break; + case 'p': + finfo->filetype =3D CURLFILETYPE_NAMEDPIPE; + break; + case 's': + finfo->filetype =3D CURLFILETYPE_SOCKET; + break; + case 'c': + finfo->filetype =3D CURLFILETYPE_DEVICE_CHAR; + break; + case 'b': + finfo->filetype =3D CURLFILETYPE_DEVICE_BLOCK; + break; + case 'D': + finfo->filetype =3D CURLFILETYPE_DOOR; + break; + default: + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + break; + } + parser->state.UNIX.main =3D PL_UNIX_PERMISSION; + parser->item_length =3D 0; + parser->item_offset =3D 1; + break; + case PL_UNIX_PERMISSION: + parser->item_length++; + if(parser->item_length <=3D 9) { + if(!strchr("rwx-tTsS", c)) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + } + else if(parser->item_length =3D=3D 10) { + int32_t perm; + if(c !=3D ' ') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + finfo->b_data[10] =3D 0; /* terminate permissions */ + perm =3D ftp_pl_get_permission(finfo->b_data + parser->item_o= ffset); + if(perm & FTP_LP_MALFORMATED_PERM) { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + parser->file_data->flags |=3D CURLFINFOFLAG_KNOWN_PERM; + parser->file_data->perm =3D perm; + parser->offsets.perm =3D parser->item_offset; + + parser->item_length =3D 0; + parser->state.UNIX.main =3D PL_UNIX_HLINKS; + parser->state.UNIX.sub.hlinks =3D PL_UNIX_HLINKS_PRESPACE; + } + break; + case PL_UNIX_HLINKS: + switch(parser->state.UNIX.sub.hlinks) { + case PL_UNIX_HLINKS_PRESPACE: + if(c !=3D ' ') { + if(c >=3D '0' && c <=3D '9') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.hlinks =3D 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 =3D=3D ' ') { + char *p; + long int hlinks; + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + hlinks =3D strtol(finfo->b_data + parser->item_offset, &p, = 10); + if(p[0] =3D=3D '\0' && hlinks !=3D LONG_MAX && hlinks !=3D = LONG_MIN) { + parser->file_data->flags |=3D CURLFINFOFLAG_KNOWN_HLINKCO= UNT; + parser->file_data->hardlinks =3D hlinks; + } + parser->item_length =3D 0; + parser->item_offset =3D 0; + parser->state.UNIX.main =3D PL_UNIX_USER; + parser->state.UNIX.sub.user =3D 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 !=3D ' ') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.user =3D PL_UNIX_USER_PARSING; + } + break; + case PL_UNIX_USER_PARSING: + parser->item_length++; + if(c =3D=3D ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + parser->offsets.user =3D parser->item_offset; + parser->state.UNIX.main =3D PL_UNIX_GROUP; + parser->state.UNIX.sub.group =3D PL_UNIX_GROUP_PRESPACE; + parser->item_offset =3D 0; + parser->item_length =3D 0; + } + break; + } + break; + case PL_UNIX_GROUP: + switch(parser->state.UNIX.sub.group) { + case PL_UNIX_GROUP_PRESPACE: + if(c !=3D ' ') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.group =3D PL_UNIX_GROUP_NAME; + } + break; + case PL_UNIX_GROUP_NAME: + parser->item_length++; + if(c =3D=3D ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + parser->offsets.group =3D parser->item_offset; + parser->state.UNIX.main =3D PL_UNIX_SIZE; + parser->state.UNIX.sub.group =3D PL_UNIX_SIZE_PRESPACE; + parser->item_offset =3D 0; + parser->item_length =3D 0; + } + break; + } + break; + case PL_UNIX_SIZE: + switch(parser->state.UNIX.sub.size) { + case PL_UNIX_SIZE_PRESPACE: + if(c !=3D ' ') { + if(c >=3D '0' && c <=3D '9') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.size =3D 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 =3D=3D ' ') { + char *p; + curl_off_t fsize; + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + fsize =3D curlx_strtoofft(finfo->b_data+parser->item_offset= , &p, 10); + if(p[0] =3D=3D '\0' && fsize !=3D CURL_LLONG_MAX && + fsize !=3D CURL_LLONG_MIN) { + parser->file_data->flags |=3D CURLFINFOFLAG_KNOWN_SIZE; + parser->file_data->size =3D fsize; + } + parser->item_length =3D 0; + parser->item_offset =3D 0; + parser->state.UNIX.main =3D PL_UNIX_TIME; + parser->state.UNIX.sub.time =3D 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 !=3D ' ') { + if(ISALNUM(c)) { + parser->item_offset =3D finfo->b_used -1; + parser->item_length =3D 1; + parser->state.UNIX.sub.time =3D 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 =3D=3D ' ') { + parser->state.UNIX.sub.size =3D PL_UNIX_TIME_PREPART2; + } + else if(!ISALNUM(c) && c !=3D '.') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_TIME_PREPART2: + parser->item_length++; + if(c !=3D ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time =3D 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 =3D=3D ' ') { + parser->state.UNIX.sub.size =3D PL_UNIX_TIME_PREPART3; + } + else if(!ISALNUM(c) && c !=3D '.') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_TIME_PREPART3: + parser->item_length++; + if(c !=3D ' ') { + if(ISALNUM(c)) { + parser->state.UNIX.sub.time =3D 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 =3D=3D ' ') { + finfo->b_data[parser->item_offset + parser->item_length -1]= =3D 0; + parser->offsets.time =3D parser->item_offset; + if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offs= et)) { + parser->file_data->flags |=3D CURLFINFOFLAG_KNOWN_TIME; + } + if(finfo->filetype =3D=3D CURLFILETYPE_SYMLINK) { + parser->state.UNIX.main =3D PL_UNIX_SYMLINK; + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_PRESPA= CE; + } + else { + parser->state.UNIX.main =3D PL_UNIX_FILENAME; + parser->state.UNIX.sub.filename =3D PL_UNIX_FILENAME_PRES= PACE; + } + } + else if(!ISALNUM(c) && c !=3D '.' && c !=3D ':') { + 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 !=3D ' ') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.filename =3D PL_UNIX_FILENAME_NAME; + } + break; + case PL_UNIX_FILENAME_NAME: + parser->item_length++; + if(c =3D=3D '\r') { + parser->item_length--; + parser->state.UNIX.sub.filename =3D PL_UNIX_FILENAME_WINDOW= SEOL; + } + else if(c =3D=3D '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + parser->offsets.filename =3D parser->item_offset; + parser->state.UNIX.main =3D PL_UNIX_FILETYPE; + rc =3D ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + } + break; + case PL_UNIX_FILENAME_WINDOWSEOL: + if(c =3D=3D '\n') { + finfo->b_data[parser->item_offset + parser->item_length] =3D= 0; + parser->offsets.filename =3D parser->item_offset; + parser->state.UNIX.main =3D PL_UNIX_FILETYPE; + rc =3D 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 !=3D ' ') { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_NAME: + parser->item_length++; + if(c =3D=3D ' ') { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_PRETARGE= T1; + } + else if(c =3D=3D '\r' || c =3D=3D '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_SYMLINK_PRETARGET1: + parser->item_length++; + if(c =3D=3D '-') { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_PRETARGE= T2; + } + else if(c =3D=3D '\r' || c =3D=3D '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET2: + parser->item_length++; + if(c =3D=3D '>') { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_PRETARGE= T3; + } + else if(c =3D=3D '\r' || c =3D=3D '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET3: + parser->item_length++; + if(c =3D=3D ' ') { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_PRETARGE= T4; + /* now place where is symlink following */ + finfo->b_data[parser->item_offset + parser->item_length - 4= ] =3D 0; + parser->offsets.filename =3D parser->item_offset; + parser->item_length =3D 0; + parser->item_offset =3D 0; + } + else if(c =3D=3D '\r' || c =3D=3D '\n') { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + else { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_PRETARGET4: + if(c !=3D '\r' && c !=3D '\n') { + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_TARGET; + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + case PL_UNIX_SYMLINK_TARGET: + parser->item_length ++; + if(c =3D=3D '\r') { + parser->item_length --; + parser->state.UNIX.sub.symlink =3D PL_UNIX_SYMLINK_WINDOWSE= OL; + } + else if(c =3D=3D '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + parser->offsets.symlink_target =3D parser->item_offset; + rc =3D ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.UNIX.main =3D PL_UNIX_FILETYPE; + } + break; + case PL_UNIX_SYMLINK_WINDOWSEOL: + if(c =3D=3D '\n') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + parser->offsets.symlink_target =3D parser->item_offset; + rc =3D ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.UNIX.main =3D 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 =3D=3D 9) { + if(c =3D=3D ' ') { + parser->state.NT.main =3D PL_WINNT_TIME; + parser->state.NT.sub.time =3D 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 =3D PL_WINNT_TIME_TIME; + } + break; + case PL_WINNT_TIME_TIME: + if(c =3D=3D ' ') { + parser->offsets.time =3D parser->item_offset; + finfo->b_data[parser->item_offset + parser->item_length -1]= =3D 0; + parser->state.NT.main =3D PL_WINNT_DIRORSIZE; + parser->state.NT.sub.dirorsize =3D PL_WINNT_DIRORSIZE_PRESP= ACE; + parser->item_length =3D 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 =3D=3D ' ') { + + } + else { + parser->item_offset =3D finfo->b_used - 1; + parser->item_length =3D 1; + parser->state.NT.sub.dirorsize =3D PL_WINNT_DIRORSIZE_CONTE= NT; + } + break; + case PL_WINNT_DIRORSIZE_CONTENT: + parser->item_length ++; + if(c =3D=3D ' ') { + finfo->b_data[parser->item_offset + parser->item_length - 1= ] =3D 0; + if(strcmp("<DIR>", finfo->b_data + parser->item_offset) =3D= =3D 0) { + finfo->filetype =3D CURLFILETYPE_DIRECTORY; + finfo->size =3D 0; + } + else { + char *endptr; + finfo->size =3D curlx_strtoofft(finfo->b_data + parser->i= tem_offset, + &endptr, 10); + if(!*endptr) { + if(finfo->size < CURL_LLONG_MAX && + finfo->size > CURL_LLONG_MIN) { + + } + else if(finfo->size =3D=3D CURL_LLONG_MAX || + finfo->size =3D=3D CURL_LLONG_MIN) { + if(errno =3D=3D 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 =3D CURLFILETYPE_FILE; + } + + parser->file_data->flags |=3D CURLFINFOFLAG_KNOWN_SIZE; + parser->item_length =3D 0; + parser->state.NT.main =3D PL_WINNT_FILENAME; + parser->state.NT.sub.filename =3D PL_WINNT_FILENAME_PRESPAC= E; + } + break; + } + break; + case PL_WINNT_FILENAME: + switch (parser->state.NT.sub.filename) { + case PL_WINNT_FILENAME_PRESPACE: + if(c !=3D ' ') { + parser->item_offset =3D finfo->b_used -1; + parser->item_length =3D 1; + parser->state.NT.sub.filename =3D PL_WINNT_FILENAME_CONTENT= ; + } + break; + case PL_WINNT_FILENAME_CONTENT: + parser->item_length++; + if(c =3D=3D '\r') { + parser->state.NT.sub.filename =3D PL_WINNT_FILENAME_WINEOL;= + finfo->b_data[finfo->b_used - 1] =3D 0; + } + else if(c =3D=3D '\n') { + parser->offsets.filename =3D parser->item_offset; + finfo->b_data[finfo->b_used - 1] =3D 0; + parser->offsets.filename =3D parser->item_offset; + rc =3D ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.NT.main =3D PL_WINNT_DATE; + parser->state.NT.sub.filename =3D 0; + } + break; + case PL_WINNT_FILENAME_WINEOL: + if(c =3D=3D '\n') { + parser->offsets.filename =3D parser->item_offset; + rc =3D ftp_pl_insert_finfo(conn, finfo); + if(rc) { + PL_ERROR(conn, rc); + return bufflen; + } + parser->state.NT.main =3D PL_WINNT_DATE; + parser->state.NT.sub.filename =3D 0; + } + else { + PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); + return bufflen; + } + break; + } + break; + } + break; + default: + return bufflen+1; + break; + } + + i++; + } + + return bufflen; +} diff --git a/lib/ftplistparser.h b/lib/ftplistparser.h new file mode 100644 index 0000000..20d75ef --- /dev/null +++ b/lib/ftplistparser.h @@ -0,0 +1,38 @@ +#ifndef __FTPLISTPARSER_H_ +#define __FTPLISTPARSER_H_ +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include <curl/curl.h> + +/* WRITEFUNCTION callback for parsing LIST responses */ +size_t ftp_parselist(char *buffer, size_t size, size_t nmemb, void *con= nptr); + +struct ftp_parselist_data; /* defined inside ftplibparser.c */ + +CURLcode ftp_parselist_geterror(struct ftp_parselist_data *pl_data); + +struct ftp_parselist_data *ftp_parselist_data_alloc(void); + +void ftp_parselist_data_free(struct ftp_parselist_data **pl_data); + +#endif /* __FTPLISTPARSER_H_ */ diff --git a/lib/multi.c b/lib/multi.c index 476cb81..34d7ecc 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1128,6 +1128,17 @@ static CURLMcode multi_runsingle(struct Curl_mult= i *multi, = if(CURLE_OK =3D=3D easy->result) { if(!dophase_done) { + /* some steps needed for wildcard matching */ + if(easy->easy_handle->set.wildcardmatch) { + struct WildcardData *wc =3D &easy->easy_handle->wildcard;= + if(wc->state =3D=3D CURLWC_DONE || wc->state =3D=3D CURLW= C_SKIP) { + /* skip some states if it is important */ + Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + multistate(easy, CURLM_STATE_DONE); + result =3D CURLM_CALL_MULTI_PERFORM; + break; + } + } /* DO was not completed in one function call, we must conti= nue DOING... */ multistate(easy, CURLM_STATE_DOING); @@ -1338,7 +1349,7 @@ static CURLMcode multi_runsingle(struct Curl_multi= *multi, easy->easy_conn->writechannel_inuse =3D FALSE; } = - if(easy->result) { + if(easy->result) { /* The transfer phase returned error, we mark the connection to= get * closed to prevent being re-used. This is because we can't po= ssibly * know if the connection is in a good shape or not now. Unles= s it is @@ -1449,6 +1460,16 @@ static CURLMcode multi_runsingle(struct Curl_mult= i *multi, easy->easy_conn =3D NULL; } = + if(easy->easy_handle->set.wildcardmatch) { + if(easy->easy_handle->wildcard.state !=3D CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start a= gain + with CURLM_STATE_INIT */ + result =3D CURLM_CALL_MULTI_PERFORM; + multistate(easy, CURLM_STATE_INIT); + break; + } + } + /* after we have DONE what we're supposed to do, go COMPLETED, an= d it doesn't matter what the Curl_done() returned! */ multistate(easy, CURLM_STATE_COMPLETED); @@ -1550,11 +1571,26 @@ CURLMcode curl_multi_perform(CURLM *multi_handle= , int *running_handles) easy=3Dmulti->easy.next; while(easy !=3D &multi->easy) { CURLMcode result; + struct WildcardData *wc =3D &easy->easy_handle->wildcard; + + if(easy->easy_handle->set.wildcardmatch) { + if(!wc->filelist) { + CURLcode ret =3D Curl_wildcard_init(wc); /* init wildcard struc= tures */ + if(ret) + return CURLM_OUT_OF_MEMORY; + } + } = do result =3D multi_runsingle(multi, easy); while (CURLM_CALL_MULTI_PERFORM =3D=3D result); = + if(easy->easy_handle->set.wildcardmatch) { + /* destruct wildcard structures if it is needed */ + if(wc->state =3D=3D CURLWC_DONE || result) + Curl_wildcard_dtor(wc); + } + if(result) returncode =3D result; = diff --git a/lib/strerror.c b/lib/strerror.c index 673e89c..6fde477 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -275,6 +275,12 @@ curl_easy_strerror(CURLcode error) case CURLE_RTSP_SESSION_ERROR: return "RTSP session error"; = + case CURLE_FTP_BAD_FILE_LIST: + return "Unable to parse FTP file list"; + + case CURLE_CHUNK_FAILED: + return "Chunk callback failed"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE4: case CURLE_OBSOLETE10: diff --git a/lib/transfer.c b/lib/transfer.c index c9234a7..823d41a 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -2005,12 +2005,7 @@ CURLcode Curl_retry_request(struct connectdata *c= onn, return CURLE_OK; } = -/* - * Curl_perform() is the internal high-level function that gets called = by the - * external curl_easy_perform() function. It inits, performs and cleans= up a - * single file transfer. - */ -CURLcode Curl_perform(struct SessionHandle *data) +static CURLcode Curl_do_perform(struct SessionHandle *data) { CURLcode res; CURLcode res2; @@ -2045,6 +2040,15 @@ CURLcode Curl_perform(struct SessionHandle *data)= res =3D Curl_do(&conn, &do_done); = if(res =3D=3D CURLE_OK) { + if(conn->data->set.wildcardmatch) { + if(conn->data->wildcard.state =3D=3D CURLWC_DONE || + conn->data->wildcard.state =3D=3D CURLWC_SKIP) { + /* keep connection open for application to use the socket *= / + conn->bits.close =3D FALSE; + res =3D Curl_done(&conn, CURLE_OK, FALSE); + break; + } + } res =3D Transfer(conn); /* now fetch that URL please */ if((res =3D=3D CURLE_OK) || (res =3D=3D CURLE_RECV_ERROR)) { bool retry =3D FALSE; @@ -2162,6 +2166,39 @@ CURLcode Curl_perform(struct SessionHandle *data)= } = /* + * Curl_perform() is the internal high-level function that gets called = by the + * external curl_easy_perform() function. It inits, performs and cleans= up a + * single file transfer. + */ +CURLcode Curl_perform(struct SessionHandle *data) +{ + CURLcode res; + if(!data->set.wildcardmatch) + return Curl_do_perform(data); + + /* init main wildcard structures */ + res =3D Curl_wildcard_init(&data->wildcard); + if(res) + return res; + + res =3D Curl_do_perform(data); + if(res) { + Curl_wildcard_dtor(&data->wildcard); + return res; + } + + /* wildcard loop */ + while(!res && data->wildcard.state !=3D CURLWC_DONE) + res =3D Curl_do_perform(data); + + Curl_wildcard_dtor(&data->wildcard); + + /* wildcard download finished or failed */ + data->wildcard.state =3D CURLWC_INIT; + return res; +} + +/* * Curl_setup_transfer() is called to setup some basic properties for t= he * upcoming transfer. */ diff --git a/lib/url.c b/lib/url.c index ec7f46a..c7612cf 100644 --- a/lib/url.c +++ b/lib/url.c @@ -760,6 +760,10 @@ CURLcode Curl_init_userdefined(struct UserDefined *= set) res =3D setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH= ); #endif = + set->wildcardmatch =3D 0L; + set->chunk_bgn =3D ZERO_NULL; + set->chunk_end =3D ZERO_NULL; + return res; } = @@ -828,6 +832,9 @@ CURLcode Curl_open(struct SessionHandle **curl) data->progress.flags |=3D PGRS_HIDE; data->state.current_speed =3D -1; /* init to negative =3D=3D imposs= ible */ = + data->wildcard.state =3D CURLWC_INIT; + data->wildcard.filelist =3D NULL; + data->set.fnmatch =3D ZERO_NULL; /* This no longer creates a connection cache here. It is instead ma= de on the first call to curl_easy_perform() or when the handle is adde= d to a multi stack. */ @@ -2445,6 +2452,23 @@ CURLcode Curl_setopt(struct SessionHandle *data, = CURLoption option, /* Set the user defined RTP write function */ data->set.fwrite_rtp =3D va_arg(param, curl_write_callback); break; + + case CURLOPT_WILDCARDMATCH: + data->set.wildcardmatch =3D va_arg(param, long); + break; + case CURLOPT_CHUNK_BGN_FUNCTION: + data->set.chunk_bgn =3D va_arg(param, curl_chunk_bgn_callback); + break; + case CURLOPT_CHUNK_END_FUNCTION: + data->set.chunk_end =3D va_arg(param, curl_chunk_end_callback); + break; + case CURLOPT_FNMATCH_FUNCTION: + data->set.fnmatch =3D va_arg(param, curl_fnmatch_callback); + break; + case CURLOPT_CHUNK_DATA: + data->wildcard.customptr =3D va_arg(param, void *); + break; + default: /* unknown tag and its companion, just ignore: */ result =3D CURLE_FAILED_INIT; /* correct this */ diff --git a/lib/urldata.h b/lib/urldata.h index 30782c2..9cfe574 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -153,6 +153,7 @@ #include "ssh.h" #include "http.h" #include "rtsp.h" +#include "wildcard.h" = #ifdef HAVE_GSSAPI # ifdef HAVE_GSSGNU @@ -1409,6 +1410,12 @@ struct UserDefined { /* Common RTSP header options */ Curl_RtspReq rtspreq; /* RTSP request type */ long rtspversion; /* like httpversion, for RTSP */ + bool wildcardmatch; /* enable wildcard matching */ + curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer = starts */ + curl_chunk_end_callback chunk_end; /* called after part transferring + stopped */ + curl_fnmatch_callback fnmatch; /* callback to decide which file corre= sponds + to pattern (e.g. if WILDCARDMATCH i= s on) */ }; = struct Names { @@ -1450,6 +1457,7 @@ struct SessionHandle { struct Progress progress; /* for all the progress meter data */ struct UrlState state; /* struct for fields used for state info= and other dynamic purposes */ + struct WildcardData wildcard; /* wildcard download state info */ struct PureInfo info; /* stats, reports and info data */ #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) iconv_t outbound_cd; /* for translating to the network encodi= ng */ diff --git a/lib/wildcard.c b/lib/wildcard.c new file mode 100644 index 0000000..43ac546 --- /dev/null +++ b/lib/wildcard.c @@ -0,0 +1,66 @@ +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include "wildcard.h" +#include "llist.h" +#include "fileinfo.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +CURLcode Curl_wildcard_init(struct WildcardData *wc) +{ + /* now allocate only wc->filelist, everything else + will be allocated if it is needed. */ + wc->filelist =3D Curl_llist_alloc(Curl_fileinfo_dtor); + if(!wc->filelist) {; + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + +void Curl_wildcard_dtor(struct WildcardData *wc) +{ + if(!wc) + return; + + if(wc->tmp_dtor) { + wc->tmp_dtor(wc->tmp); + wc->tmp =3D NULL; + } + + if(wc->filelist) { + Curl_llist_destroy(wc->filelist, NULL); + wc->filelist =3D NULL; + } + + if(wc->path) { + free(wc->path); + wc->path =3D NULL; + } + + if(wc->pattern) { + free(wc->pattern); + wc->pattern =3D NULL; + } + wc->customptr =3D NULL; +} diff --git a/lib/wildcard.h b/lib/wildcard.h new file mode 100644 index 0000000..1a1c1bb --- /dev/null +++ b/lib/wildcard.h @@ -0,0 +1,58 @@ +#ifndef __WILDCARD_H +#define __WILDCARD_H +/**********************************************************************= ***** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Daniel Stenberg, <daniel_at_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 O= F ANY + * KIND, either express or implied. + * + **********************************************************************= *****/ + +#include <curl/curl.h> + +/* list of wildcard process states */ +typedef enum { + CURLWC_INIT =3D 0, + CURLWC_MATCHING, /* library is trying to get list of addresses for + downloading */ + CURLWC_DOWNLOADING, + CURLWC_CLEAN, /* deallocate resources and reset settings */ + CURLWC_SKIP, /* skip over concrete file */ + CURLWC_ERROR, /* error cases */ + CURLWC_DONE /* if is wildcard->state =3D=3D CURLWC_DONE wildcard lo= op in + Curl_perform() will end */ +} curl_wildcard_states; + +typedef void (*curl_wildcard_tmp_dtor)(void *ptr); + +/* struct keeping information about wildcard download process */ +struct WildcardData { + curl_wildcard_states state; + char *path; /* path to the directory, where we trying wildcard-match = */ + char *pattern; /* wildcard pattern */ + struct curl_llist *filelist; /* llist with struct Curl_fileinfo */ + void *tmp; /* pointer to protocol specific temporary data */ + curl_wildcard_tmp_dtor tmp_dtor; + void *customptr; /* for CURLOPT_CHUNK_DATA pointer */ +}; + +CURLcode Curl_wildcard_init(struct WildcardData *wc); +void Curl_wildcard_dtor(struct WildcardData *wc); + +struct SessionHandle; + +#endif /* __WILDCARD_H */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index dabe3d1..a8cf5d7 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -60,6 +60,7 @@ EXTRA_DIST =3D test1 test108 test117 test127 test20 te= st27 test34 test46 \ test1072 test1073 test1074 test1075 test1076 test1077 test1078 test107= 9 \ test1080 test1081 test1082 test1083 test1084 test1085 test633 test634 = \ test635 test636 test637 test558 test559 test1086 test1087 test1088 = \ + test574 test575 test576 test577 test1113 test1114 \ test1089 test1090 test1091 test1092 test1093 test1094 test1095 test109= 6 \ test1097 test560 test561 test1098 test1099 test562 test563 test1100 = \ test564 test1101 test1102 test1103 test1104 test299 test310 test311 = \ diff --git a/tests/data/test1113 b/tests/data/test1113 new file mode 100644 index 0000000..6ff1d19 --- /dev/null +++ b/tests/data/test1113 @@ -0,0 +1,71 @@ +<testcase> +<info> +<keywords> +FTP +wildcardmatch +ftplistparser +</keywords> +</info> + +# +# Server-side +<reply> +<data mode=3D"text"> +</data> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<tool> +lib574 +</tool> +<name> +FTP wildcard download - changed fnmatch, 2x perform (DOS LIST response)= +</name> +<command> +ftp://%HOSTIP:%FTPPORT/fully_simulated/DOS/*.txt +</command> +</client> + +############################################ +# Verify data after the test has been "shot" +<verify> +<errorcode> +0 +</errorcode> +<strip> +^RETR.* +^EPSV.* +^PWD.* +^CWD.* +^TYPE.* +^LIST.* +</strip> +<strippart> +s/USER.*/USER/ +s/PASS.*/PASS/ +s/QUIT.*/QUIT/ +</strippart> +# THERE SHOULD NOT BE "SIZE"! and once "USER && PASS" +<protocol> +USER +PASS +QUIT +</protocol> +<stdout mode=3D"text"> +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +</stdout> +</verify> +</testcase> diff --git a/tests/data/test1114 b/tests/data/test1114 new file mode 100644 index 0000000..8eee429 --- /dev/null +++ b/tests/data/test1114 @@ -0,0 +1,136 @@ +<testcase> +<info> +<keywords> +FTP +wildcardmatch +ftplistparser +</keywords> +</info> + +# Server-side +<reply> +<data> +</data> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<tool> +lib576 +</tool> + <name> +FTP wildcard download - skip/parser_correctness/CURLOPT_FNMATCH_FUNCTIO= N (DOS) + </name> + <command> +ftp://%HOSTIP:%FTPPORT/fully_simulated/DOS/* +</command> +</client> + +# Verify data after the test has been "shot" +<verify> +<errorcode> +0 +</errorcode> +<stdout mode=3D"text"> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 12 +Filename: . +Size: 0B +Time: 04-27-10 05:12AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 11 +Filename: .. +Size: 0B +Time: 04-23-10 03:12AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 10 +Filename: chmod1 +Size: 38B +Time: 01-11-10 10:00AM +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 444 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 9 +Filename: chmod2 +Size: 38B +Time: 02-01-10 08:00AM +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 666 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 8 +Filename: chmod3 +Size: 38B +Time: 02-01-10 08:00AM +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 777 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 7 +Filename: chmod4 +Size: 0B +Time: 05-04-10 04:31AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 6 +Filename: chmod5 +Size: 0B +Time: 05-04-10 04:31AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 5 +Filename: empty_file.dat +Size: 0B +Time: 04-27-10 11:01AM +Filetype: regular file +Content: +------------------------------------------------------------- +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 4 +Filename: file.txt +Size: 35B +Time: 04-27-10 11:01AM +Filetype: regular file +Content: +------------------------------------------------------------- +This is content of file "file.txt" +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 3 +Filename: .NeXT +Size: 0B +Time: 01-23-05 02:05AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 2 +Filename: someothertext.txt +Size: 47B +Time: 04-27-10 11:01AM +Filetype: regular file +Content: +------------------------------------------------------------- +# THIS CONTENT WAS SKIPPED IN CHUNK_BGN CALLBACK # +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 1 +Filename: weirddir.txt +Size: 0B +Time: 04-23-10 03:12AM +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +</stdout> +</verify> +</testcase> diff --git a/tests/data/test146 b/tests/data/test146 index e04f8f0..3cd4bd5 100644 --- a/tests/data/test146 +++ b/tests/data/test146 @@ -45,7 +45,7 @@ EPSV TYPE I SIZE 146 RETR 146 -CWD /nowhere/anywhere +CWD / EPSV SIZE 146 RETR 146 diff --git a/tests/data/test149 b/tests/data/test149 index bdbdcc7..f7973f4 100644 --- a/tests/data/test149 +++ b/tests/data/test149 @@ -34,7 +34,7 @@ CWD dir1 EPSV TYPE I STOR 149 -CWD /nowhere/anywhere +CWD / CWD dir2 EPSV STOR 149 diff --git a/tests/data/test539 b/tests/data/test539 index f1ad701..2406c54 100644 --- a/tests/data/test539 +++ b/tests/data/test539 @@ -53,7 +53,7 @@ TYPE I SIZE 539 RETR 539 SYST -CWD /nowhere/anywhere +CWD / EPSV TYPE A LIST path/to/the/file/539./ diff --git a/tests/data/test574 b/tests/data/test574 new file mode 100644 index 0000000..9d50045 --- /dev/null +++ b/tests/data/test574 @@ -0,0 +1,71 @@ +<testcase> +<info> +<keywords> +FTP +wildcardmatch +ftplistparser +</keywords> +</info> + +# +# Server-side +<reply> +<data mode=3D"text"> +</data> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<tool> +lib574 +</tool> +<name> +FTP wildcard download - changed fnmatch, 2x perform (UNIX LIST response= ) +</name> +<command> +ftp://%HOSTIP:%FTPPORT/fully_simulated/UNIX/*.txt +</command> +</client> + +############################################ +# Verify data after the test has been "shot" +<verify> +<errorcode> +0 +</errorcode> +<strip> +^RETR.* +^EPSV.* +^PWD.* +^CWD.* +^TYPE.* +^LIST.* +</strip> +<strippart> +s/USER.*/USER/ +s/PASS.*/PASS/ +s/QUIT.*/QUIT/ +</strippart> +# THERE SHOULD NOT BE "SIZE"! and once "USER && PASS" +<protocol> +USER +PASS +QUIT +</protocol> +<stdout mode=3D"text"> +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +</stdout> +</verify> +</testcase> diff --git a/tests/data/test575 b/tests/data/test575 new file mode 100644 index 0000000..c460467 --- /dev/null +++ b/tests/data/test575 @@ -0,0 +1,79 @@ +<testcase> +<info> +<keywords> +FTP +multi +wildcardmatch +ftplistparser +</keywords> +</info> + +# Server-side +<reply> +<data> +</data> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<tool> +lib575 +</tool> + <name> +FTP wildcard download - dup_handle && multi interface + </name> + <command> +ftp://%HOSTIP:%FTPPORT/fully_simulated/UNIX/* +</command> +</client> +# Verify data after the test has been "shot" +<verify> +<strip> +^RETR.* +^EPSV.* +^CWD.* +^PWD.* +^TYPE.* +</strip> +<strippart> +s/^USER.*/USER/ +s/^PASS.*/PASS/ +s/^LIST.*/LIST/ +s/^QUIT.*/QUIT/ +</strippart> +<errorcode> +0 +</errorcode> +<protocol> +USER +PASS +LIST +LIST +QUIT +USER +PASS +LIST +QUIT +</protocol> +<stdout mode=3D"text"> +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +This file should have permissions 444 +This file should have permissions 666 +This file should have permissions 777 +This is content of file "file.txt" +Some junk ;-) This file does not really exist. +</stdout> +</verify> +</testcase> diff --git a/tests/data/test576 b/tests/data/test576 new file mode 100644 index 0000000..4d4b90c --- /dev/null +++ b/tests/data/test576 @@ -0,0 +1,192 @@ +<testcase> +<info> +<keywords> +FTP +wildcardmatch +ftplistparser +</keywords> +</info> + +# Server-side +<reply> +<data> +</data> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<tool> +lib576 +</tool> + <name> +FTP wildcard download - skip/parser_correctness/CURLOPT_FNMATCH_FUNCTIO= N (UNIX) + </name> + <command> +ftp://%HOSTIP:%FTPPORT/fully_simulated/UNIX/* +</command> +</client> + +# Verify data after the test has been "shot" +<verify> +<errorcode> +0 +</errorcode> +<stdout mode=3D"text"> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 14 +Filename: . +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 20480B +User: ftp-default +Group: ftp-default +Time: Apr 27 5:12 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 13 +Filename: .. +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 20480B +User: ftp-default +Group: ftp-default +Time: Apr 23 3:12 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 12 +Filename: chmod1 +Permissions: r--r--r-- (parsed =3D> 444) +Size: 38B +User: ftp-default +Group: ftp-default +Time: Jan 11 10:00 +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 444 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 11 +Filename: chmod2 +Permissions: rw-rw-rw- (parsed =3D> 666) +Size: 38B +User: ftp-default +Group: ftp-default +Time: Feb 1 8:00 +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 666 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 10 +Filename: chmod3 +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 38B +User: ftp-default +Group: ftp-default +Time: Feb 1 8:00 +Filetype: regular file +Content: +------------------------------------------------------------- +This file should have permissions 777 +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 9 +Filename: chmod4 +Permissions: --S--S--t (parsed =3D> 7001) +Size: 4096B +User: ftp-default +Group: ftp-default +Time: May 4 4:31 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 8 +Filename: chmod5 +Permissions: --s--s--T (parsed =3D> 7110) +Size: 4096B +User: ftp-default +Group: ftp-default +Time: May 4 4:31 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 7 +Filename: empty_file.dat +Permissions: rw-r--r-- (parsed =3D> 644) +Size: 0B +User: ftp-default +Group: ftp-default +Time: Apr 27 11:01 +Filetype: regular file +Content: +------------------------------------------------------------- +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 6 +Filename: file.txt +Permissions: rw-r--r-- (parsed =3D> 644) +Size: 35B +User: ftp-default +Group: ftp-default +Time: Apr 27 11:01 +Filetype: regular file +Content: +------------------------------------------------------------- +This is content of file "file.txt" +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 5 +Filename: link +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 0B +User: ftp-default +Group: ftp-default +Time: Jan 6 4:42 +Filetype: symlink +Target: file.txt +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 4 +Filename: link_absolute +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 0B +User: ftp-default +Group: ftp-default +Time: Jan 6 4:45 +Filetype: symlink +Target: /data/ftp/file.txt +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 3 +Filename: .NeXT +Permissions: rwxrwxrwx (parsed =3D> 777) +Size: 4096B +User: ftp-default +Group: ftp-default +Time: Jan 23 2:05 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 2 +Filename: someothertext.txt +Permissions: rw-r--r-- (parsed =3D> 644) +Size: 47B +User: ftp-default +Group: ftp-default +Time: Apr 27 11:01 +Filetype: regular file +Content: +------------------------------------------------------------- +# THIS CONTENT WAS SKIPPED IN CHUNK_BGN CALLBACK # +------------------------------------------------------------- +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Remains: 1 +Filename: weirddir.txt +Permissions: rwxr-xrwx (parsed =3D> 757) +Size: 4096B +User: ftp-default +Group: ftp-default +Time: Apr 23 3:12 +Filetype: directory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +</stdout> +</verify> +</testcase> diff --git a/tests/data/test577 b/tests/data/test577 new file mode 100644 index 0000000..5f1898f --- /dev/null +++ b/tests/data/test577 @@ -0,0 +1,38 @@ +<testcase> +<info> +<keywords> +wildcardmatch +</keywords> +</info> +# +# Server-side +<reply> +</reply> + +# Client-side +<client> +<server> +none +</server> +# tool is what to use instead of 'curl' +<tool> +lib577 +</tool> + + <name> +Curl_fnmatch() testing + </name> + <command> +nothing +</command> +</client> + +# +# Verify data after the test has been "shot" +<verify> +<stdout mode=3D"text"> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +</stdout> +</verify> +</testcase> diff --git a/tests/directories.pm b/tests/directories.pm new file mode 100644 index 0000000..6f20e6f --- /dev/null +++ b/tests/directories.pm @@ -0,0 +1,266 @@ +%file_chmod1 =3D ( + 'name' =3D> 'chmod1', + 'content' =3D> "This file should have permissions 444\n", + 'perm' =3D> 'r--r--r--', + 'time' =3D> 'Jan 11 10:00', + 'dostime' =3D> '01-11-10 10:00AM', +); + +%file_chmod2 =3D ( + 'name' =3D> 'chmod2', + 'content' =3D> "This file should have permissions 666\n", + 'perm' =3D> 'rw-rw-rw-', + 'time' =3D> 'Feb 1 8:00', + 'dostime' =3D> '02-01-10 08:00AM', +); + +%file_chmod3 =3D ( + 'name' =3D> 'chmod3', + 'content' =3D> "This file should have permissions 777\n", + 'perm' =3D> 'rwxrwxrwx', + 'time' =3D> 'Feb 1 8:00', + 'dostime' =3D> '02-01-10 08:00AM', +); + +%file_chmod4 =3D ( + 'type' =3D> 'd', + 'name' =3D> 'chmod4', + 'content' =3D> "This file should have permissions 001\n", + 'perm' =3D> '--S--S--t', + 'time' =3D> 'May 4 4:31', + 'dostime' =3D> '05-04-10 04:31AM' +); + +%file_chmod5 =3D ( + 'type' =3D> 'd', + 'name' =3D> 'chmod5', + 'content' =3D> "This file should have permissions 110\n", + 'perm' =3D> '--s--s--T', + 'time' =3D> 'May 4 4:31', + 'dostime' =3D> '05-04-10 04:31AM' +); + +%link_link =3D ( + 'type' =3D> 'l', + 'name' =3D> 'link -> file.txt', + 'size' =3D> '8', + 'perm' =3D> 'rwxrwxrwx', + 'time' =3D> 'Jan 6 4:42' +); + +%link_link_absolute =3D ( + 'type' =3D> 'l', + 'name' =3D> 'link_absolute -> /data/ftp/file.txt', + 'size' =3D> '15', + 'perm' =3D> 'rwxrwxrwx', + 'time' =3D> 'Jan 6 4:45' +); + +%dir_dot =3D ( + 'type' =3D> "d", + 'name' =3D> ".", + 'hlink' =3D> "4", + 'time' =3D> "Apr 27 5:12", + 'size' =3D> "20480", + 'dostime' =3D> "04-27-10 05:12AM", + 'perm' =3D> "rwxrwxrwx" +); + +%dir_ddot =3D ( + 'type' =3D> "d", + 'name' =3D> "..", + 'hlink' =3D> "4", + 'size' =3D> "20480", + 'time' =3D> "Apr 23 3:12", + 'dostime' =3D> "04-23-10 03:12AM", + 'perm' =3D> "rwxrwxrwx" +); + +%dir_weirddir_txt =3D ( + 'type' =3D> "d", + 'name' =3D> "weirddir.txt", + 'hlink' =3D> "2", + 'size' =3D> "4096", + 'time' =3D> "Apr 23 3:12", + 'dostime' =3D> "04-23-10 03:12AM", + 'perm' =3D> "rwxr-xrwx" +); + +%dir_UNIX =3D ( + 'type' =3D> "d", + 'name' =3D> "UNIX", + 'hlink' =3D> "11", + 'size' =3D> "4096", + 'time' =3D> "Nov 01 2008", + 'dostime' =3D> "11-01-08 11:11AM", + 'perm' =3D> "rwx--x--x" +); + +%dir_DOS =3D ( + 'type' =3D> "d", + 'name' =3D> "DOS", + 'hlink' =3D> "11", + 'size' =3D> "4096", + 'time' =3D> "Nov 01 2008", + 'dostime' =3D> "11-01-08 11:11AM", + 'perm' =3D> "rwx--x--x" +); + +%dir_dot_NeXT =3D ( + 'type' =3D> "d", + 'name' =3D> ".NeXT", + 'hlink' =3D> "4", + 'size' =3D> "4096", + 'time' =3D> "Jan 23 2:05", + 'dostime' =3D> "01-23-05 02:05AM", + 'perm' =3D> "rwxrwxrwx" +); + +%file_empty_file_dat =3D ( + 'name' =3D> "empty_file.dat", + 'content' =3D> "", + 'perm' =3D> "rw-r--r--", + 'time' =3D> "Apr 27 11:01", + 'dostime' =3D> "04-27-10 11:01AM" +); + +%file_file_txt =3D ( + 'name' =3D> "file.txt", + 'content' =3D> "This is content of file \"file.txt\"\n", + 'time' =3D> "Apr 27 11:01", + 'dostime' =3D> "04-27-10 11:01AM", + 'perm' =3D> "rw-r--r--" +); + +%file_someothertext_txt =3D ( + 'name' =3D> "someothertext.txt", + 'content' =3D> "Some junk ;-) This file does not really exist.\n", + 'time' =3D> "Apr 27 11:01", + 'dostime' =3D> "04-27-10 11:01AM", + 'perm' =3D> "rw-r--r--" +); + +%lists =3D ( + '/fully_simulated/' =3D> { + 'files' =3D> [ \%dir_dot, \%dir_ddot, \%dir_DOS, \%dir_UNIX ], + 'eol' =3D> "\r\n", + 'type' =3D> "unix" + }, + '/fully_simulated/UNIX/' =3D> { + 'files' =3D> [ \%dir_dot, \%dir_ddot, + \%file_chmod1, \%file_chmod2, \%file_chmod3, \%file_= chmod4, \%file_chmod5, + \%file_empty_file_dat, \%file_file_txt, + \%link_link, \%link_link_absolute, \%dir_dot_NeXT, + \%file_someothertext_txt, \%dir_weirddir_txt ], + 'eol' =3D> "\r\n", + 'type' =3D> 'unix' + }, + '/fully_simulated/DOS/' =3D> { + 'files' =3D> [ \%dir_dot, \%dir_ddot, + \%file_chmod1, \%file_chmod2, \%file_chmod3, \%file_= chmod4, \%file_chmod5, + \%file_empty_file_dat, \%file_file_txt, + \%dir_dot_NeXT, \%file_someothertext_txt, \%dir_weir= ddir_txt ], + 'eol' =3D> "\r\n", + 'type' =3D> 'dos' + } +); + +sub ftp_createcontent($) { + my (%list) =3D @_; + + $type =3D $$list{'type'}; + $eol =3D $$list{'eol'}; + $list_ref =3D $$list{'files'}; + + my @diroutput; + my @contentlist; + if($type eq "unix") { + for(@$list_ref) { + my %file =3D %$_; + my $line =3D ""; + my $ftype =3D $file{'type'} ? $file{'type'} : "-"; + my $fperm =3D $file{'perm'} ? $file{'perm'} : "rwxr-xr-x"; + my $fuser =3D $file{'user'} ? sprintf("%15s", $file{'user'}) = : "ftp-default"; + my $fgroup =3D $file{'group'} ? sprintf("%15s", $file{'group'}) = : "ftp-default"; + my $fsize =3D ""; + if($file{'type'} eq "d") { + $fsize =3D $file{'size'} ? sprintf("%7s", $file{'size'}) : spri= ntf("%7d", 4096); + } + else { + $fsize =3D sprintf("%7d", length $file{'content'}); + } + my $fhlink =3D $file{'hlink'} ? sprintf("%4d", $file{'hlink'}) = : " 1"; + my $ftime =3D $file{'time'} ? sprintf("%10s", $file{'time'}) = : "Jan 9 1933"; + push(@contentlist, "$ftype$fperm $fhlink $fuser $fgroup $fsize $f= time $file{'name'}$eol"); + } + + return @contentlist; + } + elsif($type =3D~ /^dos$/) { + for(@$list_ref) { + my %file =3D %$_; + my $line =3D ""; + my $time =3D $file{'dostime'} ? $file{'dostime'} : "06-25-97 09:= 12AM"; + my $size_or_dir; + if($file{'type'} =3D~ /^d$/) { + $size_or_dir =3D " <DIR> "; + } + else { + $size_or_dir =3D sprintf("%20d", length $file{'content'}); + } + push(@contentlist, "$time $size_or_dir $file{'name'}$eol"); + } + return @contentlist; + } +} + +sub wildcard_filesize($$) { + my ($list_type, $file) =3D @_; + $list =3D $lists{$list_type}; + if($list) { + my $files =3D $list->{'files'}; + for(@$files) { + my %f =3D %$_; + if ($f{'name'} eq $file) { + if($f{'content'}) { + return length $f{'content'}; + } + elsif ($f{'type'} ne "d"){ + return 0; + } + else { + return -1; + } + } + } + } + return -1; +} +sub wildcard_getfile($$) { + my ($list_type, $file) =3D @_; + $list =3D $lists{$list_type}; + if($list) { + my $files =3D $list->{'files'}; + for(@$files) { + my %f =3D %$_; + if ($f{'name'} eq $file) { + if($f{'content'}) { + return (length $f{'content'}, $f{'content'}); + } + elsif ($f{'type'} ne "d"){ + return (0, ""); + } + else { + return (-1, 0); + } + } + } + } + return (-1, 0); +} + +sub ftp_contentlist { + my $listname =3D $_[0]; + $list =3D $lists{$listname}; + return ftp_createcontent(\$list); +} diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index cc69585..ed2a832 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -54,6 +54,7 @@ use IPC::Open2; = require "getpart.pm"; require "ftp.pm"; +require "directories.pm"; = use serverhelp qw( servername_str @@ -137,6 +138,13 @@ my %customcount; # my %delayreply; # = #**********************************************************************= +# global variables for to test ftp wildcardmatching or other test that +# need flexible LIST responses.. and corresponding files. +# $ftptargetdir is keeping the fake "name" of LIST directory. +my $ftplistparserstate; +my $ftptargetdir; + +#**********************************************************************= # global vars used for signal handling # my $got_exit_signal =3D 0; # set if program should finish execution ASA= P @@ -344,6 +352,8 @@ sub protocolsetup { 'LIST' =3D> \&LIST_ftp, 'NLST' =3D> \&NLST_ftp, 'PASV' =3D> \&PASV_ftp, + 'CWD' =3D> \&CWD_ftp, + 'PWD' =3D> \&PWD_ftp, 'EPSV' =3D> \&PASV_ftp, 'RETR' =3D> \&RETR_ftp, 'SIZE' =3D> \&SIZE_ftp, @@ -362,7 +372,6 @@ sub protocolsetup { 'CWD' =3D> '250 CWD command successful.', 'SYST' =3D> '215 UNIX Type: L8', # just fake something 'QUIT' =3D> '221 bye bye baby', # just reply something - 'PWD' =3D> '257 "/nowhere/anywhere" is current directory',= 'MKD' =3D> '257 Created your requested directory', 'REST' =3D> '350 Yeah yeah we set it there for you', 'DELE' =3D> '200 OK OK OK whatever you say', @@ -683,6 +692,64 @@ sub REST_ftp { logmsg "Set REST position to $rest\n" } = +sub switch_directory_goto { + my $target_dir =3D $_; + + if(!$ftptargetdir) { + $ftptargetdir =3D "/"; + } + + if($target_dir eq "") { + $ftptargetdir =3D "/"; + } + elsif($target_dir eq "..") { + if($ftptargetdir eq "/") { + $ftptargetdir =3D "/"; + } + else { + $ftptargetdir =3D~ s/[[:alnum:]]+\/$//; + } + } + else { + $ftptargetdir .=3D $target_dir . "/"; + } +} + +sub switch_directory { + my $target_dir =3D $_[0]; + + if($target_dir eq "/") { + $ftptargetdir =3D "/"; + } + else { + my @dirs =3D split("/", $target_dir); + for(@dirs) { + switch_directory_goto($_); + } + } +} + +sub CWD_ftp { + my ($folder, $fullcommand) =3D $_[0]; + switch_directory($folder); + if($ftptargetdir =3D~ /^\/fully_simulated/) { + $ftplistparserstate =3D "enabled"; + } + else { + undef $ftplistparserstate; + } +} + +sub PWD_ftp { + my $mydir; + $mydir =3D $ftptargetdir ? $ftptargetdir : "/"; + + if($mydir ne "/") { + $mydir =3D~ s/\/$//; + } + sendcontrol "257 \"$mydir\" is current directory\r\n"; +} + sub LIST_ftp { # print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (= 0 bytes)\r\n"; = @@ -699,6 +766,10 @@ my @ftpdir=3D("total 20\r\n", "drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\r\n", "dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n"); = + if($ftplistparserstate) { + @ftpdir =3D ftp_contentlist($ftptargetdir); + } + logmsg "pass LIST data on data connection\n"; for(@ftpdir) { senddata $_; @@ -748,6 +819,16 @@ sub MDTM_ftp { = sub SIZE_ftp { my $testno =3D $_[0]; + if($ftplistparserstate) { + my $size =3D wildcard_filesize($ftptargetdir, $testno); + if($size =3D=3D -1) { + sendcontrol "550 $testno: No such file or directory.\r\n"; + } + else { + sendcontrol "213 $size\r\n"; + } + return 0; + } = if($testno =3D~ /^verifiedserver$/) { my $response =3D "WE ROOLZ: $$\r\n"; @@ -803,6 +884,21 @@ sub SIZE_ftp { sub RETR_ftp { my ($testno) =3D @_; = + if($ftplistparserstate) { + my @content =3D wildcard_getfile($ftptargetdir, $testno); + if($content[0] =3D=3D -1) { + #file not found + } + else { + my $size =3D length $content[1]; + sendcontrol "150 Binary data connection for $testno ($size = bytes).\r\n", + senddata $content[1]; + close_dataconn(0); + sendcontrol "226 File transfer complete\r\n"; + } + return 0; + } + if($testno =3D~ /^verifiedserver$/) { # this is the secret command that verifies that this actually i= s # the curl test server @@ -1326,6 +1422,15 @@ while(1) { &customize(); # read test control instructions = sendcontrol @welcome; + + #remove global variables from last connection + if($ftplistparserstate) { + undef $ftplistparserstate; + } + if($ftptargetdir) { + undef $ftptargetdir; + } + if($verbose) { for(@welcome) { print STDERR "OUT: $_"; diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 58d5d92..814c01e 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -8,6 +8,7 @@ SUPPORTFILES =3D first.c test.h noinst_PROGRAMS =3D lib500 lib501 lib502 lib503 lib504 lib505 lib506 \ lib507 lib508 lib510 lib511 lib512 lib513 lib514 lib515 lib516 \ lib517 lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527= \ + lib574 lib575 lib576 lib577 \ lib529 lib530 lib532 lib533 lib536 lib537 lib540 lib541 lib542 lib543= \ lib544 lib545 lib547 lib548 lib549 lib552 lib553 lib554 lib555 lib556= \ lib539 lib557 lib558 lib559 lib560 lib562 lib564 lib565 lib566 lib567= \ @@ -124,6 +125,14 @@ lib559_CFLAGS =3D -DLIB559 = lib560_SOURCES =3D lib560.c $(SUPPORTFILES) = +lib574_SOURCES =3D lib574.c $(SUPPORTFILES) + +lib575_SOURCES =3D lib575.c $(SUPPORTFILES) + +lib576_SOURCES =3D lib576.c $(SUPPORTFILES) + +lib577_SOURCES =3D lib577.c $(SUPPORTFILES) + lib562_SOURCES =3D lib562.c $(SUPPORTFILES) = lib564_SOURCES =3D lib564.c $(SUPPORTFILES) $(TESTUTIL) diff --git a/tests/libtest/lib574.c b/tests/libtest/lib574.c new file mode 100644 index 0000000..69b2979 --- /dev/null +++ b/tests/libtest/lib574.c @@ -0,0 +1,56 @@ +/**********************************************************************= ******* + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + */ + +#include "test.h" + +#include "memdebug.h" + +static int new_fnmatch(const char *pattern, const char *string) +{ + (void)pattern; + (void)string; + return CURL_FNMATCHFUNC_MATCH; +} + +int test(char *URL) +{ + int res; + CURL *curl; + + if (curl_global_init(CURL_GLOBAL_ALL) !=3D CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + if ((curl =3D curl_easy_init()) =3D=3D NULL) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + test_setopt(curl, CURLOPT_URL, URL); + test_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + test_setopt(curl, CURLOPT_FNMATCH_FUNCTION, new_fnmatch); + + res =3D curl_easy_perform(curl); + if(res) { + fprintf(stderr, "curl_easy_perform() failed %d\n", res); + goto test_cleanup; + } + res =3D curl_easy_perform(curl); + if(res) { + fprintf(stderr, "curl_easy_perform() failed %d\n", res); + goto test_cleanup; + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + return res; +} diff --git a/tests/libtest/lib575.c b/tests/libtest/lib575.c new file mode 100644 index 0000000..3bf15ea --- /dev/null +++ b/tests/libtest/lib575.c @@ -0,0 +1,109 @@ +/**********************************************************************= ******* + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + */ + +#include "test.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "testutil.h" +#include "memdebug.h" + +/* 3x download! + * 1. normal + * 2. dup handle + * 3. with multi interface + */ + +int test(char *URL) +{ + CURLMcode m; + CURL *handle =3D NULL, *duphandle; + CURLM *mhandle =3D NULL; + int res =3D 0; + int still_running =3D 0; + + if(curl_global_init(CURL_GLOBAL_ALL)) { + fprintf(stderr, "curl_global_init() failed\n"); + goto test_cleanup; + } + + handle =3D curl_easy_init(); + if(!handle) { + res =3D CURLE_OUT_OF_MEMORY; + goto test_cleanup; + } + + test_setopt(handle, CURLOPT_URL, URL); + test_setopt(handle, CURLOPT_WILDCARDMATCH, 1L); + test_setopt(handle, CURLOPT_VERBOSE, 1L); + + res =3D curl_easy_perform(handle); + if(res) + goto test_cleanup; + + res =3D curl_easy_perform(handle); + if(res) + goto test_cleanup; + + duphandle =3D curl_easy_duphandle(handle); + if(!duphandle) + goto test_cleanup; + curl_easy_cleanup(handle); + handle =3D duphandle; + + mhandle =3D curl_multi_init(); + if(!mhandle) { + fprintf(stderr, "curl_multi_init() failed\n"); + goto test_cleanup; + } + + curl_multi_add_handle(mhandle, handle); + + while(CURLM_CALL_MULTI_PERFORM =3D=3D + curl_multi_perform(mhandle, &still_running)); + + while(still_running) { + struct timeval timeout; + int rc; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int max_fdset =3D -1; + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + timeout.tv_sec =3D 3; + timeout.tv_usec =3D 0; + + m =3D curl_multi_fdset(mhandle, &fdread, &fdwrite, &fdexcep, &max_f= dset); + rc =3D select(max_fdset + 1, &fdread, &fdwrite, &fdexcep, &timeout)= ; + if(rc =3D=3D -1) { + fprintf(stderr, "select() error\n"); + goto test_cleanup; + } + else if(rc =3D=3D 0) { + fprintf(stderr, "select() timeout!\n"); + goto test_cleanup; + } + else { + while(CURLM_CALL_MULTI_PERFORM =3D=3D + curl_multi_perform(mhandle, &still_running)); + } + } + +test_cleanup: + if(mhandle) + curl_multi_cleanup(mhandle); + if(handle) + curl_easy_cleanup(handle); + curl_global_cleanup(); + return res; +} diff --git a/tests/libtest/lib576.c b/tests/libtest/lib576.c new file mode 100644 index 0000000..7f2c7ae --- /dev/null +++ b/tests/libtest/lib576.c @@ -0,0 +1,107 @@ +/**********************************************************************= ******* + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + */ + +#include "test.h" +#include "testutil.h" +#include "memdebug.h" + +typedef struct { + int remains; + int print_content; +} chunk_data_t; + +long chunk_bgn(const struct curl_fileinfo *finfo, void *ptr, int remain= s); +long chunk_end(void *ptr); + +long chunk_bgn(const struct curl_fileinfo *finfo, void *ptr, int remain= s) +{ + chunk_data_t *ch_d =3D ptr; + ch_d->remains =3D remains; + + printf("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n"); + printf("Remains: %d\n", remains); + printf("Filename: %s\n", finfo->filename); + if(finfo->strings.perm) { + printf("Permissions: %s", finfo->strings.perm); + if(finfo->flags & CURLFINFOFLAG_KNOWN_PERM) + printf(" (parsed =3D> %o)", finfo->perm); + printf("\n"); + } + printf("Size: %lldB\n", (long long int)finfo->size); + if(finfo->strings.user) + printf("User: %s\n", finfo->strings.user); + if(finfo->strings.group) + printf("Group: %s\n", finfo->strings.group); + if(finfo->strings.time) + printf("Time: %s\n", finfo->strings.time); + printf("Filetype: "); + switch(finfo->filetype) { + case CURLFILETYPE_FILE: + printf("regular file\n"); + break; + case CURLFILETYPE_DIRECTORY: + printf("directory\n"); + break; + case CURLFILETYPE_SYMLINK: + printf("symlink\n"); + printf("Target: %s\n", finfo->strings.target); + break; + default: + printf("other type\n"); + break; + } + if(finfo->filetype =3D=3D CURLFILETYPE_FILE) { + ch_d->print_content =3D 1; + printf("Content:\n-------------------------------------------------= ------------\n"); + } + if(strcmp(finfo->filename, "someothertext.txt") =3D=3D 0) { + printf("# THIS CONTENT WAS SKIPPED IN CHUNK_BGN CALLBACK #\n"); + return CURL_CHUNK_BGN_FUNC_SKIP; + } + return CURL_CHUNK_BGN_FUNC_OK; +} + +long chunk_end(void *ptr) +{ + chunk_data_t *ch_d =3D ptr; + if(ch_d->print_content) { + ch_d->print_content =3D 0; + printf("-----------------------------------------------------------= --\n"); + } + if(ch_d->remains =3D=3D 1) + printf("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n"); + return CURL_CHUNK_END_FUNC_OK; +} + +int test(char *URL) +{ + CURL *handle =3D NULL; + CURLcode res =3D 0; + chunk_data_t chunk_data =3D {0,0}; + curl_global_init(CURL_GLOBAL_ALL); + handle =3D curl_easy_init(); + if(!handle) { + res =3D CURLE_OUT_OF_MEMORY; + goto test_cleanup; + } + + test_setopt(handle, CURLOPT_URL, URL); + test_setopt(handle, CURLOPT_WILDCARDMATCH, 1L); + test_setopt(handle, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn); + test_setopt(handle, CURLOPT_CHUNK_END_FUNCTION, chunk_end); + test_setopt(handle, CURLOPT_CHUNK_DATA, &chunk_data); + + res =3D curl_easy_perform(handle); + +test_cleanup: + if(handle) + curl_easy_cleanup(handle); + curl_global_cleanup(); + return res; +} diff --git a/tests/libtest/lib577.c b/tests/libtest/lib577.c new file mode 100644 index 0000000..8b434f8 --- /dev/null +++ b/tests/libtest/lib577.c @@ -0,0 +1,217 @@ +/**********************************************************************= ******* + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + */ + +#include "test.h" + +#include "memdebug.h" + +#include "curl_fnmatch.h" + +#define MATCH CURL_FNMATCH_MATCH +#define NOMATCH CURL_FNMATCH_NOMATCH +#define ERROR CURL_FNMATCH_FAIL + +#define MAX_PATTERN_L 100 +#define MAX_STRING_L 100 + +struct testcase { + char pattern[MAX_PATTERN_L]; + char string[MAX_STRING_L]; + int result; +}; + +static const struct testcase tests[] =3D { + /* brackets syntax */ + { "\\[", "[", MATCH }, + { "[", "[", ERROR }, + { "[]", "[]", ERROR }, + { "[][]", "[", MATCH }, + { "[][]", "]", MATCH }, + { "[[]", "[", MATCH }, + { "[[[]", "[", MATCH }, + { "[[[[]", "[", MATCH }, + { "[[[[]", "[", MATCH }, + + { "[][[]", "]", MATCH }, + { "[][[[]", "[", MATCH }, + { "[[]", "]", NOMATCH }, + + { "[a-z]", "a", MATCH }, + { "[a-z]", "A", NOMATCH }, + { "?[a-z]", "?Z", NOMATCH }, + { "[A-Z]", "C", MATCH }, + { "[A-Z]", "c", NOMATCH }, + { "[0-9]", "7", MATCH }, + { "[7-8]", "7", MATCH }, + { "[7-]", "7", MATCH }, + { "[7-]", "-", MATCH }, + { "[7-]", "[", NOMATCH }, + { "[a-bA-F]", "F", MATCH }, + { "[a-bA-B9]", "9", MATCH }, + { "[a-bA-B98]", "8", MATCH }, + { "[a-bA-B98]", "C", NOMATCH }, + { "[a-bA-Z9]", "F", MATCH }, + { "[a-bA-Z9]ero*", "Zero chance.", MATCH }, + { "S[a-][x]opho*", "Saxophone", MATCH }, + { "S[a-][x]opho*", "SaXophone", NOMATCH }, + { "S[a-][x]*.txt", "S-x.txt", MATCH }, + { "[\\a-\\b]", "a", MATCH }, + { "[\\a-\\b]", "b", MATCH }, + { "[?*[][?*[][?*[]", "?*[", MATCH }, + { "[][?*-]", "]", MATCH }, + { "[][?*-]", "[", MATCH }, + { "[][?*-]", "?", MATCH }, + { "[][?*-]", "*", MATCH }, + { "[][?*-]", "-", MATCH }, + { "[]?*-]", "-", MATCH }, + { "?/b/c", "a/b/c", MATCH }, + { "^_{}~", "^_{}~", MATCH }, + { "!#%+,-./01234567889", "!#%+,-./01234567889", MATCH }, + { "PQRSTUVWXYZ]abcdefg", "PQRSTUVWXYZ]abcdefg", MATCH }, + { ":;=3D_at_ABCDEFGHIJKLMNO", ":;=3D_at_ABCDEFGHIJKLMNO", MATCH }, + + /* negate */ + { "[!a]", "b", MATCH }, + { "[!a]", "a", NOMATCH }, + { "[^a]", "b", MATCH }, + { "[^a]", "a", NOMATCH }, + { "[^a-z0-9A-Z]", "a", NOMATCH }, + { "[^a-z0-9A-Z]", "-", MATCH }, + { "curl[!a-z]lib", "curl lib", MATCH }, + { "curl[! ]lib", "curl lib", NOMATCH }, + { "[! ][ ]", " ", NOMATCH }, + { "[! ][ ]", "a ", MATCH }, + { "*[^a].t?t", "a.txt", NOMATCH }, + { "*[^a].t?t", "ba.txt", NOMATCH }, + { "*[^a].t?t", "ab.txt", MATCH }, + { "[!?*[]", "?", NOMATCH }, + { "[!!]", "!", NOMATCH }, + { "[!!]", "x", MATCH }, + + { "[[:alpha:]]", "a", MATCH }, + { "[[:alpha:]]", "9", NOMATCH }, + { "[[:alnum:]]", "a", MATCH }, + { "[[:alnum:]]", "[", NOMATCH }, + { "[[:alnum:]]", "]", NOMATCH }, + { "[[:alnum:]]", "9", MATCH }, + { "[[:digit:]]", "9", MATCH }, + { "[[:xdigit:]]", "9", MATCH }, + { "[[:xdigit:]]", "F", MATCH }, + { "[[:xdigit:]]", "G", NOMATCH }, + { "[[:upper:]]", "U", MATCH }, + { "[[:upper:]]", "u", NOMATCH }, + { "[[:lower:]]", "l", MATCH }, + { "[[:lower:]]", "L", NOMATCH }, + { "[[:print:]]", "L", MATCH }, + { "[[:print:]]", {'\10'}, NOMATCH }, + { "[[:print:]]", {'\10'}, NOMATCH }, + { "[[:space:]]", " ", MATCH }, + { "[[:space:]]", "x", NOMATCH }, + { "[[:graph:]]", " ", NOMATCH }, + { "[[:graph:]]", "x", MATCH }, + { "[[:blank:]]", {'\t'}, MATCH }, + { "[[:blank:]]", {' '}, MATCH }, + { "[[:blank:]]", {'\r'}, NOMATCH }, + { "[^[:blank:]]", {'\t'}, NOMATCH }, + { "[^[:print:]]", {'\10'}, MATCH }, + { "[[:lower:]][[:lower:]]", "ll", MATCH }, + + { "Curl[[:blank:]];-)", "Curl ;-)", MATCH }, + { "*[[:blank:]]*", " ", MATCH }, + { "*[[:blank:]]*", "", NOMATCH }, + { "*[[:blank:]]*", "hi, im_Pavel", MATCH }, + + /* common using */ + { "filename.dat", "filename.dat", MATCH }, + { "*curl*", "lets use curl!!", MATCH }, + { "filename.txt", "filename.dat", NOMATCH }, + { "*.txt", "text.txt", MATCH }, + { "*.txt", "a.txt", MATCH }, + { "*.txt", ".txt", MATCH }, + { "*.txt", "txt", NOMATCH }, + { "??.txt", "99.txt", MATCH }, + { "??.txt", "a99.txt", NOMATCH }, + { "?.???", "a.txt", MATCH }, + { "*.???", "somefile.dat", MATCH }, + { "*.???", "photo.jpeg", NOMATCH }, + { ".*", ".htaccess", MATCH }, + { ".*", ".", MATCH }, + { ".*", "..", MATCH }, + + /* many stars =3D> one star */ + { "**.txt", "text.txt", MATCH }, + { "***.txt", "t.txt", MATCH }, + { "****.txt", ".txt", MATCH }, + + /* empty string or pattern */ + { "", "", MATCH } , + { "", "hello", NOMATCH }, + { "file", "", NOMATCH }, + { "?", "", NOMATCH }, + { "*", "", MATCH }, + { "x", "", NOMATCH }, + + /* backslash */ + { "\\", "\\", ERROR }, + { "\\\\", "\\", MATCH }, + { "\\\\", "\\\\", NOMATCH }, + { "\\?", "?", MATCH }, + { "\\*", "*", MATCH }, + { "?.txt", "?.txt", MATCH }, + { "*.txt", "*.txt", MATCH }, + { "\\?.txt", "?.txt", MATCH }, + { "\\*.txt", "*.txt", MATCH }, + { "\\?.txt", "x.txt", NOMATCH }, + { "\\*.txt", "x.txt", NOMATCH }, + { "\\*\\\\.txt", "*\\.txt", MATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cc\\cc*cc", MATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cccc", NOMATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cc\\cc*cc", MATCH }, + { "*\\?*\\**", "cc?c*c", MATCH }, + { "*\\?*\\**curl*", "cc?c*curl", MATCH }, + { "*\\?*\\**", "cc?cc", NOMATCH }, + { "\\\"\\$\\&\\'\\(\\)", "\"$&'()", MATCH }, + { "\\*\\?\\[\\\\\\`\\|", "*?[\\`|", MATCH }, + { "[\\a\\b]c", "ac", MATCH }, + { "[\\a\\b]c", "bc", MATCH }, + { "[\\a\\b]d", "bc", NOMATCH }, + { "[a-bA-B\\?]", "?", MATCH }, + { "cu[a-ab-b\\r]l", "curl", MATCH }, + { "[\\a-z]", "c", MATCH }, + + { "?*?*?.*?*", "abc.c", MATCH }, + { "?*?*?.*?*", "abcc", NOMATCH }, + { "?*?*?.*?*", "abc.", NOMATCH }, + { "?*?*?.*?*", "abc.c++", MATCH }, + { "?*?*?.*?*", "abcdef.c++", MATCH }, + { "?*?*?.?", "abcdef.c", MATCH }, + { "?*?*?.?", "abcdef.cd", NOMATCH }, + + { "Lindm=C3=A4tarv", "Lindm=C3=A4tarv", MAT= CH }, + + { "", "", MATCH } +}; + + +int test(char *URL) +{ + int testnum =3D sizeof(tests) / sizeof(struct testcase); + int i, rc; + (void)URL; /* not used */ + printf("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D\n"); + for(i =3D 0; i < testnum; i++) { + rc =3D Curl_fnmatch(tests[i].pattern, tests[i].string); + if(rc !=3D tests[i].result) { + printf("Curl_fnmatch(\"%s\", \"%s\") should return %d (returns %d= )\n", + tests[i].pattern, tests[i].string, tests[i].result, rc); + } + } + printf("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D\n"); + return 0; +} -- = 1.7.1 ------------SfdLspeOyyI1Ljwe3WHAMX Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html ------------SfdLspeOyyI1Ljwe3WHAMX--Received on 2001-09-17