mime: use in curl cli tool instead of form API.

Extended -F option syntax to support multipart mail messages.
-F keyword headers= added to include custom headers in parts.
Documentation upgraded.
This commit is contained in:
Patrick Monnerat 2017-09-02 18:17:33 +01:00
parent ce0881edee
commit fec7a858b8
17 changed files with 785 additions and 473 deletions

View File

@ -1,6 +1,6 @@
Long: form-string Long: form-string
Help: Specify HTTP multipart POST data Help: Specify multipart MIME data
Protocols: HTTP Protocols: HTTP SMTP IMAP
Arg: <name=string> Arg: <name=string>
See-also: form See-also: form
--- ---

View File

@ -1,21 +1,26 @@
Long: form Long: form
Short: F Short: F
Arg: <name=content> Arg: <name=content>
Help: Specify HTTP multipart POST data Help: Specify multipart MIME data
Protocols: HTTP Protocols: HTTP SMTP IMAP
Mutexed: data head upload Mutexed: data head upload
--- ---
This lets curl emulate a filled-in form in which a user has pressed the submit For HTTP protocol family, this lets curl emulate a filled-in form in which a
button. This causes curl to POST data using the Content-Type user has pressed the submit button. This causes curl to POST data using the
multipart/form-data according to RFC 2388. This enables uploading of binary 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 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 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 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 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. 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 Example: to send an image to an HTTP server, where \&'profile' is the name of
form-field to which portrait.jpg will be the input: the form-field to which portrait.jpg will be the input:
curl -F profile=@portrait.jpg https://example.com/upload.cgi 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 Note that if a filename/path is quoted by double-quotes, any double-quote
or backslash within the filename must be escaped by backslash. 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. See further examples and details in the MANUAL.
This option can be used multiple times. This option can be used multiple times.

View File

@ -45,7 +45,6 @@ CURL_CFILES = \
tool_libinfo.c \ tool_libinfo.c \
tool_main.c \ tool_main.c \
tool_metalink.c \ tool_metalink.c \
tool_mfiles.c \
tool_msgs.c \ tool_msgs.c \
tool_operate.c \ tool_operate.c \
tool_operhlp.c \ tool_operhlp.c \
@ -86,7 +85,6 @@ CURL_HFILES = \
tool_libinfo.h \ tool_libinfo.h \
tool_main.h \ tool_main.h \
tool_metalink.h \ tool_metalink.h \
tool_mfiles.h \
tool_msgs.h \ tool_msgs.h \
tool_operate.h \ tool_operate.h \
tool_operhlp.h \ tool_operhlp.h \

View File

@ -140,11 +140,11 @@ static void free_config_fields(struct OperationConfig *config)
curl_slist_free_all(config->headers); curl_slist_free_all(config->headers);
curl_slist_free_all(config->proxyheaders); curl_slist_free_all(config->proxyheaders);
if(config->httppost) { if(config->mimepost) {
curl_formfree(config->httppost); curl_mime_free(config->mimepost);
config->httppost = NULL; config->mimepost = NULL;
} }
config->last_post = NULL; config->mimecurrent = NULL;
curl_slist_free_all(config->telnet_options); curl_slist_free_all(config->telnet_options);
curl_slist_free_all(config->resolve); curl_slist_free_all(config->resolve);

View File

