mirror of
https://github.com/curl/curl.git
synced 2026-08-25 02:33:31 +03: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
17 changed files with 785 additions and 473 deletions
|
|
@ -5,7 +5,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
|
|
@ -30,7 +30,9 @@
|
|||
#include "tool_cfgable.h"
|
||||
#include "tool_easysrc.h"
|
||||
#include "tool_setopt.h"
|
||||
#include "tool_convert.h"
|
||||
|
||||
#include "mime.h"
|
||||
#include "memdebug.h" /* keep this as LAST include */
|
||||
|
||||
/* Lookup tables for converting setopt values back to symbols */
|
||||
|
|
@ -208,14 +210,14 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = {
|
|||
|
||||
/* Escape string to C string syntax. Return NULL if out of memory.
|
||||
* Is this correct for those wacky EBCDIC guys? */
|
||||
static char *c_escape(const char *str)
|
||||
static char *c_escape(const char *str, ssize_t len)
|
||||
{
|
||||
size_t len = 0;
|
||||
const char *s;
|
||||
unsigned char c;
|
||||
char *escaped, *e;
|
||||
/* Allocate space based on worst-case */
|
||||
len = strlen(str);
|
||||
if(len < 0)
|
||||
len = strlen(str);
|
||||
escaped = malloc(4 * len + 1);
|
||||
if(!escaped)
|
||||
return NULL;
|
||||
|
|
@ -371,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 */
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue