FTP: WILDCARDMATCH/CHUNKING/FNMATCH added

This commit is contained in:
Pavel Raiskup 2010-05-12 15:33:22 +02:00 committed by Daniel Stenberg
parent 04cb15ae9d
commit 0825cd80a6
36 changed files with 3778 additions and 16 deletions

View file

@ -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 \
ldap.c ssluse.c version.c getenv.c escape.c mprintf.c telnet.c \
netrc.c getinfo.c transfer.c strequal.c easy.c security.c krb4.c \
curl_fnmatch.c fileinfo.c ftplistparser.c wildcard.c \
krb5.c memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c \
multi.c content_encoding.c share.c http_digest.c md5.c curl_rand.c \
http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \
@ -18,6 +19,7 @@ HHEADERS = 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 \
if2ip.h speedcheck.h urldata.h curl_ldap.h ssluse.h escape.h telnet.h \
getinfo.h strequal.h krb4.h memdebug.h http_chunks.h curl_rand.h \
curl_fnmatch.h wildcard.h fileinfo.h ftplistparser.h \
strtok.h connect.h llist.h hash.h content_encoding.h share.h \
curl_md5.h http_digest.h http_negotiate.h http_ntlm.h inet_pton.h \
strtoofft.h strerror.h inet_ntop.h curlx.h curl_memory.h setup.h \

410
lib/curl_fnmatch.c Normal file
View 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
View 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
View 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
View 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
View file