@ -170,8 +170,8 @@ struct OperationConfig {
time_t condtime; time_t condtime;
struct curl_slist *headers; struct curl_slist *headers;
struct curl_slist *proxyheaders; struct curl_slist *proxyheaders;
struct curl_httppost *httppost; curl_mime *mimepost;
struct curl_httppost *last_post; curl_mime *mimecurrent;
struct curl_slist *telnet_options; struct curl_slist *telnet_options;
struct curl_slist *resolve; struct curl_slist *resolve;
struct curl_slist *connect_to; struct curl_slist *connect_to;

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * 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_code = NULL; /* Setopt calls */
struct slist_wc *easysrc_toohard = NULL; /* Unconvertible setopt */ struct slist_wc *easysrc_toohard = NULL; /* Unconvertible setopt */
struct slist_wc *easysrc_clean = NULL; /* Clean up allocated data */ 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; int easysrc_slist_count = 0;
static const char *const srchead[]={ static const char *const srchead[]={

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * 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_toohard; /* Unconvertible setopt */
extern struct slist_wc *easysrc_clean; /* Clean up (reverse order) */ 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 int easysrc_slist_count; /* Number of curl_slist variables */
extern CURLcode easysrc_init(void); extern CURLcode easysrc_init(void);

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -21,6 +21,7 @@
***************************************************************************/ ***************************************************************************/
#include "tool_setup.h" #include "tool_setup.h"
#include "mime.h"
#include "strcase.h" #include "strcase.h"
#define ENABLE_CURLX_PRINTF #define ENABLE_CURLX_PRINTF
@ -29,7 +30,6 @@
#include "tool_cfgable.h" #include "tool_cfgable.h"
#include "tool_convert.h" #include "tool_convert.h"
#include "tool_mfiles.h"
#include "tool_msgs.h" #include "tool_msgs.h"
#include "tool_formparse.h" #include "tool_formparse.h"
@ -94,6 +94,259 @@ static char *get_param_word(char **str, char **end_pos)
return word_begin; 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() * formparse()
@ -143,219 +396,302 @@ static char *get_param_word(char **str, char **end_pos)
int formparse(struct OperationConfig *config, int formparse(struct OperationConfig *config,
const char *input, const char *input,
struct curl_httppost **httppost, curl_mime **mimepost,
struct curl_httppost **last_post, curl_mime **mimecurrent,
bool literal_value) 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 */ build a linked list with the info */
char name[256]; char *name = NULL;
char *contents = NULL; char *contents = NULL;
char type_major[128] = "";
char type_minor[128] = "";
char *contp; char *contp;
char *data;
char *type = NULL; char *type = NULL;
char *sep; char *filename = NULL;
struct curl_slist *headers = NULL;
curl_mimepart *part = NULL;
CURLcode res;
int sep = '\0';
if((1 == sscanf(input, "%255[^=]=", name)) && /* Allocate the main mime structure if needed. */
((contp = strchr(input, '=')) != NULL)) { if(!*mimepost) {
/* the input was using the correct format */ *mimepost = curl_mime_init(config->easy);
if(!*mimepost) {
/* Allocate the contents */ warnf(config->global, "curl_mime_init failed!\n");
contents = strdup(contp+1);
if(!contents) {
fprintf(config->global->errors, "out of memory\n");
return 1; 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;
}
/* 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) {
/* we use the @-letter to indicate file name(s) */ /* we use the @-letter to indicate file name(s) */
struct multi_files *multi_start = NULL; curl_mime *subparts = NULL;
struct multi_files *multi_current = NULL;
char *ptr = contp;
char *end = ptr + strlen(ptr);
do { do {
/* since this was a file, it may have a content-type specifier /* since this was a file, it may have a content-type specifier
at the end too, or a filename. Or both. */ at the end too, or a filename. Or both. */
char *filename = NULL; ++contp;
char *word_end; sep = get_param_part(config,
bool semicolon; &contp, &data, &type, &filename, &headers);
if(sep < 0) {
type = NULL; if(subparts != *mimecurrent)
curl_mime_free(subparts);
++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;
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';
}
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);
}
}
}
/* now ptr point to comma or string end */
/* if type == NULL curl_formadd takes care of the problem */
if(*contp && !AddMultiFiles(contp, type, filename, &multi_start,
&multi_current)) {
warnf(config->global, "Error building form post!\n");
Curl_safefree(contents); Curl_safefree(contents);
FreeMultiInfo(&multi_start, &multi_current); return 10;
return 3;
} }
/* *ptr could be '\0', so we just check with the string end */ /* now contp point to comma or string end.
} while(ptr < end); /* loop if there's another file name */ If more files to come, make sure we have multiparts. */
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;
}
/* 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);
return 16;
}
/* *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 */ /* now we add the multiple files section */
if(multi_start) { if(subparts != *mimecurrent) {
struct curl_forms *forms = NULL; part = curl_mime_addpart(*mimecurrent);
struct multi_files *start = multi_start; if(!part) {
unsigned int i, count = 0; warnf(config->global, "curl_mime_addpart failed!\n");
while(start) { curl_mime_free(subparts);
start = start->next;
++count;
}
forms = malloc((count+1)*sizeof(struct curl_forms));
if(!forms) {
fprintf(config->global->errors, "Error building form post!\n");
Curl_safefree(contents); Curl_safefree(contents);
FreeMultiInfo(&multi_start, &multi_current); return 17;
return 4;
} }
for(i = 0, start = multi_start; i < count; ++i, start = start->next) { if(curl_mime_subparts(part, subparts)) {
forms[i].option = start->form.option; warnf(config->global, "curl_mime_subparts failed!\n");
forms[i].value = start->form.value; curl_mime_free(subparts);
}
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);
Curl_safefree(contents); Curl_safefree(contents);
return 5; return 18;
} }
Curl_safefree(forms);
} }
} }
else { else {
struct curl_forms info[4]; /* Allocate a mime part. */
int i = 0; part = curl_mime_addpart(*mimecurrent);
char *ct = literal_value ? NULL : strstr(contp, ";type="); if(!part) {
warnf(config->global, "curl_mime_addpart failed!\n");
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 */
}
if(contp[0]=='<' && !literal_value) {
info[i].option = CURLFORM_FILECONTENT;
info[i].value = contp+1;
i++;
info[i].option = CURLFORM_END;
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);
Curl_safefree(contents); Curl_safefree(contents);
return 6; return 19;
}
if(*contp == '<' && !literal_value) {
++contp;
sep = get_param_part(config,
&contp, &data, &type, &filename, &headers);
if(sep < 0) {
Curl_safefree(contents);
return 20;
}
/* 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 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 { 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 #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"); warnf(config->global, "curl_formadd failed!\n");
Curl_safefree(contents); Curl_safefree(contents);
return 7; return 25;
} }
#endif #endif
info[i].option = CURLFORM_COPYCONTENTS;
info[i].value = contp; if(curl_mime_data(part, data, -1)) {
i++; warnf(config->global, "curl_mime_data failed!\n");
info[i].option = CURLFORM_END;
if(curl_formadd(httppost, last_post,
CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
warnf(config->global, "curl_formadd failed!\n");
Curl_safefree(contents); 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 { else {
warnf(config->global, "Illegally formatted input field!\n"); warnf(config->global, "Illegally formatted input field!\n");
return 1; Curl_safefree(contents);
return 30;
} }
Curl_safefree(contents); Curl_safefree(contents);
return 0; return 0;

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -25,8 +25,8 @@
int formparse(struct OperationConfig *config, int formparse(struct OperationConfig *config,
const char *input, const char *input,
struct curl_httppost **httppost, curl_mime **mimepost,
struct curl_httppost **last_post, curl_mime **mimecurrent,
bool literal_value); bool literal_value);
#endif /* HEADER_CURL_TOOL_FORMPARSE_H */ #endif /* HEADER_CURL_TOOL_FORMPARSE_H */

View File

@ -1606,11 +1606,11 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
to sort this out slowly and carefully */ to sort this out slowly and carefully */
if(formparse(config, if(formparse(config,
nextarg, nextarg,
&config->httppost, &config->mimepost,
&config->last_post, &config->mimecurrent,
(subletter=='s')?TRUE:FALSE)) /* 's' means literal string */ (subletter=='s')?TRUE:FALSE)) /* 's' means literal string */
return PARAM_BAD_USE; return PARAM_BAD_USE;
if(SetHTTPrequest(config, HTTPREQ_FORMPOST, &config->httpreq)) if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq))
return PARAM_BAD_USE; return PARAM_BAD_USE;
break; break;

