mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
FTP: WILDCARDMATCH/CHUNKING/FNMATCH added
This commit is contained in:
parent
04cb15ae9d
commit
0825cd80a6
@ -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.
|
resolver, timeouts will not occur while the name resolve takes place.
|
||||||
Consider building libcurl with c-ares support to enable asynchronous DNS
|
Consider building libcurl with c-ares support to enable asynchronous DNS
|
||||||
lookups, which enables nice timeouts for name resolves without signals.
|
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 to a
|
||||||
|
file name pattern. The pattern can be specified as part of the \fICURLOPT_URL\fP
|
||||||
|
option, using an fnmatch-like pattern (Shell Pattern Matching) in the last 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\fR 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 asterisk have
|
||||||
|
no special meaning in a bracket expression. Each bracket expression ends 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, \fBprint\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
|
.PP
|
||||||
.SH CALLBACK OPTIONS
|
.SH CALLBACK OPTIONS
|
||||||
.IP CURLOPT_WRITEFUNCTION
|
.IP CURLOPT_WRITEFUNCTION
|
||||||
@ -424,6 +472,43 @@ in 7.20.0)
|
|||||||
.IP CURLOPT_INTERLEAVEDATA
|
.IP CURLOPT_INTERLEAVEDATA
|
||||||
This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION\fP when
|
This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION\fP when
|
||||||
interleaved RTP data is received. (Added in 7.20.0)
|
interleaved RTP data is received. (Added in 7.20.0)
|
||||||
|
.IP CURLOPT_CHUNK_BGN_FUNCTION
|
||||||
|
Function pointer that should match the following prototype: \fBlong function
|
||||||
|
(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 transferred
|
||||||
|
(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 parameter
|
||||||
|
remains contains number of chunks remaining per the transfer. If the feature 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 or
|
||||||
|
\fICURL_CHUNK_BGN_FUNC_FAIL\fR to tell libcurl to stop if some error occurred.
|
||||||
|
.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 occurred.
|
||||||
|
.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 internally 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
|
.SH ERROR OPTIONS
|
||||||
.IP CURLOPT_ERRORBUFFER
|
.IP CURLOPT_ERRORBUFFER
|
||||||
Pass a char * to a buffer that the libcurl may store human readable error
|
Pass a char * to a buffer that the libcurl may store human readable error
|
||||||
|
@ -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)
|
Failed to load CRL file (Added in 7.19.0)
|
||||||
.IP "CURLE_SSL_ISSUER_ERROR (83)"
|
.IP "CURLE_SSL_ISSUER_ERROR (83)"
|
||||||
Issuer check failed (Added in 7.19.0)
|
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*"
|
.IP "CURLE_OBSOLETE*"
|
||||||
These error codes will never be returned. They were used in an old libcurl
|
These error codes will never be returned. They were used in an old libcurl
|
||||||
version and are currently unused.
|
version and are currently unused.
|
||||||
|
@ -198,6 +198,96 @@ typedef size_t (*curl_write_callback)(char *buffer,
|
|||||||
size_t nitems,
|
size_t nitems,
|
||||||
void *outstream);
|
void *outstream);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* enumeration of file types */
|
||||||
|
typedef enum {
|
||||||
|
CURLFILETYPE_FILE = 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_setopt(3) man
|
||||||
|
page for callbacks returning this structure -- some fields are mandatory,
|
||||||
|
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 before
|
||||||
|
download of an individual chunk started. Note that parameter "remains" 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 after
|
||||||
|
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 this
|
||||||
|
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 pattern */
|
||||||
|
#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 */
|
/* These are the return codes for the seek callbacks */
|
||||||
#define CURL_SEEKFUNC_OK 0
|
#define CURL_SEEKFUNC_OK 0
|
||||||
#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */
|
#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_FTP_PRET_FAILED, /* 84 - a PRET command failed */
|
||||||
CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */
|
CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */
|
||||||
CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Identifiers */
|
CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Identifiers */
|
||||||
|
CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
|
||||||
|
CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
|
||||||
|
|
||||||
CURL_LAST /* never use! */
|
CURL_LAST /* never use! */
|
||||||
} CURLcode;
|
} CURLcode;
|
||||||
@ -1322,6 +1414,23 @@ typedef enum {
|
|||||||
/* Let the application define a custom write method for RTP data */
|
/* Let the application define a custom write method for RTP data */
|
||||||
CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196),
|
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 */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ CSOURCES = 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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \
|
||||||
@ -18,6 +19,7 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
|
|||||||
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
strtoofft.h strerror.h inet_ntop.h curlx.h curl_memory.h setup.h \
|
||||||
|
410
lib/curl_fnmatch.c
Normal file
410
lib/curl_fnmatch.c
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#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 = 0,
|
||||||
|
CURLFNM_LOOP_BACKSLASH
|
||||||
|
} loop_state;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CURLFNM_SCHS_DEFAULT = 0,
|
||||||
|
CURLFNM_SCHS_MAYRANGE,
|
||||||
|
CURLFNM_SCHS_MAYRANGE2,
|
||||||
|
CURLFNM_SCHS_RIGHTBR,
|
||||||
|
CURLFNM_SCHS_RIGHTBRLEFTBR
|
||||||
|
} setcharset_state;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CURLFNM_PKW_INIT = 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 = CURLFNM_PKW_INIT;
|
||||||
|
#define KEYLEN 10
|
||||||
|
char keyword[KEYLEN] = { 0 };
|
||||||
|
int found = FALSE;
|
||||||
|
int i;
|
||||||
|
register unsigned char *p = *pattern;
|
||||||
|
for(i = 0; !found; i++) {
|
||||||
|
char c = *p++;
|
||||||
|
if(i >= KEYLEN)
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
switch(state) {
|
||||||
|
case CURLFNM_PKW_INIT:
|
||||||
|
if(ISALPHA(c) && ISLOWER(c))
|
||||||
|
keyword[i] = c;
|
||||||
|
else if(c == ':')
|
||||||
|
state = CURLFNM_PKW_DDOT;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case CURLFNM_PKW_DDOT:
|
||||||
|
if(c == ']')
|
||||||
|
found = TRUE;
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef KEYLEN
|
||||||
|
|
||||||
|
*pattern = p; /* move caller's pattern pointer */
|
||||||
|
if(strcmp(keyword, "digit") == 0)
|
||||||
|
charset[CURLFNM_DIGIT] = 1;
|
||||||
|
else if(strcmp(keyword, "alnum") == 0)
|
||||||
|
charset[CURLFNM_ALNUM] = 1;
|
||||||
|
else if(strcmp(keyword, "alpha") == 0)
|
||||||
|
charset[CURLFNM_ALPHA] = 1;
|
||||||
|
else if(strcmp(keyword, "xdigit") == 0)
|
||||||
|
charset[CURLFNM_XDIGIT] = 1;
|
||||||
|
else if(strcmp(keyword, "print") == 0)
|
||||||
|
charset[CURLFNM_PRINT] = 1;
|
||||||
|
else if(strcmp(keyword, "graph") == 0)
|
||||||
|
charset[CURLFNM_GRAPH] = 1;
|
||||||
|
else if(strcmp(keyword, "space") == 0)
|
||||||
|
charset[CURLFNM_SPACE] = 1;
|
||||||
|
else if(strcmp(keyword, "blank") == 0)
|
||||||
|
charset[CURLFNM_BLANK] = 1;
|
||||||
|
else if(strcmp(keyword, "upper") == 0)
|
||||||
|
charset[CURLFNM_UPPER] = 1;
|
||||||
|
else if(strcmp(keyword, "lower") == 0)
|
||||||
|
charset[CURLFNM_LOWER] = 1;
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
|
||||||
|
static int setcharset(unsigned char **p, unsigned char *charset)
|
||||||
|
{
|
||||||
|
setcharset_state state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
unsigned char rangestart = 0;
|
||||||
|
unsigned char lastchar = 0;
|
||||||
|
bool something_found = FALSE;
|
||||||
|
register unsigned char c;
|
||||||
|
for(;;) {
|
||||||
|
c = **p;
|
||||||
|
switch(state){
|
||||||
|
case CURLFNM_SCHS_DEFAULT:
|
||||||
|
if(ISALNUM(c)) { /* ASCII value */
|
||||||
|
rangestart = c;
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
state = CURLFNM_SCHS_MAYRANGE;
|
||||||
|
something_found = TRUE;
|
||||||
|
}
|
||||||
|
else if(c == ']') {
|
||||||
|
if(something_found)
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
else
|
||||||
|
something_found = TRUE;
|
||||||
|
state = CURLFNM_SCHS_RIGHTBR;
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else if(c == '[') {
|
||||||
|
char c2 = *((*p)+1);
|
||||||
|
if(c2 == ':') { /* there has to be a keyword */
|
||||||
|
(*p) += 2;
|
||||||
|
if(parsekeyword(p, charset)) {
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
something_found = TRUE;
|
||||||
|
}
|
||||||
|
else if(c == '?' || c == '*') {
|
||||||
|
something_found = TRUE;
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else if(c == '^' || c == '!') {
|
||||||
|
if(!something_found) {
|
||||||
|
if(charset[CURLFNM_NEGATE]) {
|
||||||
|
charset[c] = 1;
|
||||||
|
something_found = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
charset[CURLFNM_NEGATE] = 1; /* negate charset */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else if(c == '\\') {
|
||||||
|
c = *(++(*p));
|
||||||
|
if(ISPRINT((c))) {
|
||||||
|
something_found = TRUE;
|
||||||
|
state = CURLFNM_SCHS_MAYRANGE;
|
||||||
|
charset[c] = 1;
|
||||||
|
rangestart = c;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
else if(c == '\0') {
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
something_found = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CURLFNM_SCHS_MAYRANGE:
|
||||||
|
if(c == '-'){
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
lastchar = '-';
|
||||||
|
state = CURLFNM_SCHS_MAYRANGE2;
|
||||||
|
}
|
||||||
|
else if(c == '[') {
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
}
|
||||||
|
else if(ISALNUM(c)) {
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else if(c == '\\') {
|
||||||
|
c = *(++(*p));
|
||||||
|
if(isprint(c)) {
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
else if(c == ']') {
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
break;
|
||||||
|
case CURLFNM_SCHS_MAYRANGE2:
|
||||||
|
if(c == '\\') {
|
||||||
|
c = *(++(*p));
|
||||||
|
if(!ISPRINT(c))
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
if(c == ']') {
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
}
|
||||||
|
else if(c == '\\') {
|
||||||
|
c = *(++(*p));
|
||||||
|
if(ISPRINT(c)) {
|
||||||
|
charset[c] = 1;
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
if(c >= rangestart) {
|
||||||
|
if((ISLOWER(c) && ISLOWER(rangestart)) ||
|
||||||
|
(ISDIGIT(c) && ISDIGIT(rangestart)) ||
|
||||||
|
(ISUPPER(c) && ISUPPER(rangestart))) {
|
||||||
|
charset[lastchar] = 0;
|
||||||
|
rangestart++;
|
||||||
|
while(rangestart++ <= c)
|
||||||
|
charset[rangestart-1] = 1;
|
||||||
|
(*p)++;
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CURLFNM_SCHS_RIGHTBR:
|
||||||
|
if(c == '[') {
|
||||||
|
state = CURLFNM_SCHS_RIGHTBRLEFTBR;
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
else if(c == ']') {
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
}
|
||||||
|
else if(c == '\0') {
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
else if(ISPRINT(c)){
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
break;
|
||||||
|
case CURLFNM_SCHS_RIGHTBRLEFTBR:
|
||||||
|
if(c == ']') {
|
||||||
|
return SETCHARSET_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state = CURLFNM_SCHS_DEFAULT;
|
||||||
|
charset[c] = 1;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SETCHARSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int loop(const unsigned char *pattern, const unsigned char *string)
|
||||||
|
{
|
||||||
|
loop_state state = CURLFNM_LOOP_DEFAULT;
|
||||||
|
register unsigned char *p = (unsigned char *)pattern;
|
||||||
|
register unsigned char *s = (unsigned char *)string;
|
||||||
|
unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
switch(state) {
|
||||||
|
case CURLFNM_LOOP_DEFAULT:
|
||||||
|
if(*p == '*') {
|
||||||
|
while(*(p+1) == '*') /* eliminate multiple stars */
|
||||||
|
p++;
|
||||||
|
if(*s == '\0' && *(p+1) == '\0')
|
||||||
|
return CURL_FNMATCH_MATCH;
|
||||||
|
rc = loop(p + 1, s); /* *.txt matches .txt <=> .txt matches .txt */
|
||||||
|
if(rc == 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 == '?') {
|
||||||
|
if(ISPRINT(*s)) {
|
||||||
|
s++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
else if(*s == '\0')
|
||||||
|
return CURL_FNMATCH_NOMATCH;
|
||||||
|
else
|
||||||
|
return CURL_FNMATCH_FAIL; /* cannot deal with other character */
|
||||||
|
}
|
||||||
|
else if(*p == '\0') {
|
||||||
|
if(*s == '\0')
|
||||||
|
return CURL_FNMATCH_MATCH;
|
||||||
|
else
|
||||||
|
return CURL_FNMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
else if(*p == '\\') {
|
||||||
|
state = CURLFNM_LOOP_BACKSLASH;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
else if(*p == '[') {
|
||||||
|
unsigned char *pp = p+1; /* cannot handle with pointer to register */
|
||||||
|
if(setcharset(&pp, charset)) {
|
||||||
|
bool found = FALSE;
|
||||||
|
if(charset[(unsigned int)*s])
|
||||||
|
found = TRUE;
|
||||||
|
else if(charset[CURLFNM_ALNUM])
|
||||||
|
found = ISALNUM(*s);
|
||||||
|
else if(charset[CURLFNM_ALPHA])
|
||||||
|
found = ISALPHA(*s);
|
||||||
|
else if(charset[CURLFNM_DIGIT])
|
||||||
|
found = ISDIGIT(*s);
|
||||||
|
else if(charset[CURLFNM_XDIGIT])
|
||||||
|
found = ISXDIGIT(*s);
|
||||||
|
else if(charset[CURLFNM_PRINT])
|
||||||
|
found = ISPRINT(*s);
|
||||||
|
else if(charset[CURLFNM_SPACE])
|
||||||
|
found = ISSPACE(*s);
|
||||||
|
else if(charset[CURLFNM_UPPER])
|
||||||
|
found = ISUPPER(*s);
|
||||||
|
else if(charset[CURLFNM_LOWER])
|
||||||
|
found = ISLOWER(*s);
|
||||||
|
else if(charset[CURLFNM_BLANK])
|
||||||
|
found = ISBLANK(*s);
|
||||||
|
else if(charset[CURLFNM_GRAPH])
|
||||||
|
found = ISGRAPH(*s);
|
||||||
|
|
||||||
|
if(charset[CURLFNM_NEGATE])
|
||||||
|
found = !found;
|
||||||
|
|
||||||
|
if(found) {
|
||||||
|
p = pp+1;
|
||||||
|
s++;
|
||||||
|
memset(charset, 0, CURLFNM_CHSET_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return CURL_FNMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return CURL_FNMATCH_FAIL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(*p++ != *s++)
|
||||||
|
return CURL_FNMATCH_NOMATCH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CURLFNM_LOOP_BACKSLASH:
|
||||||
|
if(ISPRINT(*p)) {
|
||||||
|
if(*p++ == *s++)
|
||||||
|
state = 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);
|
||||||
|
}
|
44
lib/curl_fnmatch.h
Normal file
44
lib/curl_fnmatch.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef HEADER_CURL_FNMATCH_H
|
||||||
|
#define HEADER_CURL_FNMATCH_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#define CURL_FNMATCH_MATCH 0
|
||||||
|
#define CURL_FNMATCH_NOMATCH 1
|
||||||
|
#define CURL_FNMATCH_FAIL 2
|
||||||
|
|
||||||
|
/* default pattern matching function
|
||||||
|
* =================================
|
||||||
|
* Implemented with recursive backtracking, if you want to use Curl_fnmatch,
|
||||||
|
* 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, space
|
||||||
|
* and upper (use as "[[:alnum:]]")
|
||||||
|
*/
|
||||||
|
int Curl_fnmatch(const char *pattern, const char *string);
|
||||||
|
|
||||||
|
#endif /* HEADER_CURL_FNMATCH_H */
|
66
lib/fileinfo.c
Normal file
66
lib/fileinfo.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "strdup.h"
|
||||||
|
#include "fileinfo.h"
|
||||||
|
|
||||||
|
struct curl_fileinfo *Curl_fileinfo_alloc(void)
|
||||||
|
{
|
||||||
|
struct curl_fileinfo *tmp = 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 = 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 = malloc(sizeof(struct curl_fileinfo));
|
||||||
|
if(!ptr)
|
||||||
|
return NULL;
|
||||||
|
*ptr = *src;
|
||||||
|
|
||||||
|
ptr->b_data = 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;
|
||||||
|
}
|
||||||
|
}
|
33
lib/fileinfo.h
Normal file
33
lib/fileinfo.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef __FILEINFO_H
|
||||||
|
#define __FILEINFO_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#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 */
|
261
lib/ftp.c
261
lib/ftp.c
@ -71,6 +71,8 @@
|
|||||||
#include "http.h" /* for HTTP proxy tunnel stuff */
|
#include "http.h" /* for HTTP proxy tunnel stuff */
|
||||||
#include "socks.h"
|
#include "socks.h"
|
||||||
#include "ftp.h"
|
#include "ftp.h"
|
||||||
|
#include "fileinfo.h"
|
||||||
|
#include "ftplistparser.h"
|
||||||
|
|
||||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||||
#include "krb4.h"
|
#include "krb4.h"
|
||||||
@ -110,6 +112,7 @@
|
|||||||
#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
|
#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void Curl_ftp_wc_data_dtor(void *ptr);
|
||||||
/* Local API functions */
|
/* Local API functions */
|
||||||
static CURLcode ftp_sendquote(struct connectdata *conn,
|
static CURLcode ftp_sendquote(struct connectdata *conn,
|
||||||
struct curl_slist *quote);
|
struct curl_slist *quote);
|
||||||
@ -144,6 +147,12 @@ static CURLcode ftp_doing(struct connectdata *conn,
|
|||||||
bool *dophase_done);
|
bool *dophase_done);
|
||||||
static CURLcode ftp_setup_connection(struct connectdata * conn);
|
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: */
|
/* easy-to-use macro: */
|
||||||
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
|
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
|
||||||
return result
|
return result
|
||||||
@ -1469,8 +1478,14 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
|
|||||||
if(ftp->transfer != FTPTRANSFER_BODY)
|
if(ftp->transfer != FTPTRANSFER_BODY)
|
||||||
state(conn, FTP_STOP);
|
state(conn, FTP_STOP);
|
||||||
else {
|
else {
|
||||||
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
|
if(ftpc->known_filesize != -1) {
|
||||||
state(conn, FTP_RETR_SIZE);
|
Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
|
||||||
|
result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
|
||||||
|
state(conn, FTP_RETR_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FTP_STOR_PREQUOTE:
|
case FTP_STOR_PREQUOTE:
|
||||||
@ -2855,6 +2870,8 @@ static CURLcode ftp_init(struct connectdata *conn)
|
|||||||
if(TRUE == isBadFtpString(ftp->passwd))
|
if(TRUE == isBadFtpString(ftp->passwd))
|
||||||
return CURLE_URL_MALFORMAT;
|
return CURLE_URL_MALFORMAT;
|
||||||
|
|
||||||
|
conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3018,6 +3035,13 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
|
|||||||
if(ftpc->prevpath)
|
if(ftpc->prevpath)
|
||||||
free(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 = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the "raw" path */
|
/* get the "raw" path */
|
||||||
path = curl_easy_unescape(data, path_to_use, 0, NULL);
|
path = curl_easy_unescape(data, path_to_use, 0, NULL);
|
||||||
if(!path) {
|
if(!path) {
|
||||||
@ -3445,6 +3469,221 @@ CURLcode ftp_perform(struct connectdata *conn,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curl_ftp_wc_data_dtor(void *ptr)
|
||||||
|
{
|
||||||
|
struct ftp_wc_tmpdata *tmp = 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 = conn->data->state.path;
|
||||||
|
struct WildcardData *wildcard = &(conn->data->wildcard);
|
||||||
|
CURLcode ret = CURLE_OK;
|
||||||
|
struct ftp_wc_tmpdata *ftp_tmp;
|
||||||
|
|
||||||
|
last_slash = strrchr(conn->data->state.path, '/');
|
||||||
|
if(last_slash) {
|
||||||
|
last_slash++;
|
||||||
|
if(last_slash[0] == '\0') {
|
||||||
|
wildcard->state = CURLWC_CLEAN;
|
||||||
|
ret = ftp_parse_url_path(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wildcard->pattern = strdup(last_slash);
|
||||||
|
if (!wildcard->pattern)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
last_slash[0] = '\0'; /* cut file from path */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { /* there is only 'wildcard pattern' or nothing */
|
||||||
|
if(path[0]) {
|
||||||
|
wildcard->pattern = strdup(path);
|
||||||
|
if (!wildcard->pattern)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
path[0] = '\0';
|
||||||
|
}
|
||||||
|
else { /* only list */
|
||||||
|
conn->data->set.wildcardmatch = 0L;
|
||||||
|
ret = ftp_parse_url_path(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* program continues only if URL is not ending with slash, allocate needed
|
||||||
|
resources for wildcard transfer */
|
||||||
|
|
||||||
|
/* allocate ftp protocol specific temporary wildcard data */
|
||||||
|
ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
|
||||||
|
if(!ftp_tmp) {
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INITIALIZE parselist structure */
|
||||||
|
ftp_tmp->parser = ftp_parselist_data_alloc();
|
||||||
|
if(!ftp_tmp->parser)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
|
||||||
|
wildcard->tmp_dtor = Curl_ftp_wc_data_dtor;
|
||||||
|
|
||||||
|
/* wildcard does not support NOCWD option (assert it?) */
|
||||||
|
if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
|
||||||
|
conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
|
||||||
|
|
||||||
|
/* try to parse ftp url */
|
||||||
|
ret = ftp_parse_url_path(conn);
|
||||||
|
if(ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* backup old write_function */
|
||||||
|
ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
|
||||||
|
/* parsing write function (callback included directly from ftplistparser.c) */
|
||||||
|
conn->data->set.fwrite_func = ftp_parselist;
|
||||||
|
/* backup old file descriptor */
|
||||||
|
ftp_tmp->backup.file_descriptor = conn->data->set.out;
|
||||||
|
/* let the writefunc callback know what curl pointer is working with */
|
||||||
|
conn->data->set.out = conn;
|
||||||
|
|
||||||
|
wildcard->path = 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 = &conn->proto.ftpc;
|
||||||
|
struct WildcardData *wildcard = &(conn->data->wildcard);
|
||||||
|
struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
|
||||||
|
char *tmp_path;
|
||||||
|
CURLcode ret = CURLE_OK;
|
||||||
|
long userresponse = 0;
|
||||||
|
switch (wildcard->state) {
|
||||||
|
case CURLWC_INIT:
|
||||||
|
ret = init_wc_data(conn);
|
||||||
|
if(wildcard->state == CURLWC_CLEAN)
|
||||||
|
/* only listing! */
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CURLWC_MATCHING:
|
||||||
|
/* In this state is LIST response successfully parsed, so lets restore
|
||||||
|
previous WRITEFUNCTION callback and WRITEDATA pointer */
|
||||||
|
ftp_tmp = wildcard->tmp;
|
||||||
|
conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
|
||||||
|
conn->data->set.out = ftp_tmp->backup.file_descriptor;
|
||||||
|
wildcard->state = CURLWC_DOWNLOADING;
|
||||||
|
|
||||||
|
if(ftp_parselist_geterror(ftp_tmp->parser)) {
|
||||||
|
/* error found in LIST parsing */
|
||||||
|
wildcard->state = CURLWC_CLEAN;
|
||||||
|
return wc_statemach(conn);
|
||||||
|
}
|
||||||
|
else if(wildcard->filelist->size == 0) {
|
||||||
|
/* no corresponding file */
|
||||||
|
wildcard->state = CURLWC_CLEAN;
|
||||||
|
return CURLE_REMOTE_FILE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
ret = wc_statemach(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CURLWC_DOWNLOADING: {
|
||||||
|
/* filelist has at least one file, lets get first one */
|
||||||
|
struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
|
||||||
|
tmp_path = malloc(strlen(conn->data->state.path) +
|
||||||
|
strlen(finfo->filename) + 1);
|
||||||
|
if(!tmp_path) {
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_path[0] = 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 = tmp_path;
|
||||||
|
conn->data->state.path = tmp_path;
|
||||||
|
|
||||||
|
infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
|
||||||
|
if(conn->data->set.chunk_bgn) {
|
||||||
|
userresponse = 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 = CURLWC_SKIP;
|
||||||
|
return wc_statemach(conn);
|
||||||
|
break;
|
||||||
|
case CURL_CHUNK_BGN_FUNC_FAIL:
|
||||||
|
return CURLE_CHUNK_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(finfo->filetype != CURLFILETYPE_FILE) {
|
||||||
|
wildcard->state = CURLWC_SKIP;
|
||||||
|
return wc_statemach(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
|
||||||
|
ftpc->known_filesize = finfo->size;
|
||||||
|
|
||||||
|
ret = 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, NULL);
|
||||||
|
|
||||||
|
if(wildcard->filelist->size == 0) { /* remains only one file to down. */
|
||||||
|
wildcard->state = 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, NULL);
|
||||||
|
wildcard->state = (wildcard->filelist->size == 0) ?
|
||||||
|
CURLWC_CLEAN : CURLWC_DOWNLOADING;
|
||||||
|
ret = wc_statemach(conn);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case CURLWC_CLEAN:
|
||||||
|
ret = CURLE_OK;
|
||||||
|
if(ftp_tmp) {
|
||||||
|
ret = ftp_parselist_geterror(ftp_tmp->parser);
|
||||||
|
}
|
||||||
|
wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CURLWC_DONE:
|
||||||
|
case CURLWC_ERROR:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
*
|
*
|
||||||
* ftp_do()
|
* ftp_do()
|
||||||
@ -3471,9 +3710,21 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done)
|
|||||||
if(retcode)
|
if(retcode)
|
||||||
return retcode;
|
return retcode;
|
||||||
|
|
||||||
retcode = ftp_parse_url_path(conn);
|
if(conn->data->set.wildcardmatch) {
|
||||||
if(retcode)
|
retcode = wc_statemach(conn);
|
||||||
return retcode;
|
if(conn->data->wildcard.state == CURLWC_SKIP ||
|
||||||
|
conn->data->wildcard.state == 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 = ftp_parse_url_path(conn);
|
||||||
|
if(retcode)
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
|
|
||||||
retcode = ftp_regular_transfer(conn, done);
|
retcode = ftp_regular_transfer(conn, done);
|
||||||
|
|
||||||
|
13
lib/ftp.h
13
lib/ftp.h
@ -79,6 +79,17 @@ typedef enum {
|
|||||||
FTP_LAST /* never used */
|
FTP_LAST /* never used */
|
||||||
} ftpstate;
|
} 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 {
|
typedef enum {
|
||||||
FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
|
FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
|
||||||
FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */
|
FTPFILE_NOCWD = 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 */
|
int count3; /* general purpose counter for the state machine */
|
||||||
ftpstate state; /* always use ftp.c:state() to change state! */
|
ftpstate state; /* always use ftp.c:state() to change state! */
|
||||||
char * server_os; /* The target server operating system. */
|
char * server_os; /* The target server operating system. */
|
||||||
|
curl_off_t known_filesize; /* file size is different from -1, if wildcard
|
||||||
|
LIST parsing was done and wc_statemach set it */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* HEADER_CURL_FTP_H */
|
#endif /* HEADER_CURL_FTP_H */
|
||||||
|
1009
lib/ftplistparser.c
Normal file
1009
lib/ftplistparser.c
Normal file
File diff suppressed because it is too large
Load Diff
38
lib/ftplistparser.h
Normal file
38
lib/ftplistparser.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef __FTPLISTPARSER_H_
|
||||||
|
#define __FTPLISTPARSER_H_
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
/* WRITEFUNCTION callback for parsing LIST responses */
|
||||||
|
size_t ftp_parselist(char *buffer, size_t size, size_t nmemb, void *connptr);
|
||||||
|
|
||||||
|
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_ */
|
38
lib/multi.c
38
lib/multi.c
@ -1128,6 +1128,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
|
|
||||||
if(CURLE_OK == easy->result) {
|
if(CURLE_OK == easy->result) {
|
||||||
if(!dophase_done) {
|
if(!dophase_done) {
|
||||||
|
/* some steps needed for wildcard matching */
|
||||||
|
if(easy->easy_handle->set.wildcardmatch) {
|
||||||
|
struct WildcardData *wc = &easy->easy_handle->wildcard;
|
||||||
|
if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
|
||||||
|
/* skip some states if it is important */
|
||||||
|
Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
|
||||||
|
multistate(easy, CURLM_STATE_DONE);
|
||||||
|
result = CURLM_CALL_MULTI_PERFORM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* DO was not completed in one function call, we must continue
|
/* DO was not completed in one function call, we must continue
|
||||||
DOING... */
|
DOING... */
|
||||||
multistate(easy, CURLM_STATE_DOING);
|
multistate(easy, CURLM_STATE_DOING);
|
||||||
@ -1338,7 +1349,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
easy->easy_conn->writechannel_inuse = FALSE;
|
easy->easy_conn->writechannel_inuse = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(easy->result) {
|
if(easy->result) {
|
||||||
/* The transfer phase returned error, we mark the connection to get
|
/* The transfer phase returned error, we mark the connection to get
|
||||||
* closed to prevent being re-used. This is because we can't possibly
|
* closed to prevent being re-used. This is because we can't possibly
|
||||||
* know if the connection is in a good shape or not now. Unless it is
|
* know if the connection is in a good shape or not now. Unless it is
|
||||||
@ -1449,6 +1460,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
easy->easy_conn = NULL;
|
easy->easy_conn = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(easy->easy_handle->set.wildcardmatch) {
|
||||||
|
if(easy->easy_handle->wildcard.state != CURLWC_DONE) {
|
||||||
|
/* if a wildcard is set and we are not ending -> lets start again
|
||||||
|
with CURLM_STATE_INIT */
|
||||||
|
result = CURLM_CALL_MULTI_PERFORM;
|
||||||
|
multistate(easy, CURLM_STATE_INIT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* after we have DONE what we're supposed to do, go COMPLETED, and
|
/* after we have DONE what we're supposed to do, go COMPLETED, and
|
||||||
it doesn't matter what the Curl_done() returned! */
|
it doesn't matter what the Curl_done() returned! */
|
||||||
multistate(easy, CURLM_STATE_COMPLETED);
|
multistate(easy, CURLM_STATE_COMPLETED);
|
||||||
@ -1550,11 +1571,26 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
|||||||
easy=multi->easy.next;
|
easy=multi->easy.next;
|
||||||
while(easy != &multi->easy) {
|
while(easy != &multi->easy) {
|
||||||
CURLMcode result;
|
CURLMcode result;
|
||||||
|
struct WildcardData *wc = &easy->easy_handle->wildcard;
|
||||||
|
|
||||||
|
if(easy->easy_handle->set.wildcardmatch) {
|
||||||
|
if(!wc->filelist) {
|
||||||
|
CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
|
||||||
|
if(ret)
|
||||||
|
return CURLM_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
result = multi_runsingle(multi, easy);
|
result = multi_runsingle(multi, easy);
|
||||||
while (CURLM_CALL_MULTI_PERFORM == result);
|
while (CURLM_CALL_MULTI_PERFORM == result);
|
||||||
|
|
||||||
|
if(easy->easy_handle->set.wildcardmatch) {
|
||||||
|
/* destruct wildcard structures if it is needed */
|
||||||
|
if(wc->state == CURLWC_DONE || result)
|
||||||
|
Curl_wildcard_dtor(wc);
|
||||||
|
}
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
returncode = result;
|
returncode = result;
|
||||||
|
|
||||||
|
@ -275,6 +275,12 @@ curl_easy_strerror(CURLcode error)
|
|||||||
case CURLE_RTSP_SESSION_ERROR:
|
case CURLE_RTSP_SESSION_ERROR:
|
||||||
return "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 */
|
/* error codes not used by current libcurl */
|
||||||
case CURLE_OBSOLETE4:
|
case CURLE_OBSOLETE4:
|
||||||
case CURLE_OBSOLETE10:
|
case CURLE_OBSOLETE10:
|
||||||
|
@ -2005,12 +2005,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
|
|||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static CURLcode Curl_do_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;
|
CURLcode res;
|
||||||
CURLcode res2;
|
CURLcode res2;
|
||||||
@ -2045,6 +2040,15 @@ CURLcode Curl_perform(struct SessionHandle *data)
|
|||||||
res = Curl_do(&conn, &do_done);
|
res = Curl_do(&conn, &do_done);
|
||||||
|
|
||||||
if(res == CURLE_OK) {
|
if(res == CURLE_OK) {
|
||||||
|
if(conn->data->set.wildcardmatch) {
|
||||||
|
if(conn->data->wildcard.state == CURLWC_DONE ||
|
||||||
|
conn->data->wildcard.state == CURLWC_SKIP) {
|
||||||
|
/* keep connection open for application to use the socket */
|
||||||
|
conn->bits.close = FALSE;
|
||||||
|
res = Curl_done(&conn, CURLE_OK, FALSE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
res = Transfer(conn); /* now fetch that URL please */
|
res = Transfer(conn); /* now fetch that URL please */
|
||||||
if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
|
if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
|
||||||
bool retry = FALSE;
|
bool retry = FALSE;
|
||||||
@ -2161,6 +2165,39 @@ CURLcode Curl_perform(struct SessionHandle *data)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = Curl_wildcard_init(&data->wildcard);
|
||||||
|
if(res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = Curl_do_perform(data);
|
||||||
|
if(res) {
|
||||||
|
Curl_wildcard_dtor(&data->wildcard);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wildcard loop */
|
||||||
|
while(!res && data->wildcard.state != CURLWC_DONE)
|
||||||
|
res = Curl_do_perform(data);
|
||||||
|
|
||||||
|
Curl_wildcard_dtor(&data->wildcard);
|
||||||
|
|
||||||
|
/* wildcard download finished or failed */
|
||||||
|
data->wildcard.state = CURLWC_INIT;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_setup_transfer() is called to setup some basic properties for the
|
* Curl_setup_transfer() is called to setup some basic properties for the
|
||||||
* upcoming transfer.
|
* upcoming transfer.
|
||||||
|
24
lib/url.c
24
lib/url.c
@ -770,6 +770,10 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
|
|||||||
res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
|
res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
set->wildcardmatch = 0L;
|
||||||
|
set->chunk_bgn = ZERO_NULL;
|
||||||
|
set->chunk_end = ZERO_NULL;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,6 +842,9 @@ CURLcode Curl_open(struct SessionHandle **curl)
|
|||||||
data->progress.flags |= PGRS_HIDE;
|
data->progress.flags |= PGRS_HIDE;
|
||||||
data->state.current_speed = -1; /* init to negative == impossible */
|
data->state.current_speed = -1; /* init to negative == impossible */
|
||||||
|
|
||||||
|
data->wildcard.state = CURLWC_INIT;
|
||||||
|
data->wildcard.filelist = NULL;
|
||||||
|
data->set.fnmatch = ZERO_NULL;
|
||||||
/* This no longer creates a connection cache here. It is instead made on
|
/* This no longer creates a connection cache here. It is instead made on
|
||||||
the first call to curl_easy_perform() or when the handle is added to a
|
the first call to curl_easy_perform() or when the handle is added to a
|
||||||
multi stack. */
|
multi stack. */
|
||||||
@ -2455,6 +2462,23 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
|||||||
/* Set the user defined RTP write function */
|
/* Set the user defined RTP write function */
|
||||||
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
|
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CURLOPT_WILDCARDMATCH:
|
||||||
|
data->set.wildcardmatch = va_arg(param, long);
|
||||||
|
break;
|
||||||
|
case CURLOPT_CHUNK_BGN_FUNCTION:
|
||||||
|
data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
|
||||||
|
break;
|
||||||
|
case CURLOPT_CHUNK_END_FUNCTION:
|
||||||
|
data->set.chunk_end = va_arg(param, curl_chunk_end_callback);
|
||||||
|
break;
|
||||||
|
case CURLOPT_FNMATCH_FUNCTION:
|
||||||
|
data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
|
||||||
|
break;
|
||||||
|
case CURLOPT_CHUNK_DATA:
|
||||||
|
data->wildcard.customptr = va_arg(param, void *);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* unknown tag and its companion, just ignore: */
|
/* unknown tag and its companion, just ignore: */
|
||||||
result = CURLE_FAILED_INIT; /* correct this */
|
result = CURLE_FAILED_INIT; /* correct this */
|
||||||
|
@ -156,6 +156,7 @@
|
|||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "rtsp.h"
|
#include "rtsp.h"
|
||||||
|
#include "wildcard.h"
|
||||||
|
|
||||||
#ifdef HAVE_GSSAPI
|
#ifdef HAVE_GSSAPI
|
||||||
# ifdef HAVE_GSSGNU
|
# ifdef HAVE_GSSGNU
|
||||||
@ -1419,6 +1420,12 @@ struct UserDefined {
|
|||||||
/* Common RTSP header options */
|
/* Common RTSP header options */
|
||||||
Curl_RtspReq rtspreq; /* RTSP request type */
|
Curl_RtspReq rtspreq; /* RTSP request type */
|
||||||
long rtspversion; /* like httpversion, for RTSP */
|
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 corresponds
|
||||||
|
to pattern (e.g. if WILDCARDMATCH is on) */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Names {
|
struct Names {
|
||||||
@ -1460,6 +1467,7 @@ struct SessionHandle {
|
|||||||
struct Progress progress; /* for all the progress meter data */
|
struct Progress progress; /* for all the progress meter data */
|
||||||
struct UrlState state; /* struct for fields used for state info and
|
struct UrlState state; /* struct for fields used for state info and
|
||||||
other dynamic purposes */
|
other dynamic purposes */
|
||||||
|
struct WildcardData wildcard; /* wildcard download state info */
|
||||||
struct PureInfo info; /* stats, reports and info data */
|
struct PureInfo info; /* stats, reports and info data */
|
||||||
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
|
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
|
||||||
iconv_t outbound_cd; /* for translating to the network encoding */
|
iconv_t outbound_cd; /* for translating to the network encoding */
|
||||||
|
66
lib/wildcard.c
Normal file
66
lib/wildcard.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#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 = 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 = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wc->filelist) {
|
||||||
|
Curl_llist_destroy(wc->filelist, NULL);
|
||||||
|
wc->filelist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wc->path) {
|
||||||
|
free(wc->path);
|
||||||
|
wc->path = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wc->pattern) {
|
||||||
|
free(wc->pattern);
|
||||||
|
wc->pattern = NULL;
|
||||||
|
}
|
||||||
|
wc->customptr = NULL;
|
||||||
|
}
|
58
lib/wildcard.h
Normal file
58
lib/wildcard.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#ifndef __WILDCARD_H
|
||||||
|
#define __WILDCARD_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
/* list of wildcard process states */
|
||||||
|
typedef enum {
|
||||||
|
CURLWC_INIT = 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 == CURLWC_DONE wildcard loop 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 */
|
@ -60,6 +60,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
|
|||||||
test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079 \
|
test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079 \
|
||||||
test1080 test1081 test1082 test1083 test1084 test1085 test633 test634 \
|
test1080 test1081 test1082 test1083 test1084 test1085 test633 test634 \
|
||||||
test635 test636 test637 test558 test559 test1086 test1087 test1088 \
|
test635 test636 test637 test558 test559 test1086 test1087 test1088 \
|
||||||
|
test574 test575 test576 test577 test1113 test1114 \
|
||||||
test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \
|
test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \
|
||||||
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
|
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
|
||||||
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
|
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
|
||||||
|
71
tests/data/test1113
Normal file
71
tests/data/test1113
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<testcase>
|
||||||
|
<info>
|
||||||
|
<keywords>
|
||||||
|
FTP
|
||||||
|
wildcardmatch
|
||||||
|
ftplistparser
|
||||||
|
</keywords>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Server-side
|
||||||
|
<reply>
|
||||||
|
<data mode="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="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>
|
136
tests/data/test1114
Normal file
136
tests/data/test1114
Normal file
@ -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_FUNCTION (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="text">
|
||||||
|
=============================================================
|
||||||
|
Remains: 12
|
||||||
|
Filename: .
|
||||||
|
Size: 0B
|
||||||
|
Time: 04-27-10 05:12AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 11
|
||||||
|
Filename: ..
|
||||||
|
Size: 0B
|
||||||
|
Time: 04-23-10 03:12AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 10
|
||||||
|
Filename: chmod1
|
||||||
|
Size: 38B
|
||||||
|
Time: 01-11-10 10:00AM
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 444
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 9
|
||||||
|
Filename: chmod2
|
||||||
|
Size: 38B
|
||||||
|
Time: 02-01-10 08:00AM
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 666
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 8
|
||||||
|
Filename: chmod3
|
||||||
|
Size: 38B
|
||||||
|
Time: 02-01-10 08:00AM
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 777
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 7
|
||||||
|
Filename: chmod4
|
||||||
|
Size: 0B
|
||||||
|
Time: 05-04-10 04:31AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 6
|
||||||
|
Filename: chmod5
|
||||||
|
Size: 0B
|
||||||
|
Time: 05-04-10 04:31AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 5
|
||||||
|
Filename: empty_file.dat
|
||||||
|
Size: 0B
|
||||||
|
Time: 04-27-10 11:01AM
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 4
|
||||||
|
Filename: file.txt
|
||||||
|
Size: 35B
|
||||||
|
Time: 04-27-10 11:01AM
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This is content of file "file.txt"
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 3
|
||||||
|
Filename: .NeXT
|
||||||
|
Size: 0B
|
||||||
|
Time: 01-23-05 02:05AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
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 #
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 1
|
||||||
|
Filename: weirddir.txt
|
||||||
|
Size: 0B
|
||||||
|
Time: 04-23-10 03:12AM
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
</stdout>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
@ -45,7 +45,7 @@ EPSV
|
|||||||
TYPE I
|
TYPE I
|
||||||
SIZE 146
|
SIZE 146
|
||||||
RETR 146
|
RETR 146
|
||||||
CWD /nowhere/anywhere
|
CWD /
|
||||||
EPSV
|
EPSV
|
||||||
SIZE 146
|
SIZE 146
|
||||||
RETR 146
|
RETR 146
|
||||||
|
@ -34,7 +34,7 @@ CWD dir1
|
|||||||
EPSV
|
EPSV
|
||||||
TYPE I
|
TYPE I
|
||||||
STOR 149
|
STOR 149
|
||||||
CWD /nowhere/anywhere
|
CWD /
|
||||||
CWD dir2
|
CWD dir2
|
||||||
EPSV
|
EPSV
|
||||||
STOR 149
|
STOR 149
|
||||||
|
@ -53,7 +53,7 @@ TYPE I
|
|||||||
SIZE 539
|
SIZE 539
|
||||||
RETR 539
|
RETR 539
|
||||||
SYST
|
SYST
|
||||||
CWD /nowhere/anywhere
|
CWD /
|
||||||
EPSV
|
EPSV
|
||||||
TYPE A
|
TYPE A
|
||||||
LIST path/to/the/file/539./
|
LIST path/to/the/file/539./
|
||||||
|
71
tests/data/test574
Normal file
71
tests/data/test574
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<testcase>
|
||||||
|
<info>
|
||||||
|
<keywords>
|
||||||
|
FTP
|
||||||
|
wildcardmatch
|
||||||
|
ftplistparser
|
||||||
|
</keywords>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Server-side
|
||||||
|
<reply>
|
||||||
|
<data mode="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="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>
|
79
tests/data/test575
Normal file
79
tests/data/test575
Normal file
@ -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="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>
|
192
tests/data/test576
Normal file
192
tests/data/test576
Normal file
@ -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_FUNCTION (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="text">
|
||||||
|
=============================================================
|
||||||
|
Remains: 14
|
||||||
|
Filename: .
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 20480B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Apr 27 5:12
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 13
|
||||||
|
Filename: ..
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 20480B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Apr 23 3:12
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 12
|
||||||
|
Filename: chmod1
|
||||||
|
Permissions: r--r--r-- (parsed => 444)
|
||||||
|
Size: 38B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Jan 11 10:00
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 444
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 11
|
||||||
|
Filename: chmod2
|
||||||
|
Permissions: rw-rw-rw- (parsed => 666)
|
||||||
|
Size: 38B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Feb 1 8:00
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 666
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 10
|
||||||
|
Filename: chmod3
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 38B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Feb 1 8:00
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
This file should have permissions 777
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 9
|
||||||
|
Filename: chmod4
|
||||||
|
Permissions: --S--S--t (parsed => 7001)
|
||||||
|
Size: 4096B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: May 4 4:31
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 8
|
||||||
|
Filename: chmod5
|
||||||
|
Permissions: --s--s--T (parsed => 7110)
|
||||||
|
Size: 4096B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: May 4 4:31
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 7
|
||||||
|
Filename: empty_file.dat
|
||||||
|
Permissions: rw-r--r-- (parsed => 644)
|
||||||
|
Size: 0B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Apr 27 11:01
|
||||||
|
Filetype: regular file
|
||||||
|
Content:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 6
|
||||||
|
Filename: file.txt
|
||||||
|
Permissions: rw-r--r-- (parsed => 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"
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 5
|
||||||
|
Filename: link
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 0B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Jan 6 4:42
|
||||||
|
Filetype: symlink
|
||||||
|
Target: file.txt
|
||||||
|
=============================================================
|
||||||
|
Remains: 4
|
||||||
|
Filename: link_absolute
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 0B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Jan 6 4:45
|
||||||
|
Filetype: symlink
|
||||||
|
Target: /data/ftp/file.txt
|
||||||
|
=============================================================
|
||||||
|
Remains: 3
|
||||||
|
Filename: .NeXT
|
||||||
|
Permissions: rwxrwxrwx (parsed => 777)
|
||||||
|
Size: 4096B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Jan 23 2:05
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
Remains: 2
|
||||||
|
Filename: someothertext.txt
|
||||||
|
Permissions: rw-r--r-- (parsed => 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 #
|
||||||
|
-------------------------------------------------------------
|
||||||
|
=============================================================
|
||||||
|
Remains: 1
|
||||||
|
Filename: weirddir.txt
|
||||||
|
Permissions: rwxr-xrwx (parsed => 757)
|
||||||
|
Size: 4096B
|
||||||
|
User: ftp-default
|
||||||
|
Group: ftp-default
|
||||||
|
Time: Apr 23 3:12
|
||||||
|
Filetype: directory
|
||||||
|
=============================================================
|
||||||
|
</stdout>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
38
tests/data/test577
Normal file
38
tests/data/test577
Normal file
@ -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="text">
|
||||||
|
===========================
|
||||||
|
===========================
|
||||||
|
</stdout>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
266
tests/directories.pm
Normal file
266
tests/directories.pm
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
%file_chmod1 = (
|
||||||
|
'name' => 'chmod1',
|
||||||
|
'content' => "This file should have permissions 444\n",
|
||||||
|
'perm' => 'r--r--r--',
|
||||||
|
'time' => 'Jan 11 10:00',
|
||||||
|
'dostime' => '01-11-10 10:00AM',
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_chmod2 = (
|
||||||
|
'name' => 'chmod2',
|
||||||
|
'content' => "This file should have permissions 666\n",
|
||||||
|
'perm' => 'rw-rw-rw-',
|
||||||
|
'time' => 'Feb 1 8:00',
|
||||||
|
'dostime' => '02-01-10 08:00AM',
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_chmod3 = (
|
||||||
|
'name' => 'chmod3',
|
||||||
|
'content' => "This file should have permissions 777\n",
|
||||||
|
'perm' => 'rwxrwxrwx',
|
||||||
|
'time' => 'Feb 1 8:00',
|
||||||
|
'dostime' => '02-01-10 08:00AM',
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_chmod4 = (
|
||||||
|
'type' => 'd',
|
||||||
|
'name' => 'chmod4',
|
||||||
|
'content' => "This file should have permissions 001\n",
|
||||||
|
'perm' => '--S--S--t',
|
||||||
|
'time' => 'May 4 4:31',
|
||||||
|
'dostime' => '05-04-10 04:31AM'
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_chmod5 = (
|
||||||
|
'type' => 'd',
|
||||||
|
'name' => 'chmod5',
|
||||||
|
'content' => "This file should have permissions 110\n",
|
||||||
|
'perm' => '--s--s--T',
|
||||||
|
'time' => 'May 4 4:31',
|
||||||
|
'dostime' => '05-04-10 04:31AM'
|
||||||
|
);
|
||||||
|
|
||||||
|
%link_link = (
|
||||||
|
'type' => 'l',
|
||||||
|
'name' => 'link -> file.txt',
|
||||||
|
'size' => '8',
|
||||||
|
'perm' => 'rwxrwxrwx',
|
||||||
|
'time' => 'Jan 6 4:42'
|
||||||
|
);
|
||||||
|
|
||||||
|
%link_link_absolute = (
|
||||||
|
'type' => 'l',
|
||||||
|
'name' => 'link_absolute -> /data/ftp/file.txt',
|
||||||
|
'size' => '15',
|
||||||
|
'perm' => 'rwxrwxrwx',
|
||||||
|
'time' => 'Jan 6 4:45'
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_dot = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => ".",
|
||||||
|
'hlink' => "4",
|
||||||
|
'time' => "Apr 27 5:12",
|
||||||
|
'size' => "20480",
|
||||||
|
'dostime' => "04-27-10 05:12AM",
|
||||||
|
'perm' => "rwxrwxrwx"
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_ddot = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => "..",
|
||||||
|
'hlink' => "4",
|
||||||
|
'size' => "20480",
|
||||||
|
'time' => "Apr 23 3:12",
|
||||||
|
'dostime' => "04-23-10 03:12AM",
|
||||||
|
'perm' => "rwxrwxrwx"
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_weirddir_txt = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => "weirddir.txt",
|
||||||
|
'hlink' => "2",
|
||||||
|
'size' => "4096",
|
||||||
|
'time' => "Apr 23 3:12",
|
||||||
|
'dostime' => "04-23-10 03:12AM",
|
||||||
|
'perm' => "rwxr-xrwx"
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_UNIX = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => "UNIX",
|
||||||
|
'hlink' => "11",
|
||||||
|
'size' => "4096",
|
||||||
|
'time' => "Nov 01 2008",
|
||||||
|
'dostime' => "11-01-08 11:11AM",
|
||||||
|
'perm' => "rwx--x--x"
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_DOS = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => "DOS",
|
||||||
|
'hlink' => "11",
|
||||||
|
'size' => "4096",
|
||||||
|
'time' => "Nov 01 2008",
|
||||||
|
'dostime' => "11-01-08 11:11AM",
|
||||||
|
'perm' => "rwx--x--x"
|
||||||
|
);
|
||||||
|
|
||||||
|
%dir_dot_NeXT = (
|
||||||
|
'type' => "d",
|
||||||
|
'name' => ".NeXT",
|
||||||
|
'hlink' => "4",
|
||||||
|
'size' => "4096",
|
||||||
|
'time' => "Jan 23 2:05",
|
||||||
|
'dostime' => "01-23-05 02:05AM",
|
||||||
|
'perm' => "rwxrwxrwx"
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_empty_file_dat = (
|
||||||
|
'name' => "empty_file.dat",
|
||||||
|
'content' => "",
|
||||||
|
'perm' => "rw-r--r--",
|
||||||
|
'time' => "Apr 27 11:01",
|
||||||
|
'dostime' => "04-27-10 11:01AM"
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_file_txt = (
|
||||||
|
'name' => "file.txt",
|
||||||
|
'content' => "This is content of file \"file.txt\"\n",
|
||||||
|
'time' => "Apr 27 11:01",
|
||||||
|
'dostime' => "04-27-10 11:01AM",
|
||||||
|
'perm' => "rw-r--r--"
|
||||||
|
);
|
||||||
|
|
||||||
|
%file_someothertext_txt = (
|
||||||
|
'name' => "someothertext.txt",
|
||||||
|
'content' => "Some junk ;-) This file does not really exist.\n",
|
||||||
|
'time' => "Apr 27 11:01",
|
||||||
|
'dostime' => "04-27-10 11:01AM",
|
||||||
|
'perm' => "rw-r--r--"
|
||||||
|
);
|
||||||
|
|
||||||
|
%lists = (
|
||||||
|
'/fully_simulated/' => {
|
||||||
|
'files' => [ \%dir_dot, \%dir_ddot, \%dir_DOS, \%dir_UNIX ],
|
||||||
|
'eol' => "\r\n",
|
||||||
|
'type' => "unix"
|
||||||
|
},
|
||||||
|
'/fully_simulated/UNIX/' => {
|
||||||
|
'files' => [ \%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' => "\r\n",
|
||||||
|
'type' => 'unix'
|
||||||
|
},
|
||||||
|
'/fully_simulated/DOS/' => {
|
||||||
|
'files' => [ \%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_weirddir_txt ],
|
||||||
|
'eol' => "\r\n",
|
||||||
|
'type' => 'dos'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
sub ftp_createcontent($) {
|
||||||
|
my (%list) = @_;
|
||||||
|
|
||||||
|
$type = $$list{'type'};
|
||||||
|
$eol = $$list{'eol'};
|
||||||
|
$list_ref = $$list{'files'};
|
||||||
|
|
||||||
|
my @diroutput;
|
||||||
|
my @contentlist;
|
||||||
|
if($type eq "unix") {
|
||||||
|
for(@$list_ref) {
|
||||||
|
my %file = %$_;
|
||||||
|
my $line = "";
|
||||||
|
my $ftype = $file{'type'} ? $file{'type'} : "-";
|
||||||
|
my $fperm = $file{'perm'} ? $file{'perm'} : "rwxr-xr-x";
|
||||||
|
my $fuser = $file{'user'} ? sprintf("%15s", $file{'user'}) : "ftp-default";
|
||||||
|
my $fgroup = $file{'group'} ? sprintf("%15s", $file{'group'}) : "ftp-default";
|
||||||
|
my $fsize = "";
|
||||||
|
if($file{'type'} eq "d") {
|
||||||
|
$fsize = $file{'size'} ? sprintf("%7s", $file{'size'}) : sprintf("%7d", 4096);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$fsize = sprintf("%7d", length $file{'content'});
|
||||||
|
}
|
||||||
|
my $fhlink = $file{'hlink'} ? sprintf("%4d", $file{'hlink'}) : " 1";
|
||||||
|
my $ftime = $file{'time'} ? sprintf("%10s", $file{'time'}) : "Jan 9 1933";
|
||||||
|
push(@contentlist, "$ftype$fperm $fhlink $fuser $fgroup $fsize $ftime $file{'name'}$eol");
|
||||||
|
}
|
||||||
|
|
||||||
|
return @contentlist;
|
||||||
|
}
|
||||||
|
elsif($type =~ /^dos$/) {
|
||||||
|
for(@$list_ref) {
|
||||||
|
my %file = %$_;
|
||||||
|
my $line = "";
|
||||||
|
my $time = $file{'dostime'} ? $file{'dostime'} : "06-25-97 09:12AM";
|
||||||
|
my $size_or_dir;
|
||||||
|
if($file{'type'} =~ /^d$/) {
|
||||||
|
$size_or_dir = " <DIR> ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$size_or_dir = sprintf("%20d", length $file{'content'});
|
||||||
|
}
|
||||||
|
push(@contentlist, "$time $size_or_dir $file{'name'}$eol");
|
||||||
|
}
|
||||||
|
return @contentlist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub wildcard_filesize($$) {
|
||||||
|
my ($list_type, $file) = @_;
|
||||||
|
$list = $lists{$list_type};
|
||||||
|
if($list) {
|
||||||
|
my $files = $list->{'files'};
|
||||||
|
for(@$files) {
|
||||||
|
my %f = %$_;
|
||||||
|
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) = @_;
|
||||||
|
$list = $lists{$list_type};
|
||||||
|
if($list) {
|
||||||
|
my $files = $list->{'files'};
|
||||||
|
for(@$files) {
|
||||||
|
my %f = %$_;
|
||||||
|
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 = $_[0];
|
||||||
|
$list = $lists{$listname};
|
||||||
|
return ftp_createcontent(\$list);
|
||||||
|
}
|
@ -54,6 +54,7 @@ use IPC::Open2;
|
|||||||
|
|
||||||
require "getpart.pm";
|
require "getpart.pm";
|
||||||
require "ftp.pm";
|
require "ftp.pm";
|
||||||
|
require "directories.pm";
|
||||||
|
|
||||||
use serverhelp qw(
|
use serverhelp qw(
|
||||||
servername_str
|
servername_str
|
||||||
@ -136,6 +137,13 @@ my %customreply; #
|
|||||||
my %customcount; #
|
my %customcount; #
|
||||||
my %delayreply; #
|
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
|
# global vars used for signal handling
|
||||||
#
|
#
|
||||||
@ -344,6 +352,8 @@ sub protocolsetup {
|
|||||||
'LIST' => \&LIST_ftp,
|
'LIST' => \&LIST_ftp,
|
||||||
'NLST' => \&NLST_ftp,
|
'NLST' => \&NLST_ftp,
|
||||||
'PASV' => \&PASV_ftp,
|
'PASV' => \&PASV_ftp,
|
||||||
|
'CWD' => \&CWD_ftp,
|
||||||
|
'PWD' => \&PWD_ftp,
|
||||||
'EPSV' => \&PASV_ftp,
|
'EPSV' => \&PASV_ftp,
|
||||||
'RETR' => \&RETR_ftp,
|
'RETR' => \&RETR_ftp,
|
||||||
'SIZE' => \&SIZE_ftp,
|
'SIZE' => \&SIZE_ftp,
|
||||||
@ -362,7 +372,6 @@ sub protocolsetup {
|
|||||||
'CWD' => '250 CWD command successful.',
|
'CWD' => '250 CWD command successful.',
|
||||||
'SYST' => '215 UNIX Type: L8', # just fake something
|
'SYST' => '215 UNIX Type: L8', # just fake something
|
||||||
'QUIT' => '221 bye bye baby', # just reply something
|
'QUIT' => '221 bye bye baby', # just reply something
|
||||||
'PWD' => '257 "/nowhere/anywhere" is current directory',
|
|
||||||
'MKD' => '257 Created your requested directory',
|
'MKD' => '257 Created your requested directory',
|
||||||
'REST' => '350 Yeah yeah we set it there for you',
|
'REST' => '350 Yeah yeah we set it there for you',
|
||||||
'DELE' => '200 OK OK OK whatever you say',
|
'DELE' => '200 OK OK OK whatever you say',
|
||||||
@ -683,6 +692,64 @@ sub REST_ftp {
|
|||||||
logmsg "Set REST position to $rest\n"
|
logmsg "Set REST position to $rest\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub switch_directory_goto {
|
||||||
|
my $target_dir = $_;
|
||||||
|
|
||||||
|
if(!$ftptargetdir) {
|
||||||
|
$ftptargetdir = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if($target_dir eq "") {
|
||||||
|
$ftptargetdir = "/";
|
||||||
|
}
|
||||||
|
elsif($target_dir eq "..") {
|
||||||
|
if($ftptargetdir eq "/") {
|
||||||
|
$ftptargetdir = "/";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ftptargetdir =~ s/[[:alnum:]]+\/$//;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ftptargetdir .= $target_dir . "/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub switch_directory {
|
||||||
|
my $target_dir = $_[0];
|
||||||
|
|
||||||
|
if($target_dir eq "/") {
|
||||||
|
$ftptargetdir = "/";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my @dirs = split("/", $target_dir);
|
||||||
|
for(@dirs) {
|
||||||
|
switch_directory_goto($_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub CWD_ftp {
|
||||||
|
my ($folder, $fullcommand) = $_[0];
|
||||||
|
switch_directory($folder);
|
||||||
|
if($ftptargetdir =~ /^\/fully_simulated/) {
|
||||||
|
$ftplistparserstate = "enabled";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
undef $ftplistparserstate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub PWD_ftp {
|
||||||
|
my $mydir;
|
||||||
|
$mydir = $ftptargetdir ? $ftptargetdir : "/";
|
||||||
|
|
||||||
|
if($mydir ne "/") {
|
||||||
|
$mydir =~ s/\/$//;
|
||||||
|
}
|
||||||
|
sendcontrol "257 \"$mydir\" is current directory\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
sub LIST_ftp {
|
sub LIST_ftp {
|
||||||
# print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
|
# print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
|
||||||
|
|
||||||
@ -699,6 +766,10 @@ my @ftpdir=("total 20\r\n",
|
|||||||
"drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\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");
|
"dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n");
|
||||||
|
|
||||||
|
if($ftplistparserstate) {
|
||||||
|
@ftpdir = ftp_contentlist($ftptargetdir);
|
||||||
|
}
|
||||||
|
|
||||||
logmsg "pass LIST data on data connection\n";
|
logmsg "pass LIST data on data connection\n";
|
||||||
for(@ftpdir) {
|
for(@ftpdir) {
|
||||||
senddata $_;
|
senddata $_;
|
||||||
@ -748,6 +819,16 @@ sub MDTM_ftp {
|
|||||||
|
|
||||||
sub SIZE_ftp {
|
sub SIZE_ftp {
|
||||||
my $testno = $_[0];
|
my $testno = $_[0];
|
||||||
|
if($ftplistparserstate) {
|
||||||
|
my $size = wildcard_filesize($ftptargetdir, $testno);
|
||||||
|
if($size == -1) {
|
||||||
|
sendcontrol "550 $testno: No such file or directory.\r\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendcontrol "213 $size\r\n";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if($testno =~ /^verifiedserver$/) {
|
if($testno =~ /^verifiedserver$/) {
|
||||||
my $response = "WE ROOLZ: $$\r\n";
|
my $response = "WE ROOLZ: $$\r\n";
|
||||||
@ -803,6 +884,21 @@ sub SIZE_ftp {
|
|||||||
sub RETR_ftp {
|
sub RETR_ftp {
|
||||||
my ($testno) = @_;
|
my ($testno) = @_;
|
||||||
|
|
||||||
|
if($ftplistparserstate) {
|
||||||
|
my @content = wildcard_getfile($ftptargetdir, $testno);
|
||||||
|
if($content[0] == -1) {
|
||||||
|
#file not found
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my $size = 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 =~ /^verifiedserver$/) {
|
if($testno =~ /^verifiedserver$/) {
|
||||||
# this is the secret command that verifies that this actually is
|
# this is the secret command that verifies that this actually is
|
||||||
# the curl test server
|
# the curl test server
|
||||||
@ -1326,6 +1422,15 @@ while(1) {
|
|||||||
&customize(); # read test control instructions
|
&customize(); # read test control instructions
|
||||||
|
|
||||||
sendcontrol @welcome;
|
sendcontrol @welcome;
|
||||||
|
|
||||||
|
#remove global variables from last connection
|
||||||
|
if($ftplistparserstate) {
|
||||||
|
undef $ftplistparserstate;
|
||||||
|
}
|
||||||
|
if($ftptargetdir) {
|
||||||
|
undef $ftptargetdir;
|
||||||
|
}
|
||||||
|
|
||||||
if($verbose) {
|
if($verbose) {
|
||||||
for(@welcome) {
|
for(@welcome) {
|
||||||
print STDERR "OUT: $_";
|
print STDERR "OUT: $_";
|
||||||
|
@ -8,6 +8,7 @@ SUPPORTFILES = first.c test.h
|
|||||||
noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 \
|
noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 \
|
||||||
lib507 lib508 lib510 lib511 lib512 lib513 lib514 lib515 lib516 \
|
lib507 lib508 lib510 lib511 lib512 lib513 lib514 lib515 lib516 \
|
||||||
lib517 lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527 \
|
lib517 lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527 \
|
||||||
|
lib574 lib575 lib576 lib577 \
|
||||||
lib529 lib530 lib532 lib533 lib536 lib537 lib540 lib541 lib542 lib543 \
|
lib529 lib530 lib532 lib533 lib536 lib537 lib540 lib541 lib542 lib543 \
|
||||||
lib544 lib545 lib547 lib548 lib549 lib552 lib553 lib554 lib555 lib556 \
|
lib544 lib545 lib547 lib548 lib549 lib552 lib553 lib554 lib555 lib556 \
|
||||||
lib539 lib557 lib558 lib559 lib560 lib562 lib564 lib565 lib566 lib567 \
|
lib539 lib557 lib558 lib559 lib560 lib562 lib564 lib565 lib566 lib567 \
|
||||||
@ -124,6 +125,14 @@ lib559_CFLAGS = -DLIB559
|
|||||||
|
|
||||||
lib560_SOURCES = lib560.c $(SUPPORTFILES)
|
lib560_SOURCES = lib560.c $(SUPPORTFILES)
|
||||||
|
|
||||||
|
lib574_SOURCES = lib574.c $(SUPPORTFILES)
|
||||||
|
|
||||||
|
lib575_SOURCES = lib575.c $(SUPPORTFILES)
|
||||||
|
|
||||||
|
lib576_SOURCES = lib576.c $(SUPPORTFILES)
|
||||||
|
|
||||||
|
lib577_SOURCES = lib577.c $(SUPPORTFILES)
|
||||||
|
|
||||||
lib562_SOURCES = lib562.c $(SUPPORTFILES)
|
lib562_SOURCES = lib562.c $(SUPPORTFILES)
|
||||||
|
|
||||||
lib564_SOURCES = lib564.c $(SUPPORTFILES) $(TESTUTIL)
|
lib564_SOURCES = lib564.c $(SUPPORTFILES) $(TESTUTIL)
|
||||||
|
56
tests/libtest/lib574.c
Normal file
56
tests/libtest/lib574.c
Normal file
@ -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) != CURLE_OK) {
|
||||||
|
fprintf(stderr, "curl_global_init() failed\n");
|
||||||
|
return TEST_ERR_MAJOR_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((curl = curl_easy_init()) == 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 = curl_easy_perform(curl);
|
||||||
|
if(res) {
|
||||||
|
fprintf(stderr, "curl_easy_perform() failed %d\n", res);
|
||||||
|
goto test_cleanup;
|
||||||
|
}
|
||||||
|
res = 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;
|
||||||
|
}
|
109
tests/libtest/lib575.c
Normal file
109
tests/libtest/lib575.c
Normal file
@ -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 = NULL, *duphandle;
|
||||||
|
CURLM *mhandle = NULL;
|
||||||
|
int res = 0;
|
||||||
|
int still_running = 0;
|
||||||
|
|
||||||
|
if(curl_global_init(CURL_GLOBAL_ALL)) {
|
||||||
|
fprintf(stderr, "curl_global_init() failed\n");
|
||||||
|
goto test_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = curl_easy_init();
|
||||||
|
if(!handle) {
|
||||||
|
res = 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 = curl_easy_perform(handle);
|
||||||
|
if(res)
|
||||||
|
goto test_cleanup;
|
||||||
|
|
||||||
|
res = curl_easy_perform(handle);
|
||||||
|
if(res)
|
||||||
|
goto test_cleanup;
|
||||||
|
|
||||||
|
duphandle = curl_easy_duphandle(handle);
|
||||||
|
if(!duphandle)
|
||||||
|
goto test_cleanup;
|
||||||
|
curl_easy_cleanup(handle);
|
||||||
|
handle = duphandle;
|
||||||
|
|
||||||
|
mhandle = 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 ==
|
||||||
|
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 = -1;
|
||||||
|
FD_ZERO(&fdread);
|
||||||
|
FD_ZERO(&fdwrite);
|
||||||
|
FD_ZERO(&fdexcep);
|
||||||
|
timeout.tv_sec = 3;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
m = curl_multi_fdset(mhandle, &fdread, &fdwrite, &fdexcep, &max_fdset);
|
||||||
|
rc = select(max_fdset + 1, &fdread, &fdwrite, &fdexcep, &timeout);
|
||||||
|
if(rc == -1) {
|
||||||
|
fprintf(stderr, "select() error\n");
|
||||||
|
goto test_cleanup;
|
||||||
|
}
|
||||||
|
else if(rc == 0) {
|
||||||
|
fprintf(stderr, "select() timeout!\n");
|
||||||
|
goto test_cleanup;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while(CURLM_CALL_MULTI_PERFORM ==
|
||||||
|
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;
|
||||||
|
}
|
107
tests/libtest/lib576.c
Normal file
107
tests/libtest/lib576.c
Normal file
@ -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 remains);
|
||||||
|
long chunk_end(void *ptr);
|
||||||
|
|
||||||
|
long chunk_bgn(const struct curl_fileinfo *finfo, void *ptr, int remains)
|
||||||
|
{
|
||||||
|
chunk_data_t *ch_d = ptr;
|
||||||
|
ch_d->remains = remains;
|
||||||
|
|
||||||
|
printf("=============================================================\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 => %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 == CURLFILETYPE_FILE) {
|
||||||
|
ch_d->print_content = 1;
|
||||||
|
printf("Content:\n-------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
if(strcmp(finfo->filename, "someothertext.txt") == 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 = ptr;
|
||||||
|
if(ch_d->print_content) {
|
||||||
|
ch_d->print_content = 0;
|
||||||
|
printf("-------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
if(ch_d->remains == 1)
|
||||||
|
printf("=============================================================\n");
|
||||||
|
return CURL_CHUNK_END_FUNC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test(char *URL)
|
||||||
|
{
|
||||||
|
CURL *handle = NULL;
|
||||||
|
CURLcode res = 0;
|
||||||
|
chunk_data_t chunk_data = {0,0};
|
||||||
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
handle = curl_easy_init();
|
||||||
|
if(!handle) {
|
||||||
|
res = 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 = curl_easy_perform(handle);
|
||||||
|
|
||||||
|
test_cleanup:
|
||||||
|
if(handle)
|
||||||
|
curl_easy_cleanup(handle);
|
||||||
|
curl_global_cleanup();
|
||||||
|
return res;
|
||||||
|
}
|
217
tests/libtest/lib577.c
Normal file
217
tests/libtest/lib577.c
Normal file
@ -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[] = {
|
||||||
|
/* 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 },
|
||||||
|
{ ":;=@ABCDEFGHIJKLMNO", ":;=@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 => 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ätarv", "Lindmätarv", MATCH },
|
||||||
|
|
||||||
|
{ "", "", MATCH }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int test(char *URL)
|
||||||
|
{
|
||||||
|
int testnum = sizeof(tests) / sizeof(struct testcase);
|
||||||
|
int i, rc;
|
||||||
|
(void)URL; /* not used */
|
||||||
|
printf("===========================\n");
|
||||||
|
for(i = 0; i < testnum; i++) {
|
||||||
|
rc = Curl_fnmatch(tests[i].pattern, tests[i].string);
|
||||||
|
if(rc != 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("===========================\n");
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user