@ -71,6 +71,8 @@
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
#include "ftp.h"
#include "fileinfo.h"
#include "ftplistparser.h"
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
#include "krb4.h"
@ -110,6 +112,7 @@
#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
#endif
void Curl_ftp_wc_data_dtor(void *ptr);
/* Local API functions */
static CURLcode ftp_sendquote(struct connectdata *conn,
struct curl_slist *quote);
@ -144,6 +147,12 @@ static CURLcode ftp_doing(struct connectdata *conn,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct connectdata * conn);
static CURLcode init_wc_data(struct connectdata *conn);
static CURLcode wc_statemach(struct connectdata *conn);
static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
curl_off_t filesize);
/* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
return result
@ -1469,8 +1478,14 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
if(ftp->transfer != FTPTRANSFER_BODY)
state(conn, FTP_STOP);
else {
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
state(conn, FTP_RETR_SIZE);
if(ftpc->known_filesize != -1) {
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;
case FTP_STOR_PREQUOTE:
@ -2855,6 +2870,8 @@ static CURLcode ftp_init(struct connectdata *conn)
if(TRUE == isBadFtpString(ftp->passwd))
return CURLE_URL_MALFORMAT;
conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
return CURLE_OK;
}
@ -3018,6 +3035,13 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(ftpc->prevpath)
free(ftpc->prevpath);
if(data->set.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
data->set.chunk_end(data->wildcard.customptr);
}
ftpc->known_filesize = -1;
}
/* get the "raw" path */
path = curl_easy_unescape(data, path_to_use, 0, NULL);
if(!path) {
@ -3445,6 +3469,221 @@ CURLcode ftp_perform(struct connectdata *conn,
return result;
}
void Curl_ftp_wc_data_dtor(void *ptr)
{
struct ftp_wc_tmpdata *tmp = 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()
@ -3471,9 +3710,21 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done)
if(retcode)
return retcode;
retcode = ftp_parse_url_path(conn);
if(retcode)
return retcode;
if(conn->data->set.wildcardmatch) {
retcode = wc_statemach(conn);
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);

View file

@ -79,6 +79,17 @@ typedef enum {
FTP_LAST /* never used */
} ftpstate;
struct ftp_parselist_data; /* defined later in ftplistparser.c */
struct ftp_wc_tmpdata {
struct ftp_parselist_data *parser;
struct {
curl_write_callback write_function;
FILE *file_descriptor;
} backup;
};
typedef enum {
FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
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 */
ftpstate state; /* always use ftp.c:state() to change state! */
char * server_os; /* The target server operating system. */
curl_off_t known_filesize; /* file size is different from -1, if wildcard
LIST parsing was done and wc_statemach set it */
};
#endif /* HEADER_CURL_FTP_H */

1009
lib/ftplistparser.c Normal file

File diff suppressed because it is too large Load diff

38
lib/ftplistparser.h Normal file
View 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_ */

View file

@ -1128,6 +1128,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(CURLE_OK == easy->result) {
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
DOING... */
multistate(easy, CURLM_STATE_DOING);
@ -1338,7 +1349,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
easy->easy_conn->writechannel_inuse = FALSE;
}
if(easy->result) {
if(easy->result) {
/* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is because we can't possibly
* 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;
}
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
it doesn't matter what the Curl_done() returned! */
multistate(easy, CURLM_STATE_COMPLETED);
@ -1550,11 +1571,26 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
easy=multi->easy.next;
while(easy != &multi->easy) {
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
result = multi_runsingle(multi, easy);
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)
returncode = result;

View file

@ -275,6 +275,12 @@ curl_easy_strerror(CURLcode error)
case CURLE_RTSP_SESSION_ERROR:
return "RTSP session error";
case CURLE_FTP_BAD_FILE_LIST:
return "Unable to parse FTP file list";
case CURLE_CHUNK_FAILED:
return "Chunk callback failed";
/* error codes not used by current libcurl */
case CURLE_OBSOLETE4:
case CURLE_OBSOLETE10:

View file

@ -2005,12 +2005,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
return CURLE_OK;
}
/*
* Curl_perform() is the internal high-level function that gets called by the
* external curl_easy_perform() function. It inits, performs and cleans up a
* single file transfer.
*/
CURLcode Curl_perform(struct SessionHandle *data)
static CURLcode Curl_do_perform(struct SessionHandle *data)
{
CURLcode res;
CURLcode res2;
@ -2045,6 +2040,15 @@ CURLcode Curl_perform(struct SessionHandle *data)
res = Curl_do(&conn, &do_done);
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 */
if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
bool retry = FALSE;
@ -2161,6 +2165,39 @@ CURLcode Curl_perform(struct SessionHandle *data)
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
* upcoming transfer.

View file

@ -770,6 +770,10 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
#endif
set->wildcardmatch = 0L;
set->chunk_bgn = ZERO_NULL;
set->chunk_end = ZERO_NULL;
return res;
}
@ -838,6 +842,9 @@ CURLcode Curl_open(struct SessionHandle **curl)
data->progress.flags |= PGRS_HIDE;
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
the first call to curl_easy_perform() or when the handle is added to a
multi stack. */
@ -2455,6 +2462,23 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
/* Set the user defined RTP write function */
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
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:
/* unknown tag and its companion, just ignore: */
result = CURLE_FAILED_INIT; /* correct this */

View file

@ -156,6 +156,7 @@
#include "ssh.h"
#include "http.h"
#include "rtsp.h"
#include "wildcard.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@ -1419,6 +1420,12 @@ struct UserDefined {
/* Common RTSP header options */
Curl_RtspReq rtspreq; /* RTSP request type */
long rtspversion; /* like httpversion, for RTSP */
bool wildcardmatch; /* enable wildcard matching */
curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer starts */
curl_chunk_end_callback chunk_end; /* called after part transferring
stopped */
curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds
to pattern (e.g. if WILDCARDMATCH is on) */
};
struct Names {
@ -1460,6 +1467,7 @@ struct SessionHandle {
struct Progress progress; /* for all the progress meter data */
struct UrlState state; /* struct for fields used for state info and
other dynamic purposes */
struct WildcardData wildcard; /* wildcard download state info */
struct PureInfo info; /* stats, reports and info data */
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
iconv_t outbound_cd; /* for translating to the network encoding */

66
lib/wildcard.c Normal file
View 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
View 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 */