From fec7a858b88c86e97e5dc96414a01feb21a2b661 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Sat, 2 Sep 2017 18:17:33 +0100 Subject: 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. --- src/tool_setopt.c | 267 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 183 insertions(+), 84 deletions(-) (limited to 'src/tool_setopt.c') diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 694d3ffa5..6cb6bb3e3 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,7 +30,9 @@ #include "tool_cfgable.h" #include "tool_easysrc.h" #include "tool_setopt.h" +#include "tool_convert.h" +#include "mime.h" #include "memdebug.h" /* keep this as LAST include */ /* Lookup tables for converting setopt values back to symbols */ @@ -208,14 +210,14 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = { /* Escape string to C string syntax. Return NULL if out of memory. * Is this correct for those wacky EBCDIC guys? */ -static char *c_escape(const char *str) +static char *c_escape(const char *str, ssize_t len) { - size_t len = 0; const char *s; unsigned char c; char *escaped, *e; /* Allocate space based on worst-case */ - len = strlen(str); + if(len < 0) + len = strlen(str); escaped = malloc(4 * len + 1); if(!escaped) return NULL; @@ -371,118 +373,215 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, return ret; } -/* setopt wrapper for CURLOPT_HTTPPOST */ -CURLcode tool_setopt_httppost(CURL *curl, struct GlobalConfig *config, - const char *name, CURLoption tag, - struct curl_httppost *post) +/* Generate code for a struct curl_slist. */ +static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno) { CURLcode ret = CURLE_OK; char *escaped = NULL; - bool skip = FALSE; - ret = curl_easy_setopt(curl, tag, post); - if(!post) - skip = TRUE; + /* May need several slist variables, so invent name */ + *slistno = ++easysrc_slist_count; + + DECL1("struct curl_slist *slist%d;", *slistno); + DATA1("slist%d = NULL;", *slistno); + CLEAN1("curl_slist_free_all(slist%d);", *slistno); + CLEAN1("slist%d = NULL;", *slistno); + for(; slist; slist = slist->next) { + Curl_safefree(escaped); + escaped = c_escape(slist->data, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + DATA3("slist%d = curl_slist_append(slist%d, \"%s\");", + *slistno, *slistno, escaped); + } - if(config->libcurl && !skip && !ret) { - struct curl_httppost *pp, *p; - int i; - /* May use several httppost lists, if multiple POST actions */ - i = ++ easysrc_form_count; - DECL1("struct curl_httppost *post%d;", i); - DATA1("post%d = NULL;", i); - CLEAN1("curl_formfree(post%d);", i); - CLEAN1("post%d = NULL;", i); - if(i == 1) - DECL0("struct curl_httppost *postend;"); - DATA0("postend = NULL;"); - for(p=post; p; p=p->next) { - DATA1("curl_formadd(&post%d, &postend,", i); - DATA1(" CURLFORM_COPYNAME, \"%s\",", p->name); - for(pp=p; pp; pp=pp->more) { - /* May be several files uploaded for one name; - * these are linked through the 'more' pointer */ + nomem: + Curl_safefree(escaped); + return ret; +} + +/* Generate source code for a mime structure. */ +static CURLcode libcurl_generate_mime(curl_mime *mime, int *mimeno) +{ + CURLcode ret = CURLE_OK; + int i; + curl_off_t size; + curl_mimepart *part; + char *filename; + char *escaped = NULL; + char *cp; + char *data; + + /* May need several mime variables, so invent name */ + *mimeno = ++easysrc_mime_count; + + DECL1("curl_mime *mime%d;", *mimeno); + DATA1("mime%d = NULL;", *mimeno); + CODE1("mime%d = curl_mime_init(hnd);", *mimeno); + CLEAN1("curl_mime_free(mime%d);", *mimeno); + CLEAN1("mime%d = NULL;", *mimeno); + if(mime->firstpart) { + DECL1("curl_mimepart *part%d;", *mimeno); + for(part = mime->firstpart; part; part = part->nextpart) { + CODE2("part%d = curl_mime_addpart(mime%d);", *mimeno, *mimeno); + filename = part->filename; + switch(part->kind) { + case MIMEKIND_NAMEDFILE: Curl_safefree(escaped); - escaped = c_escape(pp->contents); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } - if(pp->flags & CURL_HTTPPOST_FILENAME) { - /* file upload as for -F @filename */ - DATA1(" CURLFORM_FILE, \"%s\",", escaped); - } - else if(pp->flags & CURL_HTTPPOST_READFILE) { - /* content from file as for -F data, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE2("curl_mime_filedata(part%d, \"%s\");", *mimeno, escaped); + if(!filename) + CODE1("curl_mime_filename(part%d, NULL);", *mimeno); + else { + /* Fast check to see if remote file name is base name. */ + filename = part->data; + for(cp = filename; *cp; cp++) + if(*cp == '/' || *cp == '\\') + filename = cp + 1; + if(!part->filename || !strcmp(filename, part->filename)) + filename = NULL; + else + filename = part->filename; } - else - DATA1(" CURLFORM_COPYCONTENTS, \"%s\",", escaped); - if(pp->showfilename) { - Curl_safefree(escaped); - escaped = c_escape(pp->showfilename); - if(!escaped) { + break; + case MIMEKIND_FILE: + /* Can only be stdin in the current context. */ + CODE1("curl_mime_file(part%d, \"-\");", *mimeno); + break; + case MIMEKIND_DATA: +#ifdef CURL_DOES_CONVERSIONS + /* Data is stored in ASCII and we want in in the host character + code. Convert it back for output. */ + data = malloc(part->datasize + 1); + if(!data) { ret = CURLE_OUT_OF_MEMORY; goto nomem; } - DATA1(" CURLFORM_FILENAME, \"%s\",", escaped); - } - if(pp->contenttype) { - Curl_safefree(escaped); - escaped = c_escape(pp->contenttype); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; + memcpy(data, part->data, part->datasize + 1); + ret = convert_from_network(data, strlen(data)); + if(ret) { + Curl_safefree(data); goto nomem; } - DATA1(" CURLFORM_CONTENTTYPE, \"%s\",", escaped); - } +#else + data = part->data; +#endif + + /* Are there any nul byte in data? */ + for(cp = data; *cp; cp++) + ; + size = (cp == data + part->datasize)? (curl_off_t) -1: part->datasize; + Curl_safefree(escaped); + escaped = c_escape(data, part->datasize); + if(data != part->data) + Curl_safefree(data); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE3("curl_mime_data(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");", + *mimeno, escaped, size); + break; + case MIMEKIND_MULTIPART: + ret = libcurl_generate_mime(part->arg, &i); + if(ret) + goto nomem; + CODE2("curl_mime_subparts(part%d, mime%d);", *mimeno, i); + CODE1("mime%d = NULL;", i); /* Avoid freeing in CLEAN sequence. */ + break; + default: + /* Other cases not possible in this context. */ + break; + } + + if(filename) { + Curl_safefree(escaped); + escaped = c_escape(filename, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE2("curl_mime_filename(part%d, \"%s\");", *mimeno, escaped); + } + + if(part->name) { + Curl_safefree(escaped); + escaped = c_escape(part->name, part->namesize); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + for(cp = part->name; *cp; cp++) + ; + size = (cp == part->name + part->namesize)? + (curl_off_t) -1: (curl_off_t) part->namesize; + CODE3("curl_mime_name(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");", + *mimeno, escaped, size); + } + + if(part->mimetype) { + Curl_safefree(escaped); + escaped = c_escape(part->mimetype, -1); + if(!escaped) + return CURLE_OUT_OF_MEMORY; + CODE2("curl_mime_type(part%d, \"%s\");", *mimeno, escaped); + } + + if(part->userheaders) { + int ownership = part->flags & MIME_USERHEADERS_OWNER? 1: 0; + + ret = libcurl_generate_slist(part->userheaders, &i); + if(ret) + goto nomem; + CODE3("curl_mime_headers(part%d, slist%d, %d);", + *mimeno, i, ownership); + if(ownership) + CODE1("slist%d = NULL;", i); /* Prevent freeing in CLEAN sequence. */ } - DATA0(" CURLFORM_END);"); } - CODE2("curl_easy_setopt(hnd, %s, post%d);", name, i); } - nomem: +nomem: Curl_safefree(escaped); return ret; } +/* setopt wrapper for CURLOPT_MIMEPOST */ +CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, + const char *name, CURLoption tag, + curl_mime *mimepost) +{ + CURLcode ret = CURLE_OK; + + ret = curl_easy_setopt(curl, tag, mimepost); + + if(config->libcurl && mimepost && !ret) { + int i; + + ret = libcurl_generate_mime(mimepost, &i); + + if(!ret) + CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, i); + } + +nomem: + return ret; +} + /* setopt wrapper for curl_slist options */ CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, const char *name, CURLoption tag, struct curl_slist *list) { CURLcode ret = CURLE_OK; - char *escaped = NULL; - bool skip = FALSE; ret = curl_easy_setopt(curl, tag, list); - if(!list) - skip = TRUE; - if(config->libcurl && !skip && !ret) { - struct curl_slist *s; + if(config->libcurl && list && !ret) { int i; - /* May need several slist variables, so invent name */ - i = ++ easysrc_slist_count; - DECL1("struct curl_slist *slist%d;", i); - DATA1("slist%d = NULL;", i); - CLEAN1("curl_slist_free_all(slist%d);", i); - CLEAN1("slist%d = NULL;", i); - for(s=list; s; s=s->next) { - Curl_safefree(escaped); - escaped = c_escape(s->data); - if(!escaped) { - ret = CURLE_OUT_OF_MEMORY; - goto nomem; - } - DATA3("slist%d = curl_slist_append(slist%d, \"%s\");", i, i, escaped); - } - CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); + + ret = libcurl_generate_slist(list, &i); + if(!ret) + CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); } nomem: - Curl_safefree(escaped); return ret; } @@ -569,7 +668,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config, REM2("%s set to a %s", name, value); else { if(escape) { - escaped = c_escape(value); + escaped = c_escape(value, -1); if(!escaped) { ret = CURLE_OUT_OF_MEMORY; goto nomem; -- cgit v1.2.3