View File

@ -131,9 +131,9 @@ static const struct helptxt helptext[] = {
{" --false-start", {" --false-start",
"Enable TLS False Start"}, "Enable TLS False Start"},
{"-F, --form <name=content>", {"-F, --form <name=content>",
"Specify HTTP multipart POST data"}, "Specify multipart MIME data"},
{" --form-string <name=string>", {" --form-string <name=string>",
"Specify HTTP multipart POST data"}, "Specify multipart MIME data"},
{" --ftp-account <data>", {" --ftp-account <data>",
"Account data string"}, "Account data string"},
{" --ftp-alternative-to-user <command>", {" --ftp-alternative-to-user <command>",

View File

@ -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;
}

View File

@ -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 */

View File

@ -1002,8 +1002,8 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE,
config->postfieldsize); config->postfieldsize);
break; break;
case HTTPREQ_FORMPOST: case HTTPREQ_MIMEPOST:
my_setopt_httppost(curl, CURLOPT_HTTPPOST, config->httppost); my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost);
break; break;
default: default:
break; break;

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -135,7 +135,7 @@ typedef enum {
HTTPREQ_UNSPEC, /* first in list */ HTTPREQ_UNSPEC, /* first in list */
HTTPREQ_GET, HTTPREQ_GET,
HTTPREQ_HEAD, HTTPREQ_HEAD,
HTTPREQ_FORMPOST, HTTPREQ_MIMEPOST,
HTTPREQ_SIMPLEPOST HTTPREQ_SIMPLEPOST
} HttpReq; } HttpReq;

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -30,7 +30,9 @@
#include "tool_cfgable.h" #include "tool_cfgable.h"
#include "tool_easysrc.h" #include "tool_easysrc.h"
#include "tool_setopt.h" #include "tool_setopt.h"
#include "tool_convert.h"
#include "mime.h"
#include "memdebug.h" /* keep this as LAST include */ #include "memdebug.h" /* keep this as LAST include */
/* Lookup tables for converting setopt values back to symbols */ /* 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. /* Escape string to C string syntax. Return NULL if out of memory.
* Is this correct for those wacky EBCDIC guys? */ * 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; const char *s;
unsigned char c; unsigned char c;
char *escaped, *e; char *escaped, *e;
/* Allocate space based on worst-case */ /* Allocate space based on worst-case */
len = strlen(str); if(len < 0)
len = strlen(str);
escaped = malloc(4 * len + 1); escaped = malloc(4 * len + 1);
if(!escaped) if(!escaped)
return NULL; return NULL;
@ -371,75 +373,26 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config,
return ret; return ret;
} }
/* setopt wrapper for CURLOPT_HTTPPOST */ /* Generate code for a struct curl_slist. */
CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config, static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno)
const char *name, CURLoption tag,
struct curl_httppost *post)
{ {
CURLcode ret = CURLE_OK; CURLcode ret = CURLE_OK;
char *escaped = NULL; char *escaped = NULL;
bool skip = FALSE;
ret = curl_easy_setopt(curl, tag, post); /* May need several slist variables, so invent name */
if(!post) *slistno = ++easysrc_slist_count;
skip = TRUE;
if(config->libcurl && !skip && !ret) { DECL1("struct curl_slist *slist%d;", *slistno);
struct curl_httppost *pp, *p; DATA1("slist%d = NULL;", *slistno);
int i; CLEAN1("curl_slist_free_all(slist%d);", *slistno);
/* May use several httppost lists, if multiple POST actions */ CLEAN1("slist%d = NULL;", *slistno);
i = ++ easysrc_form_count; for(; slist; slist = slist->next) {
DECL1("struct curl_httppost *post%d;", i); Curl_safefree(escaped);
DATA1("post%d = NULL;", i); escaped = c_escape(slist->data, -1);
CLEAN1("curl_formfree(post%d);", i); if(!escaped)
CLEAN1("post%d = NULL;", i); return CURLE_OUT_OF_MEMORY;
if(i == 1) DATA3("slist%d = curl_slist_append(slist%d, \"%s\");",
DECL0("struct curl_httppost *postend;"); *slistno, *slistno, escaped);
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 */
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);
}
else
DATA1(" CURLFORM_COPYCONTENTS, \"%s\",", escaped);
if(pp->showfilename) {
Curl_safefree(escaped);
escaped = c_escape(pp->showfilename);
if(!escaped) {
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;
goto nomem;
}
DATA1(" CURLFORM_CONTENTTYPE, \"%s\",", escaped);
}
}
DATA0(" CURLFORM_END);");
}
CODE2("curl_easy_setopt(hnd, %s, post%d);", name, i);
} }
nomem: nomem:
@ -447,42 +400,188 @@ CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config,
return ret; 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(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;
}
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;
}
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
/* 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. */
}
}
}
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 */ /* setopt wrapper for curl_slist options */
CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config,
const char *name, CURLoption tag, const char *name, CURLoption tag,
struct curl_slist *list) struct curl_slist *list)
{ {
CURLcode ret = CURLE_OK; CURLcode ret = CURLE_OK;
char *escaped = NULL;
bool skip = FALSE;
ret = curl_easy_setopt(curl, tag, list); ret = curl_easy_setopt(curl, tag, list);
if(!list)
skip = TRUE;
if(config->libcurl && !skip && !ret) { if(config->libcurl && list && !ret) {
struct curl_slist *s;
int i; int i;
/* May need several slist variables, so invent name */
i = ++ easysrc_slist_count; ret = libcurl_generate_slist(list, &i);
DECL1("struct curl_slist *slist%d;", i); if(!ret)
DATA1("slist%d = NULL;", i); CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, 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);
} }
nomem: nomem:
Curl_safefree(escaped);
return ret; return ret;
} }
@ -569,7 +668,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config,
REM2("%s set to a %s", name, value); REM2("%s set to a %s", name, value);
else { else {
if(escape) { if(escape) {
escaped = c_escape(value); escaped = c_escape(value, -1);
if(!escaped) { if(!escaped) {
ret = CURLE_OUT_OF_MEMORY; ret = CURLE_OUT_OF_MEMORY;
goto nomem; goto nomem;

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * 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, CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config,
const char *name, CURLoption tag, const char *name, CURLoption tag,
const NameValueUnsigned *nv, long lval); 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, const char *name, CURLoption tag,
struct curl_httppost *httppost); curl_mime *mimepost);
CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config,
const char *name, CURLoption tag, const char *name, CURLoption tag,
struct curl_slist *list); 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) \ #define my_setopt_bitmask(x,y,z) \
SETOPT_CHECK(tool_setopt_bitmask(x, global, #y, y, setopt_nv_ ## y, z)) SETOPT_CHECK(tool_setopt_bitmask(x, global, #y, y, setopt_nv_ ## y, z))
#define my_setopt_httppost(x,y,z) \ #define my_setopt_mimepost(x,y,z) \
SETOPT_CHECK(tool_setopt_httppost(x, global, #y, y, z)) SETOPT_CHECK(tool_setopt_mimepost(x, global, #y, y, z))
#define my_setopt_slist(x,y,z) \ #define my_setopt_slist(x,y,z) \
SETOPT_CHECK(tool_setopt_slist(x, global, #y, 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) \ #define my_setopt_bitmask(x,y,z) \
SETOPT_CHECK(curl_easy_setopt(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)) SETOPT_CHECK(curl_easy_setopt(x, y, z))
#define my_setopt_slist(x,y,z) \ #define my_setopt_slist(x,y,z) \