diff options
author | Patrick Monnerat <patrick@monnerat.net> | 2019-02-11 19:10:41 +0100 |
---|---|---|
committer | Patrick Monnerat <patrick@monnerat.net> | 2019-02-11 19:10:41 +0100 |
commit | cac0e4a6ad14b42471ebc62e82ecdb7dad164702 (patch) | |
tree | 46e38a8e2da72fd2b621982f882dd6b1969a6161 /src/tool_formparse.c | |
parent | b75fb9b5a6ee02beb9e2c6ed43556603f4ef64cf (diff) | |
download | curl-cac0e4a6ad14b42471ebc62e82ecdb7dad164702.tar.gz |
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
Diffstat (limited to 'src/tool_formparse.c')
-rw-r--r-- | src/tool_formparse.c | 609 |
1 files changed, 281 insertions, 328 deletions
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, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, 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 @@ -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 '<filename', field data is read from the + * given file. + * Specify files to upload with 'name=@filename', or 'name=@"filename"' * in case the filename contain ',' or ';'. Supports specified * given Content-Type of the files. Such as ';type=<content-type>'. @@ -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; |