From cac0e4a6ad14b42471ebc62e82ecdb7dad164702 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 11 Feb 2019 19:10:41 +0100 Subject: cli tool: do not use mime.h private structures. Option -F generates an intermediate representation of the mime structure that is used later to create the libcurl mime structure and generate the --libcurl statements. Reported-by: Daniel Stenberg Fixes #3532 Closes #3546 --- src/tool_cfgable.c | 4 +- src/tool_cfgable.h | 8 +- src/tool_formparse.c | 609 ++++++++++++++++++++++++--------------------------- src/tool_formparse.h | 46 +++- src/tool_setopt.c | 352 +++++++++++++++++------------ src/tool_setopt.h | 6 +- tests/data/test1404 | 2 + 7 files changed, 546 insertions(+), 481 deletions(-) diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 0eb941ef6..6d4a0ed9b 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -145,7 +145,7 @@ static void free_config_fields(struct OperationConfig *config) curl_slist_free_all(config->proxyheaders); if(config->mimepost) { - curl_mime_free(config->mimepost); + tool_mime_free(config->mimepost); config->mimepost = NULL; } config->mimecurrent = NULL; diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 81680dbbb..0ebe947ae 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,6 +27,8 @@ #include "tool_metalink.h" +#include "tool_formparse.h" + typedef enum { ERR_NONE, ERR_BINARY_TERMINAL = 1, /* binary to terminal detected */ @@ -176,8 +178,8 @@ struct OperationConfig { curl_off_t condtime; struct curl_slist *headers; struct curl_slist *proxyheaders; - curl_mime *mimepost; - curl_mime *mimecurrent; + tool_mime *mimepost; + tool_mime *mimecurrent; struct curl_slist *telnet_options; struct curl_slist *resolve; struct curl_slist *connect_to; diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 5d1ea9c53..537c6ca93 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,7 +21,6 @@ ***************************************************************************/ #include "tool_setup.h" -#include "mime.h" #include "strcase.h" #define ENABLE_CURLX_PRINTF @@ -38,14 +37,205 @@ #include "memdebug.h" /* keep this as LAST include */ -/* Stdin parameters. */ -typedef struct { - char *data; /* Memory data. */ - curl_off_t origin; /* File read origin offset. */ - curl_off_t size; /* Data size. */ - curl_off_t curpos; /* Current read position. */ -} standard_input; +/* Macros to free const pointers. */ +#define CONST_FREE(x) free((void *) (x)) +#define CONST_SAFEFREE(x) Curl_safefree(*((void **) &(x))) +/* tool_mime functions. */ +static tool_mime *tool_mime_new(tool_mime *parent, toolmimekind kind) +{ + tool_mime *m = (tool_mime *) calloc(1, sizeof(*m)); + + if(m) { + m->kind = kind; + m->parent = parent; + if(parent) { + m->prev = parent->subparts; + parent->subparts = m; + } + } + return m; +} + +static tool_mime *tool_mime_new_parts(tool_mime *parent) +{ + return tool_mime_new(parent, TOOLMIME_PARTS); +} + +static tool_mime *tool_mime_new_data(tool_mime *parent, const char *data) +{ + tool_mime *m = NULL; + + data = strdup(data); + if(data) { + m = tool_mime_new(parent, TOOLMIME_DATA); + if(!m) + CONST_FREE(data); + else + m->data = data; + } + return m; +} + +static tool_mime *tool_mime_new_filedata(tool_mime *parent, + const char *filename, + bool isremotefile, + CURLcode *errcode) +{ + CURLcode result = CURLE_OK; + tool_mime *m = NULL; + + *errcode = CURLE_OUT_OF_MEMORY; + if(strcmp(filename, "-")) { + /* This is a normal file. */ + filename = strdup(filename); + if(filename) { + m = tool_mime_new(parent, TOOLMIME_FILE); + if(!m) + CONST_FREE(filename); + else { + m->data = filename; + if(!isremotefile) + m->kind = TOOLMIME_FILEDATA; + *errcode = CURLE_OK; + } + } + } + else { /* Standard input. */ + int fd = fileno(stdin); + char *data = NULL; + curl_off_t size; + curl_off_t origin; + struct_stat sbuf; + + set_binmode(stdin); + origin = ftell(stdin); + /* If stdin is a regular file, do not buffer data but read it + when needed. */ + if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) && +#ifdef __VMS + sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && +#endif + S_ISREG(sbuf.st_mode)) { + size = sbuf.st_size - origin; + if(size < 0) + size = 0; + } + else { /* Not suitable for direct use, buffer stdin data. */ + size_t stdinsize = 0; + + if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) { + /* Out of memory. */ + return m; + } + + if(ferror(stdin)) { + result = CURLE_READ_ERROR; + Curl_safefree(data); + data = NULL; + } + else if(!stdinsize) { + /* Zero-length data has been freed. Re-create it. */ + data = strdup(""); + if(!data) + return m; + } + size = curlx_uztoso(stdinsize); + origin = 0; + } + m = tool_mime_new(parent, TOOLMIME_STDIN); + if(!m) + Curl_safefree(data); + else { + m->data = data; + m->origin = origin; + m->size = size; + m->curpos = 0; + if(!isremotefile) + m->kind = TOOLMIME_STDINDATA; + *errcode = result; + } + } + return m; +} + +void tool_mime_free(tool_mime *mime) +{ + if(mime) { + if(mime->subparts) + tool_mime_free(mime->subparts); + if(mime->prev) + tool_mime_free(mime->prev); + curl_mime_free(mime->handle); + CONST_SAFEFREE(mime->name); + CONST_SAFEFREE(mime->filename); + CONST_SAFEFREE(mime->type); + CONST_SAFEFREE(mime->encoder); + CONST_SAFEFREE(mime->data); + curl_slist_free_all(mime->headers); + free(mime); + } +} + + +/* Mime part callbacks for stdin. */ +size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg) +{ + tool_mime *sip = (tool_mime *) arg; + curl_off_t bytesleft; + (void) size; /* Always 1: ignored. */ + + if(sip->size >= 0) { + if(sip->curpos >= sip->size) + return 0; /* At eof. */ + bytesleft = sip->size - sip->curpos; + if(curlx_uztoso(nitems) > bytesleft) + nitems = curlx_sotouz(bytesleft); + } + if(nitems) { + if(sip->data) { + /* Return data from memory. */ + memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems); + } + else { + /* Read from stdin. */ + nitems = fread(buffer, 1, nitems, stdin); + if(ferror(stdin)) { + /* Show error only once. */ + if(sip->config) { + warnf(sip->config, "stdin: %s\n", strerror(errno)); + sip->config = NULL; + } + return CURL_READFUNC_ABORT; + } + } + sip->curpos += curlx_uztoso(nitems); + } + return nitems; +} + +int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) +{ + tool_mime *sip = (tool_mime *) instream; + + switch(whence) { + case SEEK_CUR: + offset += sip->curpos; + break; + case SEEK_END: + offset += sip->size; + break; + } + if(offset < 0) + return CURL_SEEKFUNC_CANTSEEK; + if(!sip->data) { + if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) + return CURL_SEEKFUNC_CANTSEEK; + } + sip->curpos = offset; + return CURL_SEEKFUNC_OK; +} /* * helper function to get a word from form param @@ -379,130 +569,15 @@ static int get_param_part(struct OperationConfig *config, char endchar, } -/* Mime part callbacks for stdin. */ -static size_t stdin_read(char *buffer, size_t size, size_t nitems, void *arg) -{ - standard_input *sip = (standard_input *) arg; - curl_off_t bytesleft; - (void) size; /* Always 1: ignored. */ - - if(sip->curpos >= sip->size) - return 0; /* At eof. */ - bytesleft = sip->size - sip->curpos; - if((curl_off_t) nitems > bytesleft) - nitems = (size_t) bytesleft; - if(sip->data) { - /* Return data from memory. */ - memcpy(buffer, sip->data + (size_t) sip->curpos, nitems); - } - else { - /* Read from stdin. */ - nitems = fread(buffer, 1, nitems, stdin); - } - sip->curpos += nitems; - return nitems; -} - -static int stdin_seek(void *instream, curl_off_t offset, int whence) -{ - standard_input *sip = (standard_input *) instream; - - switch(whence) { - case SEEK_CUR: - offset += sip->curpos; - break; - case SEEK_END: - offset += sip->size; - break; - } - if(offset < 0) - return CURL_SEEKFUNC_CANTSEEK; - if(!sip->data) { - if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) - return CURL_SEEKFUNC_CANTSEEK; - } - sip->curpos = offset; - return CURL_SEEKFUNC_OK; -} - -static void stdin_free(void *ptr) -{ - standard_input *sip = (standard_input *) ptr; - - Curl_safefree(sip->data); - free(sip); -} - -/* Set a part's data from a file, taking care about the pseudo filename "-" as - * a shortcut to read stdin: if so, use a callback to read OUR stdin (to - * workaround Windows DLL file handle caveat). - * If stdin is a regular file opened in binary mode, save current offset as - * origin for rewind and do not buffer data. Else read to EOF and keep in - * memory. In all cases, compute the stdin data size. - */ -static CURLcode file_or_stdin(curl_mimepart *part, const char *file) -{ - standard_input *sip = NULL; - int fd = -1; - CURLcode result = CURLE_OK; - struct_stat sbuf; - - if(strcmp(file, "-")) - return curl_mime_filedata(part, file); - - sip = (standard_input *) calloc(1, sizeof(*sip)); - if(!sip) - return CURLE_OUT_OF_MEMORY; - - set_binmode(stdin); - - /* If stdin is a regular file, do not buffer data but read it when needed. */ - fd = fileno(stdin); - sip->origin = ftell(stdin); - if(fd >= 0 && sip->origin >= 0 && !fstat(fd, &sbuf) && -#ifdef __VMS - sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && -#endif - S_ISREG(sbuf.st_mode)) { - sip->size = sbuf.st_size - sip->origin; - if(sip->size < 0) - sip->size = 0; - } - else { /* Not suitable for direct use, buffer stdin data. */ - size_t stdinsize = 0; - - sip->origin = 0; - if(file2memory(&sip->data, &stdinsize, stdin) != PARAM_OK) - result = CURLE_OUT_OF_MEMORY; - else { - if(!stdinsize) - sip->data = NULL; /* Has been freed if no data. */ - sip->size = stdinsize; - if(ferror(stdin)) - result = CURLE_READ_ERROR; - } - } - - /* Set remote file name. */ - if(!result) - result = curl_mime_filename(part, file); - - /* Set part's data from callback. */ - if(!result) - result = curl_mime_data_cb(part, sip->size, - stdin_read, stdin_seek, stdin_free, sip); - if(result) - stdin_free(sip); - return result; -} - - /*************************************************************************** * * formparse() * * Reads a 'name=value' parameter and builds the appropriate linked list. * + * If the value is of the form ''. @@ -539,15 +614,27 @@ static CURLcode file_or_stdin(curl_mimepart *part, const char *file) * else curl will fail to figure out the correct filename. if the filename * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. * - * This function uses curl_formadd to fulfill it's job. Is heavily based on - * the old curl_formparse code. - * ***************************************************************************/ +/* Convenience macros for null pointer check. */ +#define NULL_CHECK(ptr, init, retcode) { \ + (ptr) = (init); \ + if(!(ptr)) { \ + warnf(config->global, "out of memory!\n"); \ + curl_slist_free_all(headers); \ + Curl_safefree(contents); \ + return retcode; \ + } \ +} +#define SET_TOOL_MIME_PTR(m, field, retcode) { \ + if(field) \ + NULL_CHECK(m->field, strdup(field), retcode); \ +} + int formparse(struct OperationConfig *config, const char *input, - curl_mime **mimepost, - curl_mime **mimecurrent, + tool_mime **mimepost, + tool_mime **mimecurrent, bool literal_value) { /* input MUST be a string in the format 'name=contents' and we'll @@ -560,25 +647,17 @@ int formparse(struct OperationConfig *config, char *filename = NULL; char *encoder = NULL; struct curl_slist *headers = NULL; - curl_mimepart *part = NULL; + tool_mime *part = NULL; CURLcode res; /* Allocate the main mime structure if needed. */ if(!*mimepost) { - *mimepost = curl_mime_init(config->easy); - if(!*mimepost) { - warnf(config->global, "curl_mime_init failed!\n"); - return 1; - } + NULL_CHECK(*mimepost, tool_mime_new_parts(NULL), 1); *mimecurrent = *mimepost; } /* Make a copy we can overwrite. */ - contents = strdup(input); - if(!contents) { - fprintf(config->global->errors, "out of memory\n"); - return 2; - } + NULL_CHECK(contents, strdup(input), 2); /* Scan for the end of the name. */ contp = strchr(contents, '='); @@ -589,8 +668,6 @@ int formparse(struct OperationConfig *config, *contp++ = '\0'; if(*contp == '(' && !literal_value) { - curl_mime *subparts; - /* Starting a multipart. */ sep = get_param_part(config, '\0', &contp, &data, &type, NULL, NULL, &headers); @@ -598,55 +675,26 @@ int formparse(struct OperationConfig *config, Curl_safefree(contents); return 3; } - subparts = curl_mime_init(config->easy); - if(!subparts) { - warnf(config->global, "curl_mime_init failed!\n"); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 4; - } - part = curl_mime_addpart(*mimecurrent); - if(!part) { - warnf(config->global, "curl_mime_addpart failed!\n"); - curl_mime_free(subparts); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 5; - } - if(curl_mime_subparts(part, subparts)) { - warnf(config->global, "curl_mime_subparts failed!\n"); - curl_mime_free(subparts); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 6; - } - *mimecurrent = subparts; - if(curl_mime_headers(part, headers, 1)) { - warnf(config->global, "curl_mime_headers failed!\n"); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 7; - } - if(curl_mime_type(part, type)) { - warnf(config->global, "curl_mime_type failed!\n"); - Curl_safefree(contents); - return 8; - } + NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4); + *mimecurrent = part; + part->headers = headers; + headers = NULL; + SET_TOOL_MIME_PTR(part, type, 5); } else if(!name && !strcmp(contp, ")") && !literal_value) { - /* Ending a mutipart. */ + /* Ending a multipart. */ if(*mimecurrent == *mimepost) { warnf(config->global, "no multipart to terminate!\n"); Curl_safefree(contents); - return 9; + return 6; } - *mimecurrent = (*mimecurrent)->parent->parent; + *mimecurrent = (*mimecurrent)->parent; } else if('@' == contp[0] && !literal_value) { /* we use the @-letter to indicate file name(s) */ - curl_mime *subparts = NULL; + tool_mime *subparts = NULL; do { /* since this was a file, it may have a content-type specifier @@ -655,10 +703,8 @@ int formparse(struct OperationConfig *config, sep = get_param_part(config, ',', &contp, &data, &type, &filename, &encoder, &headers); if(sep < 0) { - if(subparts != *mimecurrent) - curl_mime_free(subparts); Curl_safefree(contents); - return 10; + return 7; } /* now contp point to comma or string end. @@ -666,125 +712,66 @@ int formparse(struct OperationConfig *config, if(!subparts) { if(sep != ',') /* If there is a single file. */ subparts = *mimecurrent; - else { - subparts = curl_mime_init(config->easy); - if(!subparts) { - warnf(config->global, "curl_mime_init failed!\n"); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 11; - } - } - } - - /* Allocate a part for that file. */ - part = curl_mime_addpart(subparts); - if(!part) { - warnf(config->global, "curl_mime_addpart failed!\n"); - if(subparts != *mimecurrent) - curl_mime_free(subparts); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 12; - } - - /* Set part headers. */ - if(curl_mime_headers(part, headers, 1)) { - warnf(config->global, "curl_mime_headers failed!\n"); - if(subparts != *mimecurrent) - curl_mime_free(subparts); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 13; + else + NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8); } - /* Setup file in part. */ - res = file_or_stdin(part, data); - if(res) { - warnf(config->global, "setting file %s failed!\n", data); - if(res != CURLE_READ_ERROR) { - if(subparts != *mimecurrent) - curl_mime_free(subparts); + /* Store that file in a part. */ + NULL_CHECK(part, + tool_mime_new_filedata(subparts, data, TRUE, &res), 9); + part->headers = headers; + headers = NULL; + if(res == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by + libcurl. */ + if(part->size > 0) { + warnf(config->global, + "error while reading standard input\n"); Curl_safefree(contents); - return 14; + return 10; } + CONST_SAFEFREE(part->data); + part->data = NULL; + part->size = -1; + res = CURLE_OK; } - if(filename && curl_mime_filename(part, filename)) { - warnf(config->global, "curl_mime_filename failed!\n"); - if(subparts != *mimecurrent) - curl_mime_free(subparts); - Curl_safefree(contents); - return 15; - } - if(curl_mime_type(part, type)) { - warnf(config->global, "curl_mime_type failed!\n"); - if(subparts != *mimecurrent) - curl_mime_free(subparts); - Curl_safefree(contents); - return 16; - } - if(curl_mime_encoder(part, encoder)) { - warnf(config->global, "curl_mime_encoder failed!\n"); - if(subparts != *mimecurrent) - curl_mime_free(subparts); - Curl_safefree(contents); - return 17; - } + SET_TOOL_MIME_PTR(part, filename, 11); + SET_TOOL_MIME_PTR(part, type, 12); + SET_TOOL_MIME_PTR(part, encoder, 13); /* *contp could be '\0', so we just check with the delimiter */ } while(sep); /* loop if there's another file name */ - - /* now we add the multiple files section */ - if(subparts != *mimecurrent) { - part = curl_mime_addpart(*mimecurrent); - if(!part) { - warnf(config->global, "curl_mime_addpart failed!\n"); - curl_mime_free(subparts); - Curl_safefree(contents); - return 18; - } - if(curl_mime_subparts(part, subparts)) { - warnf(config->global, "curl_mime_subparts failed!\n"); - curl_mime_free(subparts); - Curl_safefree(contents); - return 19; - } - } + part = (*mimecurrent)->subparts; /* Set name on group. */ } else { - /* Allocate a mime part. */ - part = curl_mime_addpart(*mimecurrent); - if(!part) { - warnf(config->global, "curl_mime_addpart failed!\n"); - Curl_safefree(contents); - return 20; - } - if(*contp == '<' && !literal_value) { ++contp; sep = get_param_part(config, '\0', &contp, &data, &type, NULL, &encoder, &headers); if(sep < 0) { Curl_safefree(contents); - return 21; - } - - /* Set part headers. */ - if(curl_mime_headers(part, headers, 1)) { - warnf(config->global, "curl_mime_headers failed!\n"); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 22; + return 14; } - /* Setup file in part. */ - res = file_or_stdin(part, data); - if(res) { - warnf(config->global, "setting file %s failed!\n", data); - if(res != CURLE_READ_ERROR) { + NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE, + &res), 15); + part->headers = headers; + headers = NULL; + if(res == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by + libcurl. */ + if(part->size > 0) { + warnf(config->global, + "error while reading standard input\n"); Curl_safefree(contents); - return 23; + return 16; } + CONST_SAFEFREE(part->data); + part->data = NULL; + part->size = -1; + res = CURLE_OK; } } else { @@ -795,48 +782,18 @@ int formparse(struct OperationConfig *config, &data, &type, &filename, &encoder, &headers); if(sep < 0) { Curl_safefree(contents); - return 24; + return 17; } } - /* Set part headers. */ - if(curl_mime_headers(part, headers, 1)) { - warnf(config->global, "curl_mime_headers failed!\n"); - curl_slist_free_all(headers); - Curl_safefree(contents); - return 25; - } - -#ifdef CURL_DOES_CONVERSIONS - if(convert_to_network(data, strlen(data))) { - warnf(config->global, "curl_formadd failed!\n"); - Curl_safefree(contents); - return 26; - } -#endif - - if(curl_mime_data(part, data, CURL_ZERO_TERMINATED)) { - warnf(config->global, "curl_mime_data failed!\n"); - Curl_safefree(contents); - return 27; - } + NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18); + part->headers = headers; + headers = NULL; } - if(curl_mime_filename(part, filename)) { - warnf(config->global, "curl_mime_filename failed!\n"); - Curl_safefree(contents); - return 28; - } - if(curl_mime_type(part, type)) { - warnf(config->global, "curl_mime_type failed!\n"); - Curl_safefree(contents); - return 29; - } - if(curl_mime_encoder(part, encoder)) { - warnf(config->global, "curl_mime_encoder failed!\n"); - Curl_safefree(contents); - return 30; - } + SET_TOOL_MIME_PTR(part, filename, 19); + SET_TOOL_MIME_PTR(part, type, 20); + SET_TOOL_MIME_PTR(part, encoder, 21); if(sep) { *contp = (char) sep; @@ -846,16 +803,12 @@ int formparse(struct OperationConfig *config, } /* Set part name. */ - if(name && curl_mime_name(part, name)) { - warnf(config->global, "curl_mime_name failed!\n"); - Curl_safefree(contents); - return 31; - } + SET_TOOL_MIME_PTR(part, name, 22); } else { warnf(config->global, "Illegally formatted input field!\n"); Curl_safefree(contents); - return 32; + return 23; } Curl_safefree(contents); return 0; diff --git a/src/tool_formparse.h b/src/tool_formparse.h index cdf02d028..203f5ab30 100644 --- a/src/tool_formparse.h +++ b/src/tool_formparse.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,10 +23,50 @@ ***************************************************************************/ #include "tool_setup.h" +/* Private structure for mime/parts. */ + +typedef enum { + TOOLMIME_NONE = 0, + TOOLMIME_PARTS, + TOOLMIME_DATA, + TOOLMIME_FILE, + TOOLMIME_FILEDATA, + TOOLMIME_STDIN, + TOOLMIME_STDINDATA +} toolmimekind; + +typedef struct tool_mime tool_mime; +struct tool_mime { + /* Structural fields. */ + toolmimekind kind; /* Part kind. */ + tool_mime *parent; /* Parent item. */ + tool_mime *prev; /* Previous sibling (reverse order link). */ + /* Common fields. */ + const char *data; /* Actual data or data filename. */ + const char *name; /* Part name. */ + const char *filename; /* Part's filename. */ + const char *type; /* Part's mime type. */ + const char *encoder; /* Part's requested encoding. */ + struct curl_slist *headers; /* User-defined headers. */ + /* TOOLMIME_PARTS fields. */ + tool_mime *subparts; /* Part's subparts. */ + curl_mime *handle; /* Libcurl mime handle. */ + /* TOOLMIME_STDIN/TOOLMIME_STDINDATA fields. */ + curl_off_t origin; /* Stdin read origin offset. */ + curl_off_t size; /* Stdin data size. */ + curl_off_t curpos; /* Stdin current read position. */ + struct GlobalConfig *config; /* For access from callback. */ +}; + +size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg); +int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence); + int formparse(struct OperationConfig *config, const char *input, - curl_mime **mimepost, - curl_mime **mimecurrent, + tool_mime **mimepost, + tool_mime **mimecurrent, bool literal_value); +void tool_mime_free(tool_mime *mime); #endif /* HEADER_CURL_TOOL_FORMPARSE_H */ diff --git a/src/tool_setopt.c b/src/tool_setopt.c index cd28ad829..878bea2b4 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,7 +32,6 @@ #include "tool_setopt.h" #include "tool_convert.h" -#include "mime.h" #include "memdebug.h" /* keep this as LAST include */ /* Lookup tables for converting setopt values back to symbols */ @@ -187,6 +186,12 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = { if(ret) \ goto nomem; \ } WHILE_FALSE +#define NULL_CHECK(p) do { \ + if(!p) { \ + ret = CURLE_OUT_OF_MEMORY; \ + goto nomem; \ + } \ +} WHILE_FALSE #define DECL0(s) ADD((&easysrc_decl, s)) #define DECL1(f,a) ADDF((&easysrc_decl, f,a)) @@ -406,174 +411,238 @@ static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno) return ret; } -/* Generate source code for a mime structure. */ -static CURLcode libcurl_generate_mime(curl_mime *mime, int *mimeno) +static CURLcode libcurl_generate_mime(CURL *curl, + struct GlobalConfig *config, + tool_mime *toolmime, + curl_mime **mime, + int *mimeno); /* Forward. */ + +/* Wrapper to build and generate source code for a mime part. */ +static CURLcode libcurl_generate_mime_part(CURL *curl, + struct GlobalConfig *config, + tool_mime *part, + curl_mime *mime, + int mimeno) { CURLcode ret = CURLE_OK; - int i; - curl_off_t size; - curl_mimepart *part; - char *filename; + curl_mimepart *mimepart; + int submimeno; + curl_mime *submime = NULL; char *escaped = NULL; - char *cp; - char *data; - - /* May need several mime variables, so invent name */ - *mimeno = ++easysrc_mime_count; - - DECL1("curl_mime *mime%d;", *mimeno); - DATA1("mime%d = NULL;", *mimeno); - CODE1("mime%d = curl_mime_init(hnd);", *mimeno); - CLEAN1("curl_mime_free(mime%d);", *mimeno); - CLEAN1("mime%d = NULL;", *mimeno); - if(mime->firstpart) { - DECL1("curl_mimepart *part%d;", *mimeno); - for(part = mime->firstpart; part; part = part->nextpart) { - CODE2("part%d = curl_mime_addpart(mime%d);", *mimeno, *mimeno); - filename = part->filename; - switch(part->kind) { - case MIMEKIND_FILE: - Curl_safefree(escaped); - escaped = c_escape(part->data, CURL_ZERO_TERMINATED); - if(!escaped) - return CURLE_OUT_OF_MEMORY; - CODE2("curl_mime_filedata(part%d, \"%s\");", *mimeno, escaped); - if(!filename) - CODE1("curl_mime_filename(part%d, NULL);", *mimeno); - else { - /* Fast check to see if remote file name is base name. */ - filename = part->data; - for(cp = filename; *cp; cp++) - if(*cp == '/' || *cp == '\\') - filename = cp + 1; - if(!part->filename || !strcmp(filename, part->filename)) - filename = NULL; - else - filename = part->filename; + const char *data = NULL; + const char *filename = part->filename; + + /* Parts are linked in reverse order. */ + if(part->prev) { + ret = libcurl_generate_mime_part(curl, config, part->prev, mime, mimeno); + if(ret) + return ret; + } + + /* Create the part. */ + mimepart = curl_mime_addpart(mime); + NULL_CHECK(mimepart); + if(config->libcurl) + CODE2("part%d = curl_mime_addpart(mime%d);", mimeno, mimeno); + + switch(part->kind) { + case TOOLMIME_PARTS: + ret = libcurl_generate_mime(curl, config, part, &submime, &submimeno); + if(!ret) { + ret = curl_mime_subparts(mimepart, submime); + if(!ret) { + submime = NULL; + if(config->libcurl) { + CODE2("curl_mime_subparts(part%d, mime%d);", mimeno, submimeno); + CODE1("mime%d = NULL;", submimeno); /* Avoid freeing in CLEAN. */ } - break; - case MIMEKIND_CALLBACK: - /* Can only be reading stdin in the current context. */ - CODE1("curl_mime_data_cb(part%d, -1, (curl_read_callback) fread, \\", - *mimeno); - CODE0(" (curl_seek_callback) fseek, NULL, stdin);"); - break; - case MIMEKIND_DATA: -#ifdef CURL_DOES_CONVERSIONS - /* Data is stored in ASCII and we want in in the host character - code. Convert it back for output. */ - data = malloc(part->datasize + 1); - if(!data) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } - memcpy(data, part->data, part->datasize + 1); - ret = convert_from_network(data, strlen(data)); - if(ret) { - Curl_safefree(data); - goto nomem; - } -#else - data = part->data; -#endif + } + } + break; - /* Are there any nul byte in data? */ - for(cp = data; *cp; cp++) - ; - size = (cp == data + part->datasize)? (curl_off_t) -1: part->datasize; - Curl_safefree(escaped); - escaped = c_escape(data, (size_t) part->datasize); + case TOOLMIME_DATA: #ifdef CURL_DOES_CONVERSIONS - Curl_safefree(data); + if(config->libcurl) { + /* Data will be set in ASCII, thus issue a comment with clear text. */ + escaped = c_escape(part->data, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE1("/* \"%s\" */", escaped); + } + + /* Our data is always textual: convert it to ASCII. */ + { + size_t size = strlen(part->data); + char *cp = malloc(size + 1); + + NULL_CHECK(cp); + memcpy(cp, part->data, size + 1); + ret = convert_to_network(cp, size); + data = cp; + } +#else + data = part->data; #endif - if(!escaped) - return CURLE_OUT_OF_MEMORY; - if(size >= 0) - CODE3("curl_mime_data(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");", - *mimeno, escaped, size); - else - CODE2("curl_mime_data(part%d, \"%s\", CURL_ZERO_TERMINATED);", - *mimeno, escaped); - break; - case MIMEKIND_MULTIPART: - ret = libcurl_generate_mime(part->arg, &i); - if(ret) - goto nomem; - CODE2("curl_mime_subparts(part%d, mime%d);", *mimeno, i); - CODE1("mime%d = NULL;", i); /* Avoid freeing in CLEAN sequence. */ - break; - default: - /* Other cases not possible in this context. */ - break; + if(!ret) + ret = curl_mime_data(mimepart, data, CURL_ZERO_TERMINATED); + if(!ret && config->libcurl) { + Curl_safefree(escaped); + escaped = c_escape(data, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_data(part%d, \"%s\", CURL_ZERO_TERMINATED);", + mimeno, escaped); + } + break; + + case TOOLMIME_FILE: + case TOOLMIME_FILEDATA: + ret = curl_mime_filedata(mimepart, part->data); + if(!ret && config->libcurl) { + escaped = c_escape(part->data, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_filedata(part%d, \"%s\");", mimeno, escaped); + } + if(!ret && part->kind == TOOLMIME_FILEDATA && !filename) { + ret = curl_mime_filename(mimepart, NULL); + if(!ret && config->libcurl) + CODE1("curl_mime_filename(part%d, NULL);", mimeno); + } + break; + + case TOOLMIME_STDIN: + if(!filename) + filename = "-"; + /* FALLTHROUGH */ + case TOOLMIME_STDINDATA: + part->config = config; + ret = curl_mime_data_cb(mimepart, part->size, + (curl_read_callback) tool_mime_stdin_read, + (curl_seek_callback) tool_mime_stdin_seek, + NULL, part); + if(!ret && config->libcurl) { + /* Can only be reading stdin in the current context. */ + CODE1("curl_mime_data_cb(part%d, -1, (curl_read_callback) fread, \\", + mimeno); + CODE0(" (curl_seek_callback) fseek, NULL, stdin);"); } + break; + default: + /* Other cases not possible in this context. */ + break; + } - if(part->encoder) { - Curl_safefree(escaped); - escaped = c_escape(part->encoder->name, CURL_ZERO_TERMINATED); - if(!escaped) - return CURLE_OUT_OF_MEMORY; - CODE2("curl_mime_encoder(part%d, \"%s\");", *mimeno, escaped); - } + if(!ret && part->encoder) { + ret = curl_mime_encoder(mimepart, part->encoder); + if(!ret && config->libcurl) { + Curl_safefree(escaped); + escaped = c_escape(part->encoder, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_encoder(part%d, \"%s\");", mimeno, escaped); + } + } - if(filename) { - Curl_safefree(escaped); - escaped = c_escape(filename, CURL_ZERO_TERMINATED); - if(!escaped) - return CURLE_OUT_OF_MEMORY; - CODE2("curl_mime_filename(part%d, \"%s\");", *mimeno, escaped); - } + if(!ret && filename) { + ret = curl_mime_filename(mimepart, filename); + if(!ret && config->libcurl) { + Curl_safefree(escaped); + escaped = c_escape(filename, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_filename(part%d, \"%s\");", mimeno, escaped); + } + } - if(part->name) { - Curl_safefree(escaped); - escaped = c_escape(part->name, CURL_ZERO_TERMINATED); - if(!escaped) - return CURLE_OUT_OF_MEMORY; - CODE2("curl_mime_name(part%d, \"%s\");", *mimeno, escaped); - } + if(!ret && part->name) { + ret = curl_mime_name(mimepart, part->name); + if(!ret && config->libcurl) { + Curl_safefree(escaped); + escaped = c_escape(part->name, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_name(part%d, \"%s\");", mimeno, escaped); + } + } - if(part->mimetype) { - Curl_safefree(escaped); - escaped = c_escape(part->mimetype, CURL_ZERO_TERMINATED); - if(!escaped) - return CURLE_OUT_OF_MEMORY; - CODE2("curl_mime_type(part%d, \"%s\");", *mimeno, escaped); - } + if(!ret && part->type) { + ret = curl_mime_type(mimepart, part->type); + if(!ret && config->libcurl) { + Curl_safefree(escaped); + escaped = c_escape(part->type, CURL_ZERO_TERMINATED); + NULL_CHECK(escaped); + CODE2("curl_mime_type(part%d, \"%s\");", mimeno, escaped); + } + } - if(part->userheaders) { - int ownership = part->flags & MIME_USERHEADERS_OWNER? 1: 0; + if(!ret && part->headers) { + ret = curl_mime_headers(mimepart, part->headers, 0); + if(!ret && config->libcurl) { + int slistno; - ret = libcurl_generate_slist(part->userheaders, &i); - if(ret) - goto nomem; - CODE3("curl_mime_headers(part%d, slist%d, %d);", - *mimeno, i, ownership); - if(ownership) - CODE1("slist%d = NULL;", i); /* Prevent freeing in CLEAN sequence. */ + ret = libcurl_generate_slist(part->headers, &slistno); + if(!ret) { + CODE2("curl_mime_headers(part%d, slist%d, 1);", mimeno, slistno); + CODE1("slist%d = NULL;", slistno); /* Prevent CLEANing. */ } } } nomem: +#ifdef CURL_DOES_CONVERSIONS + if(data) + free((char *) data); +#endif + + curl_mime_free(submime); Curl_safefree(escaped); return ret; } +/* Wrapper to build and generate source code for a mime structure. */ +static CURLcode libcurl_generate_mime(CURL *curl, + struct GlobalConfig *config, + tool_mime *toolmime, + curl_mime **mime, + int *mimeno) +{ + CURLcode ret = CURLE_OK; + + *mime = curl_mime_init(curl); + NULL_CHECK(*mime); + + if(config->libcurl) { + /* May need several mime variables, so invent name. */ + *mimeno = ++easysrc_mime_count; + DECL1("curl_mime *mime%d;", *mimeno); + DATA1("mime%d = NULL;", *mimeno); + CODE1("mime%d = curl_mime_init(hnd);", *mimeno); + CLEAN1("curl_mime_free(mime%d);", *mimeno); + CLEAN1("mime%d = NULL;", *mimeno); + } + + if(toolmime->subparts) { + if(config->libcurl) + DECL1("curl_mimepart *part%d;", *mimeno); + ret = libcurl_generate_mime_part(curl, config, + toolmime->subparts, *mime, *mimeno); + } + +nomem: + return ret; +} + /* setopt wrapper for CURLOPT_MIMEPOST */ CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, - curl_mime *mimepost) + tool_mime *mimepost) { CURLcode ret = CURLE_OK; + int mimeno = 0; - ret = curl_easy_setopt(curl, tag, mimepost); - - if(config->libcurl && mimepost && !ret) { - int i; + ret = libcurl_generate_mime(curl, config, mimepost, + &mimepost->handle, &mimeno); - ret = libcurl_generate_mime(mimepost, &i); + if(!ret) { + ret = curl_easy_setopt(curl, tag, mimepost->handle); - if(!ret) - CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, i); + if(config->libcurl && !ret) + CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, mimeno); } nomem: @@ -685,10 +754,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config, else { if(escape) { escaped = c_escape(value, CURL_ZERO_TERMINATED); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } + NULL_CHECK(escaped); CODE2("curl_easy_setopt(hnd, %s, \"%s\");", name, escaped); } else diff --git a/src/tool_setopt.h b/src/tool_setopt.h index f8a52cd75..4a03a06fc 100644 --- a/src/tool_setopt.h +++ b/src/tool_setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,6 +23,8 @@ ***************************************************************************/ #include "tool_setup.h" +#include "tool_formparse.h" + /* * Macros used in operate() */ @@ -87,7 +89,7 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, const NameValueUnsigned *nv, long lval); CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, - curl_mime *mimepost); + tool_mime *mimepost); CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, struct curl_slist *list); diff --git a/tests/data/test1404 b/tests/data/test1404 index a00bf10a3..a7b8cc823 100644 --- a/tests/data/test1404 +++ b/tests/data/test1404 @@ -94,6 +94,8 @@ s/(USERAGENT, \")[^\"]+/${1}stripped/ $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ +# CURL_DOES_CONVERSION generates an extra comment. +$_ = '' if /\/\* "value" \*\// /********* Sample code generated by the curl command line tool ********** -- cgit v1.2.1