diff options
-rw-r--r-- | docs/cmdline-opts/form-string.d | 4 | ||||
-rw-r--r-- | docs/cmdline-opts/form.d | 66 | ||||
-rw-r--r-- | src/Makefile.inc | 2 | ||||
-rw-r--r-- | src/tool_cfgable.c | 8 | ||||
-rw-r--r-- | src/tool_cfgable.h | 4 | ||||
-rw-r--r-- | src/tool_easysrc.c | 4 | ||||
-rw-r--r-- | src/tool_easysrc.h | 4 | ||||
-rw-r--r-- | src/tool_formparse.c | 656 | ||||
-rw-r--r-- | src/tool_formparse.h | 6 | ||||
-rw-r--r-- | src/tool_getparam.c | 6 | ||||
-rw-r--r-- | src/tool_help.c | 4 | ||||
-rw-r--r-- | src/tool_mfiles.c | 127 | ||||
-rw-r--r-- | src/tool_mfiles.h | 46 | ||||
-rw-r--r-- | src/tool_operate.c | 4 | ||||
-rw-r--r-- | src/tool_sdecls.h | 4 | ||||
-rw-r--r-- | src/tool_setopt.c | 267 | ||||
-rw-r--r-- | src/tool_setopt.h | 12 |
17 files changed, 768 insertions, 456 deletions
diff --git a/docs/cmdline-opts/form-string.d b/docs/cmdline-opts/form-string.d index 80790553c..49d0d44ef 100644 --- a/docs/cmdline-opts/form-string.d +++ b/docs/cmdline-opts/form-string.d @@ -1,6 +1,6 @@ Long: form-string -Help: Specify HTTP multipart POST data -Protocols: HTTP +Help: Specify multipart MIME data +Protocols: HTTP SMTP IMAP Arg: <name=string> See-also: form --- diff --git a/docs/cmdline-opts/form.d b/docs/cmdline-opts/form.d index 87a7d0766..14261d3ad 100644 --- a/docs/cmdline-opts/form.d +++ b/docs/cmdline-opts/form.d @@ -1,21 +1,26 @@ Long: form Short: F Arg: <name=content> -Help: Specify HTTP multipart POST data -Protocols: HTTP +Help: Specify multipart MIME data +Protocols: HTTP SMTP IMAP Mutexed: data head upload --- -This lets curl emulate a filled-in form in which a user has pressed the submit -button. This causes curl to POST data using the Content-Type -multipart/form-data according to RFC 2388. This enables uploading of binary +For HTTP protocol family, this lets curl emulate a filled-in form in which a +user has pressed the submit button. This causes curl to POST data using the +Content-Type multipart/form-data according to RFC 2388. + +For SMTP and IMAP protocols, this is the mean to compose a multipart mail +message to transmit. + +This enables uploading of binary files etc. To force the 'content' part to be a file, prefix the file name with an @ sign. To just get the content part from a file, prefix the file name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a file. -Example: to send an image to a server, where \&'profile' is the name of the -form-field to which portrait.jpg will be the input: +Example: to send an image to an HTTP server, where \&'profile' is the name of +the form-field to which portrait.jpg will be the input: curl -F profile=@portrait.jpg https://example.com/upload.cgi @@ -49,6 +54,53 @@ or Note that if a filename/path is quoted by double-quotes, any double-quote or backslash within the filename must be escaped by backslash. +You can add custom headers to the field by setting headers=, like + + curl -F "submit=OK;headers=\\"X-submit-type: OK\\"" example.com + +or + + curl -F "submit=OK;headers=@headerfile" example.com + +The headers= keyword may appear more that once and above notes about quoting +apply. When headers are read from a file, Empty lines and lines starting +with '#' are comments and ignored; each header can be folded by splitting +between two words and starting the continuation line with a space; embedded +carriage-returns and trailing spaces are stripped. +Here is an example of a header file contents: + + # This file contain two headers. +.br + X-header-1: this is a header + + # The following header is folded. +.br + X-header-2: this is +.br + another header + + +To support sending multipart mail messages, the syntax is extended as follows: +.br +- name can be omitted: the equal sign is the first character of the argument, +.br +- if data starts with '(', this signals to start a new multipart: it can be +followed by a content type specification. +.br +- a multipart can be terminated with a '=)' argument. + +Example: the following command sends an SMTP mime e-mail consisting in an +inline part in two alternative formats: plain text and HTML. It attaches a +text file: + + curl -F '=(;type=multipart/alternative' \\ +.br + -F '=plain text message' \\ +.br + -F '= <body>HTML message</body>;type=text/html' \\ +.br + -F '=)' -F '=@textfile.txt' ... smtp://example.com + See further examples and details in the MANUAL. This option can be used multiple times. diff --git a/src/Makefile.inc b/src/Makefile.inc index 5074f8fc6..45b4967f6 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -45,7 +45,6 @@ CURL_CFILES = \ tool_libinfo.c \ tool_main.c \ tool_metalink.c \ - tool_mfiles.c \ tool_msgs.c \ tool_operate.c \ tool_operhlp.c \ @@ -86,7 +85,6 @@ CURL_HFILES = \ tool_libinfo.h \ tool_main.h \ tool_metalink.h \ - tool_mfiles.h \ tool_msgs.h \ tool_operate.h \ tool_operhlp.h \ diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 675e88b45..755195ced 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -140,11 +140,11 @@ static void free_config_fields(struct OperationConfig *config) curl_slist_free_all(config->headers); curl_slist_free_all(config->proxyheaders); - if(config->httppost) { - curl_formfree(config->httppost); - config->httppost = NULL; + if(config->mimepost) { + curl_mime_free(config->mimepost); + config->mimepost = NULL; } - config->last_post = NULL; + config->mimecurrent = NULL; curl_slist_free_all(config->telnet_options); curl_slist_free_all(config->resolve); diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 2b436657f..23943fe7b 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -170,8 +170,8 @@ struct OperationConfig { time_t condtime; struct curl_slist *headers; struct curl_slist *proxyheaders; - struct curl_httppost *httppost; - struct curl_httppost *last_post; + curl_mime *mimepost; + curl_mime *mimecurrent; struct curl_slist *telnet_options; struct curl_slist *resolve; struct curl_slist *connect_to; diff --git a/src/tool_easysrc.c b/src/tool_easysrc.c index c2dccf9fb..602409d14 100644 --- a/src/tool_easysrc.c +++ b/src/tool_easysrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -42,7 +42,7 @@ struct slist_wc *easysrc_data = NULL; /* Build slists, forms etc. */ struct slist_wc *easysrc_code = NULL; /* Setopt calls */ struct slist_wc *easysrc_toohard = NULL; /* Unconvertible setopt */ struct slist_wc *easysrc_clean = NULL; /* Clean up allocated data */ -int easysrc_form_count = 0; +int easysrc_mime_count = 0; int easysrc_slist_count = 0; static const char *const srchead[]={ diff --git a/src/tool_easysrc.h b/src/tool_easysrc.h index 54607b861..fd799ab84 100644 --- a/src/tool_easysrc.h +++ b/src/tool_easysrc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -32,7 +32,7 @@ extern struct slist_wc *easysrc_code; /* Setopt calls etc. */ extern struct slist_wc *easysrc_toohard; /* Unconvertible setopt */ extern struct slist_wc *easysrc_clean; /* Clean up (reverse order) */ -extern int easysrc_form_count; /* Number of curl_httppost variables */ +extern int easysrc_mime_count; /* Number of curl_mime variables */ extern int easysrc_slist_count; /* Number of curl_slist variables */ extern CURLcode easysrc_init(void); diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 952377c49..cf19c0508 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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,6 +21,7 @@ ***************************************************************************/ #include "tool_setup.h" +#include "mime.h" #include "strcase.h" #define ENABLE_CURLX_PRINTF @@ -29,7 +30,6 @@ #include "tool_cfgable.h" #include "tool_convert.h" -#include "tool_mfiles.h" #include "tool_msgs.h" #include "tool_formparse.h" @@ -94,6 +94,259 @@ static char *get_param_word(char **str, char **end_pos) return word_begin; } +/* Append slist item and return -1 if failed. */ +static int slist_append(struct curl_slist **plist, const char *data) +{ + struct curl_slist *s = curl_slist_append(*plist, data); + + if(!s) + return -1; + + *plist = s; + return 0; +} + +/* Read headers from a file and append to list. */ +static int read_field_headers(struct OperationConfig *config, + const char *filename, FILE *fp, + struct curl_slist **pheaders) +{ + size_t hdrlen = 0; + size_t pos = 0; + int c; + bool incomment = FALSE; + int lineno = 1; + char hdrbuf[999]; /* Max. header length + 1. */ + + for(;;) { + c = getc(fp); + if(c == EOF || (!pos && !ISSPACE(c))) { + /* Strip and flush the current header. */ + while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1])) + hdrlen--; + if(hdrlen) { + hdrbuf[hdrlen] = '\0'; + if(slist_append(pheaders, hdrbuf)) { + fprintf(config->global->errors, + "Out of memory for field headers!\n"); + return -1; + } + hdrlen = 0; + } + } + + switch(c) { + case EOF: + if(ferror(fp)) { + fprintf(config->global->errors, + "Header file %s read error: %s\n", filename, strerror(errno)); + return -1; + } + return 0; /* Done. */ + case '\r': + continue; /* Ignore. */ + case '\n': + pos = 0; + incomment = FALSE; + lineno++; + continue; + case '#': + if(!pos) + incomment = TRUE; + break; + } + + pos++; + if(!incomment) { + if(hdrlen == sizeof hdrbuf - 1) { + warnf(config->global, "File %s line %d: header too long (truncated)\n", + filename, lineno); + c = ' '; + } + if(hdrlen <= sizeof hdrbuf - 1) + hdrbuf[hdrlen++] = (char) c; + } + } + /* NOTREACHED */ +} + +static int get_param_part(struct OperationConfig *config, char **str, + char **pdata, char **ptype, char **pfilename, + struct curl_slist **pheaders) +{ + char *p = *str; + char *type = NULL; + char *filename = NULL; + char *endpos; + char *tp; + char sep; + char type_major[128] = ""; + char type_minor[128] = ""; + char *endct = NULL; + struct curl_slist *headers = NULL; + + if(ptype) + *ptype = NULL; + if(pfilename) + *pfilename = NULL; + if(pheaders) + *pheaders = NULL; + while(ISSPACE(*p)) + p++; + tp = p; + *pdata = get_param_word(&p, &endpos); + /* If not quoted, strip trailing spaces. */ + if(*pdata == tp) + while(endpos > *pdata && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + while(sep == ';') { + while(ISSPACE(*++p)) + ; + + if(!endct && checkprefix("type=", p)) { + for(p += 5; ISSPACE(*p); p++) + ; + /* set type pointer */ + type = p; + + /* verify that this is a fine type specifier */ + if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) { + warnf(config->global, "Illegally formatted content-type field!\n"); + curl_slist_free_all(headers); + return -1; /* illegal content-type syntax! */ + } + + /* now point beyond the content-type specifier */ + endpos = type + strlen(type_major) + strlen(type_minor) + 1; + for(p = endpos; ISSPACE(*p); p++) + ; + while(*p && *p != ';' && *p != ',') + p++; + endct = p; + sep = *p; + } + else if(checkprefix("filename=", p)) { + if(endct) { + *endct = '\0'; + endct = NULL; + } + for(p += 9; ISSPACE(*p); p++) + ; + tp = p; + filename = get_param_word(&p, &endpos); + /* If not quoted, strip trailing spaces. */ + if(filename == tp) + while(endpos > filename && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + } + else if(checkprefix("headers=", p)) { + if(endct) { + *endct = '\0'; + endct = NULL; + } + p += 8; + if(*p == '@' || *p == '<') { + char *hdrfile; + FILE *fp; + /* Read headers from a file. */ + + do { + p++; + } while(ISSPACE(*p)); + tp = p; + hdrfile = get_param_word(&p, &endpos); + /* If not quoted, strip trailing spaces. */ + if(hdrfile == tp) + while(endpos > hdrfile && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + /* TODO: maybe special fopen for VMS? */ + fp = fopen(hdrfile, FOPEN_READTEXT); + if(!fp) + warnf(config->global, "Cannot read from %s: %s\n", hdrfile, + strerror(errno)); + else { + int i = read_field_headers(config, hdrfile, fp, &headers); + + fclose(fp); + if(i) { + curl_slist_free_all(headers); + return -1; + } + } + } + else { + char *hdr; + + while(ISSPACE(*p)) + p++; + tp = p; + hdr = get_param_word(&p, &endpos); + /* If not quoted, strip trailing spaces. */ + if(hdr == tp) + while(endpos > hdr && ISSPACE(endpos[-1])) + endpos--; + sep = *p; + *endpos = '\0'; + if(slist_append(&headers, hdr)) { + fprintf(config->global->errors, "Out of memory for field header!\n"); + curl_slist_free_all(headers); + return -1; + } + } + } + else { + /* unknown prefix, skip to next block */ + char *unknown = get_param_word(&p, &endpos); + + sep = *p; + if(endct) + endct = p; + else { + *endpos = '\0'; + if(*unknown) + warnf(config->global, "skip unknown form field: %s\n", unknown); + } + } + } + + /* Terminate and strip content type. */ + if(type) { + if(!endct) + endct = type + strlen(type); + while(endct > type && ISSPACE(endct[-1])) + endct--; + *endct = '\0'; + } + + if(ptype) + *ptype = type; + else if(type) + warnf(config->global, "Field content type not allowed here: %s\n", type); + + if(pfilename) + *pfilename = filename; + else if(filename) + warnf(config->global, + "Field file name not allowed here: %s\n", filename); + + if(pheaders) + *pheaders = headers; + else if(headers) { + warnf(config->global, + "Field headers not allowed here: %s\n", headers->data); + curl_slist_free_all(headers); + } + + *str = p; + return sep & 0xFF; +} + /*************************************************************************** * * formparse() @@ -143,219 +396,302 @@ static char *get_param_word(char **str, char **end_pos) int formparse(struct OperationConfig *config, const char *input, - struct curl_httppost **httppost, - struct curl_httppost **last_post, + curl_mime **mimepost, + curl_mime **mimecurrent, bool literal_value) { - /* nextarg MUST be a string in the format 'name=contents' and we'll + /* input MUST be a string in the format 'name=contents' and we'll build a linked list with the info */ - char name[256]; + char *name = NULL; char *contents = NULL; - char type_major[128] = ""; - char type_minor[128] = ""; char *contp; + char *data; char *type = NULL; - char *sep; - - if((1 == sscanf(input, "%255[^=]=", name)) && - ((contp = strchr(input, '=')) != NULL)) { - /* the input was using the correct format */ - - /* Allocate the contents */ - contents = strdup(contp+1); - if(!contents) { - fprintf(config->global->errors, "out of memory\n"); + char *filename = NULL; + struct curl_slist *headers = NULL; + curl_mimepart *part = NULL; + CURLcode res; + int sep = '\0'; + + /* 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; } - contp = contents; + *mimecurrent = *mimepost; + } - if('@' == contp[0] && !literal_value) { + /* Make a copy we can overwrite. */ + contents = strdup(input); + if(!contents) { + fprintf(config->global->errors, "out of memory\n"); + return 2; + } - /* we use the @-letter to indicate file name(s) */ + /* Scan for the end of the name. */ + contp = strchr(contents, '='); + if(contp) { + if(contp > contents) + name = contents; + *contp++ = '\0'; + + if(*contp == '(' && !literal_value) { + curl_mime *subparts; + + /* Starting a multipart. */ + sep = get_param_part(config, &contp, &data, &type, NULL, &headers); + if(sep < 0) { + 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; + } + } + else if(!name && !strcmp(contp, ")") && !literal_value) { + /* Ending a mutipart. */ + if(*mimecurrent == *mimepost) { + warnf(config->global, "no multipart to terminate!\n"); + Curl_safefree(contents); + return 9; + } + *mimecurrent = (*mimecurrent)->parent->parent; + } + else if('@' == contp[0] && !literal_value) { - struct multi_files *multi_start = NULL; - struct multi_files *multi_current = NULL; + /* we use the @-letter to indicate file name(s) */ - char *ptr = contp; - char *end = ptr + strlen(ptr); + curl_mime *subparts = NULL; do { /* since this was a file, it may have a content-type specifier at the end too, or a filename. Or both. */ - char *filename = NULL; - char *word_end; - bool semicolon; - - type = NULL; - - ++ptr; - contp = get_param_word(&ptr, &word_end); - semicolon = (';' == *ptr) ? TRUE : FALSE; - *word_end = '\0'; /* terminate the contp */ - - /* have other content, continue parse */ - while(semicolon) { - /* have type or filename field */ - ++ptr; - while(*ptr && (ISSPACE(*ptr))) - ++ptr; - - if(checkprefix("type=", ptr)) { - /* set type pointer */ - type = &ptr[5]; - - /* verify that this is a fine type specifier */ - if(2 != sscanf(type, "%127[^/]/%127[^;,\n]", - type_major, type_minor)) { - warnf(config->global, - "Illegally formatted content-type field!\n"); - Curl_safefree(contents); - FreeMultiInfo(&multi_start, &multi_current); - return 2; /* illegal content-type syntax! */ - } - - /* now point beyond the content-type specifier */ - sep = type + strlen(type_major)+strlen(type_minor)+1; - - /* there's a semicolon following - we check if it is a filename - specified and if not we simply assume that it is text that - the user wants included in the type and include that too up - to the next sep. */ - ptr = sep; - if(*sep==';') { - if(!checkprefix(";filename=", sep)) { - ptr = sep + 1; - (void)get_param_word(&ptr, &sep); - semicolon = (';' == *ptr) ? TRUE : FALSE; - } - } - else - semicolon = FALSE; + ++contp; + sep = get_param_part(config, + &contp, &data, &type, &filename, &headers); + if(sep < 0) { + if(subparts != *mimecurrent) + curl_mime_free(subparts); + Curl_safefree(contents); + return 10; + } - if(*sep) - *sep = '\0'; /* zero terminate type string */ - } - else if(checkprefix("filename=", ptr)) { - ptr += 9; - filename = get_param_word(&ptr, &word_end); - semicolon = (';' == *ptr) ? TRUE : FALSE; - *word_end = '\0'; - } + /* now contp point to comma or string end. + If more files to come, make sure we have multiparts. */ + if(!subparts) { + if(sep != ',') /* If there is a single file. */ + subparts = *mimecurrent; else { - /* unknown prefix, skip to next block */ - char *unknown = NULL; - unknown = get_param_word(&ptr, &word_end); - semicolon = (';' == *ptr) ? TRUE : FALSE; - if(*unknown) { - *word_end = '\0'; - warnf(config->global, "skip unknown form field: %s\n", unknown); + 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; } } } - /* now ptr point to comma or string end */ + /* 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; + } - /* if type == NULL curl_formadd takes care of the problem */ + /* 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; + } - if(*contp && !AddMultiFiles(contp, type, filename, &multi_start, - &multi_current)) { - warnf(config->global, "Error building form post!\n"); + /* Setup file in part. */ + res = curl_mime_filedata(part, data); + if(res) { + warnf(config->global, "curl_mime_filedata failed!\n"); + if(res != CURLE_READ_ERROR) { + if(subparts != *mimecurrent) + curl_mime_free(subparts); + Curl_safefree(contents); + return 14; + } + } + 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(type && curl_mime_type(part, type)) { + warnf(config->global, "curl_mime_type failed!\n"); + if(subparts != *mimecurrent) + curl_mime_free(subparts); Curl_safefree(contents); - FreeMultiInfo(&multi_start, &multi_current); - return 3; + return 16; } - /* *ptr could be '\0', so we just check with the string end */ - } while(ptr < end); /* loop if there's another file name */ + /* *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(multi_start) { - struct curl_forms *forms = NULL; - struct multi_files *start = multi_start; - unsigned int i, count = 0; - while(start) { - start = start->next; - ++count; - } - forms = malloc((count+1)*sizeof(struct curl_forms)); - if(!forms) { - fprintf(config->global->errors, "Error building form post!\n"); + 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); - FreeMultiInfo(&multi_start, &multi_current); - return 4; + return 17; } - for(i = 0, start = multi_start; i < count; ++i, start = start->next) { - forms[i].option = start->form.option; - forms[i].value = start->form.value; - } - forms[count].option = CURLFORM_END; - FreeMultiInfo(&multi_start, &multi_current); - if(curl_formadd(httppost, last_post, - CURLFORM_COPYNAME, name, - CURLFORM_ARRAY, forms, CURLFORM_END) != 0) { - warnf(config->global, "curl_formadd failed!\n"); - Curl_safefree(forms); + if(curl_mime_subparts(part, subparts)) { + warnf(config->global, "curl_mime_subparts failed!\n"); + curl_mime_free(subparts); Curl_safefree(contents); - return 5; + return 18; } - Curl_safefree(forms); } } else { - struct curl_forms info[4]; - int i = 0; - char *ct = literal_value ? NULL : strstr(contp, ";type="); - - info[i].option = CURLFORM_COPYNAME; - info[i].value = name; - i++; - - if(ct) { - info[i].option = CURLFORM_CONTENTTYPE; - info[i].value = &ct[6]; - i++; - ct[0] = '\0'; /* zero terminate here */ - } + /* Allocate a mime part. */ + part = curl_mime_addpart(*mimecurrent); + if(!part) { + warnf(config->global, "curl_mime_addpart failed!\n"); + Curl_safefree(contents); + return 19; + } - if(contp[0]=='<' && !literal_value) { - info[i].option = CURLFORM_FILECONTENT; - info[i].value = contp+1; - i++; - info[i].option = CURLFORM_END; + if(*contp == '<' && !literal_value) { + ++contp; + sep = get_param_part(config, + &contp, &data, &type, &filename, &headers); + if(sep < 0) { + Curl_safefree(contents); + return 20; + } - if(curl_formadd(httppost, last_post, - CURLFORM_ARRAY, info, CURLFORM_END) != 0) { - warnf(config->global, "curl_formadd failed, possibly the file %s is " - "bad!\n", contp + 1); + /* 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 6; + return 21; + } + + /* Setup file in part. */ + res = curl_mime_filedata(part, data); + if(res) { + warnf(config->global, "curl_mime_filedata failed!\n"); + if(res != CURLE_READ_ERROR) { + Curl_safefree(contents); + return 22; + } } } else { + if(literal_value) + data = contp; + else { + sep = get_param_part(config, + &contp, &data, &type, &filename, &headers); + if(sep < 0) { + Curl_safefree(contents); + return 23; + } + } + + /* 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 24; + } + #ifdef CURL_DOES_CONVERSIONS - if(convert_to_network(contp, strlen(contp))) { + if(convert_to_network(data, strlen(data))) { warnf(config->global, "curl_formadd failed!\n"); Curl_safefree(contents); - return 7; + return 25; } #endif - info[i].option = CURLFORM_COPYCONTENTS; - info[i].value = contp; - i++; - info[i].option = CURLFORM_END; - if(curl_formadd(httppost, last_post, - CURLFORM_ARRAY, info, CURLFORM_END) != 0) { - warnf(config->global, "curl_formadd failed!\n"); + + if(curl_mime_data(part, data, -1)) { + warnf(config->global, "curl_mime_data failed!\n"); Curl_safefree(contents); - return 8; + return 26; } } + + if(curl_mime_filename(part, filename)) { + warnf(config->global, "curl_mime_filename failed!\n"); + Curl_safefree(contents); + return 27; + } + if(curl_mime_type(part, type)) { + warnf(config->global, "curl_mime_type failed!\n"); + Curl_safefree(contents); + return 28; + } + + if(sep) { + *contp = (char) sep; + warnf(config->global, + "garbage at end of field specification: %s\n", contp); + } } + /* Set part name. */ + if(name && curl_mime_name(part, name, -1)) { + warnf(config->global, "curl_mime_name failed!\n"); + Curl_safefree(contents); + return 29; + } } else { warnf(config->global, "Illegally formatted input field!\n"); - return 1; + Curl_safefree(contents); + return 30; } Curl_safefree(contents); return 0; diff --git a/src/tool_formparse.h b/src/tool_formparse.h index ce7a29256..a52b98d39 100644 --- a/src/tool_formparse.h +++ b/src/tool_formparse.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -25,8 +25,8 @@ int formparse(struct OperationConfig *config, const char *input, - struct curl_httppost **httppost, - struct curl_httppost **last_post, + curl_mime **mimepost, + curl_mime **mimecurrent, bool literal_value); #endif /* HEADER_CURL_TOOL_FORMPARSE_H */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 3dd1dec3f..31fc7a1ff 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -1606,11 +1606,11 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ to sort this out slowly and carefully */ if(formparse(config, nextarg, - &config->httppost, - &config->last_post, + &config->mimepost, + &config->mimecurrent, (subletter=='s')?TRUE:FALSE)) /* 's' means literal string */ return PARAM_BAD_USE; - if(SetHTTPrequest(config, HTTPREQ_FORMPOST, &config->httpreq)) + if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq)) return PARAM_BAD_USE; break; diff --git a/src/tool_help.c b/src/tool_help.c index 7b982aa96..486f65dc8 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -131,9 +131,9 @@ static const struct helptxt helptext[] = { {" --false-start", "Enable TLS False Start"}, {"-F, --form <name=content>", - "Specify HTTP multipart POST data"}, + "Specify multipart MIME data"}, {" --form-string <name=string>", - "Specify HTTP multipart POST data"}, + "Specify multipart MIME data"}, {" --ftp-account <data>", "Account data string"}, {" --ftp-alternative-to-user <command>", diff --git a/src/tool_mfiles.c b/src/tool_mfiles.c deleted file mode 100644 index d862d4140..000000000 --- a/src/tool_mfiles.c +++ /dev/null @@ -1,127 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2012, 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 https://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 "tool_setup.h" - -#include "tool_mfiles.h" - -#include "memdebug.h" /* keep this as LAST include */ - -static void AppendNode(struct multi_files **first, - struct multi_files **last, - struct multi_files *new) -{ - DEBUGASSERT(((*first) && (*last)) || ((!*first) && (!*last))); - - if(*last) - (*last)->next = new; - else - *first = new; - *last = new; -} - -/* - * AddMultiFiles: Add a new list node possibly followed with a type_name. - * - * multi_first argument is the address of a pointer to the first element - * of the multi_files linked list. A NULL pointer indicates empty list. - * - * multi_last argument is the address of a pointer to the last element - * of the multi_files linked list. A NULL pointer indicates empty list. - * - * Pointers stored in multi_first and multi_last are modified while - * function is executed. An out of memory condition free's the whole - * list and returns with pointers stored in multi_first and multi_last - * set to NULL and a NULL function result. - * - * Function returns same pointer as stored at multi_last. - */ - -struct multi_files *AddMultiFiles(const char *file_name, - const char *type_name, - const char *show_filename, - struct multi_files **multi_first, - struct multi_files **multi_last) -{ - struct multi_files *multi; - struct multi_files *multi_type; - struct multi_files *multi_name; - - multi = calloc(1, sizeof(struct multi_files)); - if(multi) { - multi->form.option = CURLFORM_FILE; - multi->form.value = file_name; - AppendNode(multi_first, multi_last, multi); - } - else { - FreeMultiInfo(multi_first, multi_last); - return NULL; - } - - if(type_name) { - multi_type = calloc(1, sizeof(struct multi_files)); - if(multi_type) { - multi_type->form.option = CURLFORM_CONTENTTYPE; - multi_type->form.value = type_name; - AppendNode(multi_first, multi_last, multi_type); - } - else { - FreeMultiInfo(multi_first, multi_last); - return NULL; - } - } - - if(show_filename) { - multi_name = calloc(1, sizeof(struct multi_files)); - if(multi_name) { - multi_name->form.option = CURLFORM_FILENAME; - multi_name->form.value = show_filename; - AppendNode(multi_first, multi_last, multi_name); - } - else { - FreeMultiInfo(multi_first, multi_last); - return NULL; - } - } - - return *multi_last; -} - -/* - * FreeMultiInfo: Free the items of the list. - */ - -void FreeMultiInfo(struct multi_files **multi_first, - struct multi_files **multi_last) -{ - struct multi_files *next; - struct multi_files *item = *multi_first; - - while(item) { - next = item->next; - Curl_safefree(item); - item = next; - } - *multi_first = NULL; - if(multi_last) - *multi_last = NULL; -} - diff --git a/src/tool_mfiles.h b/src/tool_mfiles.h deleted file mode 100644 index 827f40065..000000000 --- a/src/tool_mfiles.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef HEADER_CURL_TOOL_MFILES_H -#define HEADER_CURL_TOOL_MFILES_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2012, 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 https://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 "tool_setup.h" - -/* - * Structure for storing the information needed to build - * a multiple files section. - */ - -struct multi_files { - struct curl_forms form; - struct multi_files *next; -}; - -struct multi_files *AddMultiFiles(const char *file_name, - const char *type_name, - const char *show_filename, - struct multi_files **multi_first, - struct multi_files **multi_last); - -void FreeMultiInfo(struct multi_files **multi_first, - struct multi_files **multi_last); - -#endif /* HEADER_CURL_TOOL_MFILES_H */ - diff --git a/src/tool_operate.c b/src/tool_operate.c index fd9a13921..25ea9c4e6 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1002,8 +1002,8 @@ static CURLcode operate_do(struct GlobalConfig *global, my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, config->postfieldsize); break; - case HTTPREQ_FORMPOST: - my_setopt_httppost(curl, CURLOPT_HTTPPOST, config->httppost); + case HTTPREQ_MIMEPOST: + my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); break; default: break; diff --git a/src/tool_sdecls.h b/src/tool_sdecls.h index a56390e59..48dd4aed9 100644 --- a/src/tool_sdecls.h +++ b/src/tool_sdecls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -135,7 +135,7 @@ typedef enum { HTTPREQ_UNSPEC, /* first in list */ HTTPREQ_GET, HTTPREQ_HEAD, - HTTPREQ_FORMPOST, + HTTPREQ_MIMEPOST, HTTPREQ_SIMPLEPOST } HttpReq; diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 694d3ffa5..6cb6bb3e3 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -30,7 +30,9 @@ #include "tool_cfgable.h" #include "tool_easysrc.h" #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 */ @@ -208,14 +210,14 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = { /* Escape string to C string syntax. Return NULL if out of memory. * Is this correct for those wacky EBCDIC guys? */ -static char *c_escape(const char *str) +static char *c_escape(const char *str, ssize_t len) { - size_t len = 0; const char *s; unsigned char c; char *escaped, *e; /* Allocate space based on worst-case */ - len = strlen(str); + if(len < 0) + len = strlen(str); escaped = malloc(4 * len + 1); if(!escaped) return NULL; @@ -371,118 +373,215 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, return ret; } -/* setopt wrapper for CURLOPT_HTTPPOST */ -CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config, - const char *name, CURLoption tag, - struct curl_httppost *post) +/* Generate code for a struct curl_slist. */ +static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno) { CURLcode ret = CURLE_OK; char *escaped = NULL; - bool skip = FALSE; - ret = curl_easy_setopt(curl, tag, post); - if(!post) - skip = TRUE; + /* May need several slist variables, so invent name */ + *slistno = ++easysrc_slist_count; + + DECL1("struct curl_slist *slist%d;", *slistno); + DATA1("slist%d = NULL;", *slistno); + CLEAN1("curl_slist_free_all(slist%d);", *slistno); + CLEAN1("slist%d = NULL;", *slistno); + for(; slist; slist = slist->next) { + Curl_safefree(escaped); + escaped = c_escape(slist->data, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + DATA3("slist%d = curl_slist_append(slist%d, \"%s\");", + *slistno, *slistno, escaped); + } - if(config->libcurl && !skip && !ret) { - struct curl_httppost *pp, *p; - int i; - /* May use several httppost lists, if multiple POST actions */ - i = ++ easysrc_form_count; - DECL1("struct curl_httppost *post%d;", i); - DATA1("post%d = NULL;", i); - CLEAN1("curl_formfree(post%d);", i); - CLEAN1("post%d = NULL;", i); - if(i == 1) - DECL0("struct curl_httppost *postend;"); - DATA0("postend = NULL;"); - for(p=post; p; p=p->next) { - DATA1("curl_formadd(&post%d, &postend,", i); - DATA1(" CURLFORM_COPYNAME, \"%s\",", p->name); - for(pp=p; pp; pp=pp->more) { - /* May be several files uploaded for one name; - * these are linked through the 'more' pointer */ + nomem: + Curl_safefree(escaped); + return ret; +} + +/* Generate source code for a mime structure. */ +static CURLcode libcurl_generate_mime(curl_mime *mime, int *mimeno) +{ + CURLcode ret = CURLE_OK; + int i; + curl_off_t size; + curl_mimepart *part; + char *filename; + 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_NAMEDFILE: Curl_safefree(escaped); - escaped = c_escape(pp->contents); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } - if(pp->flags & CURL_HTTPPOST_FILENAME) { - /* file upload as for -F @filename */ - DATA1(" CURLFORM_FILE, \"%s\",", escaped); - } - else if(pp->flags & CURL_HTTPPOST_READFILE) { - /* content from file as for -F <filename */ - DATA1(" CURLFORM_FILECONTENT, \"%s\",", escaped); + escaped = c_escape(part->data, -1); + 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; } - else - DATA1(" CURLFORM_COPYCONTENTS, \"%s\",", escaped); - if(pp->showfilename) { - Curl_safefree(escaped); - escaped = c_escape(pp->showfilename); - if(!escaped) { + break; + case MIMEKIND_FILE: + /* Can only be stdin in the current context. */ + CODE1("curl_mime_file(part%d, \"-\");", *mimeno); + 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; } - DATA1(" CURLFORM_FILENAME, \"%s\",", escaped); - } - if(pp->contenttype) { - Curl_safefree(escaped); - escaped = c_escape(pp->contenttype); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; + memcpy(data, part->data, part->datasize + 1); + ret = convert_from_network(data, strlen(data)); + if(ret) { + Curl_safefree(data); goto nomem; } - DATA1(" CURLFORM_CONTENTTYPE, \"%s\",", escaped); - } +#else + data = part->data; +#endif + + /* 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, part->datasize); + if(data != part->data) + Curl_safefree(data); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE3("curl_mime_data(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");", + *mimeno, escaped, size); + 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(filename) { + Curl_safefree(escaped); + escaped = c_escape(filename, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE2("curl_mime_filename(part%d, \"%s\");", *mimeno, escaped); + } + + if(part->name) { + Curl_safefree(escaped); + escaped = c_escape(part->name, part->namesize); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + for(cp = part->name; *cp; cp++) + ; + size = (cp == part->name + part->namesize)? + (curl_off_t) -1: (curl_off_t) part->namesize; + CODE3("curl_mime_name(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");", + *mimeno, escaped, size); + } + + if(part->mimetype) { + Curl_safefree(escaped); + escaped = c_escape(part->mimetype, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE2("curl_mime_type(part%d, \"%s\");", *mimeno, escaped); + } + + if(part->userheaders) { + int ownership = part->flags & MIME_USERHEADERS_OWNER? 1: 0; + + 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. */ } - DATA0(" CURLFORM_END);"); } - CODE2("curl_easy_setopt(hnd, %s, post%d);", name, i); } - nomem: +nomem: Curl_safefree(escaped); return ret; } +/* setopt wrapper for CURLOPT_MIMEPOST */ +CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, + const char *name, CURLoption tag, + curl_mime *mimepost) +{ + CURLcode ret = CURLE_OK; + + ret = curl_easy_setopt(curl, tag, mimepost); + + if(config->libcurl && mimepost && !ret) { + int i; + + ret = libcurl_generate_mime(mimepost, &i); + + if(!ret) + CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, i); + } + +nomem: + return ret; +} + /* setopt wrapper for curl_slist options */ CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, struct curl_slist *list) { CURLcode ret = CURLE_OK; - char *escaped = NULL; - bool skip = FALSE; ret = curl_easy_setopt(curl, tag, list); - if(!list) - skip = TRUE; - if(config->libcurl && !skip && !ret) { - struct curl_slist *s; + if(config->libcurl && list && !ret) { int i; - /* May need several slist variables, so invent name */ - i = ++ easysrc_slist_count; - DECL1("struct curl_slist *slist%d;", i); - DATA1("slist%d = NULL;", i); - CLEAN1("curl_slist_free_all(slist%d);", i); - CLEAN1("slist%d = NULL;", i); - for(s=list; s; s=s->next) { - Curl_safefree(escaped); - escaped = c_escape(s->data); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } - DATA3("slist%d = curl_slist_append(slist%d, \"%s\");", i, i, escaped); - } - CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); + + ret = libcurl_generate_slist(list, &i); + if(!ret) + CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); } nomem: - Curl_safefree(escaped); return ret; } @@ -569,7 +668,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config, REM2("%s set to a %s", name, value); else { if(escape) { - escaped = c_escape(value); + escaped = c_escape(value, -1); if(!escaped) { ret = CURLE_OUT_OF_MEMORY; goto nomem; diff --git a/src/tool_setopt.h b/src/tool_setopt.h index da67deeb6..f8a52cd75 100644 --- a/src/tool_setopt.h +++ b/src/tool_setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, 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 @@ -85,9 +85,9 @@ CURLcode tool_setopt_flags(CURL *curl, struct GlobalConfig *config, CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, const NameValueUnsigned *nv, long lval); -CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config, +CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, - struct curl_httppost *httppost); + curl_mime *mimepost); CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, struct curl_slist *list); @@ -109,8 +109,8 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config, #define my_setopt_bitmask(x,y,z) \ SETOPT_CHECK(tool_setopt_bitmask(x, global, #y, y, setopt_nv_ ## y, z)) -#define my_setopt_httppost(x,y,z) \ - SETOPT_CHECK(tool_setopt_httppost(x, global, #y, y, z)) +#define my_setopt_mimepost(x,y,z) \ + SETOPT_CHECK(tool_setopt_mimepost(x, global, #y, y, z)) #define my_setopt_slist(x,y,z) \ SETOPT_CHECK(tool_setopt_slist(x, global, #y, y, z)) @@ -138,7 +138,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config, #define my_setopt_bitmask(x,y,z) \ SETOPT_CHECK(curl_easy_setopt(x, y, z)) -#define my_setopt_httppost(x,y,z) \ +#define my_setopt_mimepost(x,y,z) \ SETOPT_CHECK(curl_easy_setopt(x, y, z)) #define my_setopt_slist(x,y,z) \ |