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