mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
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:
parent
ce0881edee
commit
fec7a858b8
@ -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
|
||||
---
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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[]={
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
char *filename = NULL;
|
||||
struct curl_slist *headers = NULL;
|
||||
curl_mimepart *part = NULL;
|
||||
CURLcode res;
|
||||
int sep = '\0';
|
||||
|
||||
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");
|
||||
/* 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';
|
||||
|
||||
struct multi_files *multi_start = NULL;
|
||||
struct multi_files *multi_current = NULL;
|
||||
if(*contp == '(' && !literal_value) {
|
||||
curl_mime *subparts;
|
||||
|
||||
char *ptr = contp;
|
||||
char *end = ptr + strlen(ptr);
|
||||
|
||||
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");
|
||||
/* Starting a multipart. */
|
||||
sep = get_param_part(config, &contp, &data, &type, NULL, &headers);
|
||||
if(sep < 0) {
|
||||
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);
|
||||
FreeMultiInfo(&multi_start, &multi_current);
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* *ptr could be '\0', so we just check with the string end */
|
||||
} while(ptr < end); /* 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");
|
||||
subparts = curl_mime_init(config->easy);
|
||||
if(!subparts) {
|
||||
warnf(config->global, "curl_mime_init failed!\n");
|
||||
curl_slist_free_all(headers);
|
||||
Curl_safefree(contents);
|
||||
FreeMultiInfo(&multi_start, &multi_current);
|
||||
return 4;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
if(convert_to_network(contp, strlen(contp))) {
|
||||
warnf(config->global, "curl_formadd failed!\n");
|
||||
*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;
|
||||
}
|
||||
#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_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) */
|
||||
|
||||
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. */
|
||||
++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;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
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 */
|
||||
if(subparts != *mimecurrent) {
|
||||
part = curl_mime_addpart(*mimecurrent);
|
||||
if(!part) {
|
||||
warnf(config->global, "curl_mime_addpart failed!\n");
|
||||
curl_mime_free(subparts);
|
||||
Curl_safefree(contents);
|
||||
return 17;
|
||||
}
|
||||
if(curl_mime_subparts(part, subparts)) {
|
||||
warnf(config->global, "curl_mime_subparts failed!\n");
|
||||
curl_mime_free(subparts);
|
||||
Curl_safefree(contents);
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 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 == '<' && !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 {
|
||||
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(data, strlen(data))) {
|
||||
warnf(config->global, "curl_formadd failed!\n");
|
||||
Curl_safefree(contents);
|
||||
return 25;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(curl_mime_data(part, data, -1)) {
|
||||
warnf(config->global, "curl_mime_data failed!\n");
|
||||
Curl_safefree(contents);
|
||||
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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,13 +210,13 @@ 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 */
|
||||
if(len < 0)
|
||||
len = strlen(str);
|
||||
escaped = malloc(4 * len + 1);
|
||||
if(!escaped)
|
||||
@ -371,75 +373,26 @@ 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;
|
||||
|
||||
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 */
|
||||
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(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);
|
||||
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);
|
||||
}
|
||||
|
||||
nomem:
|
||||
@ -447,42 +400,188 @@ CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config,
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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) \
|
||||
|
Loading…
Reference in New Issue
Block a user