2011-10-03 16:59:38 -04:00
|
|
|
/***************************************************************************
|
|
|
|
* _ _ ____ _
|
|
|
|
* Project ___| | | | _ \| |
|
|
|
|
* / __| | | | |_) | |
|
|
|
|
* | (__| |_| | _ <| |___
|
|
|
|
* \___|\___/|_| \_\_____|
|
|
|
|
*
|
2017-09-02 13:17:33 -04:00
|
|
|
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
|
2011-10-03 16:59:38 -04:00
|
|
|
*
|
|
|
|
* This software is licensed as described in the file COPYING, which
|
|
|
|
* you should have received as part of this distribution. The terms
|
2016-02-02 18:19:02 -05:00
|
|
|
* are also available at https://curl.haxx.se/docs/copyright.html.
|
2011-10-03 16:59:38 -04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
***************************************************************************/
|
2012-04-06 17:35:15 -04:00
|
|
|
#include "tool_setup.h"
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
#include "mime.h"
|
2016-09-30 12:54:02 -04:00
|
|
|
#include "strcase.h"
|
2011-10-03 16:59:38 -04:00
|
|
|
|
|
|
|
#define ENABLE_CURLX_PRINTF
|
|
|
|
/* use our own printf() functions */
|
|
|
|
#include "curlx.h"
|
|
|
|
|
|
|
|
#include "tool_cfgable.h"
|
2017-05-01 14:10:43 -04:00
|
|
|
#include "tool_convert.h"
|
2011-10-03 16:59:38 -04:00
|
|
|
#include "tool_msgs.h"
|
2017-10-12 09:25:59 -04:00
|
|
|
#include "tool_binmode.h"
|
2017-10-12 11:42:02 -04:00
|
|
|
#include "tool_getparam.h"
|
|
|
|
#include "tool_paramhlp.h"
|
2011-10-03 16:59:38 -04:00
|
|
|
#include "tool_formparse.h"
|
|
|
|
|
2013-01-03 20:50:28 -05:00
|
|
|
#include "memdebug.h" /* keep this as LAST include */
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-10-12 09:25:59 -04:00
|
|
|
/* 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;
|
|
|
|
|
2013-01-21 17:20:09 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* helper function to get a word from form param
|
|
|
|
* after call get_parm_word, str either point to string end
|
|
|
|
* or point to any of end chars.
|
|
|
|
*/
|
2017-10-29 08:57:16 -04:00
|
|
|
static char *get_param_word(char **str, char **end_pos, char endchar)
|
2013-01-21 17:20:09 -05:00
|
|
|
{
|
|
|
|
char *ptr = *str;
|
|
|
|
char *word_begin = NULL;
|
|
|
|
char *ptr2;
|
|
|
|
char *escape = NULL;
|
|
|
|
|
|
|
|
/* the first non-space char is here */
|
|
|
|
word_begin = ptr;
|
|
|
|
if(*ptr == '"') {
|
|
|
|
++ptr;
|
|
|
|
while(*ptr) {
|
|
|
|
if(*ptr == '\\') {
|
|
|
|
if(ptr[1] == '\\' || ptr[1] == '"') {
|
|
|
|
/* remember the first escape position */
|
|
|
|
if(!escape)
|
|
|
|
escape = ptr;
|
|
|
|
/* skip escape of back-slash or double-quote */
|
|
|
|
ptr += 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(*ptr == '"') {
|
|
|
|
*end_pos = ptr;
|
|
|
|
if(escape) {
|
|
|
|
/* has escape, we restore the unescaped string here */
|
|
|
|
ptr = ptr2 = escape;
|
|
|
|
do {
|
|
|
|
if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
|
|
|
|
++ptr;
|
|
|
|
*ptr2++ = *ptr++;
|
|
|
|
}
|
|
|
|
while(ptr < *end_pos);
|
|
|
|
*end_pos = ptr2;
|
|
|
|
}
|
2017-10-29 08:57:16 -04:00
|
|
|
while(*ptr && *ptr != ';' && *ptr != endchar)
|
2013-01-21 17:20:09 -05:00
|
|
|
++ptr;
|
|
|
|
*str = ptr;
|
2017-09-09 17:55:08 -04:00
|
|
|
return word_begin + 1;
|
2013-01-21 17:20:09 -05:00
|
|
|
}
|
|
|
|
++ptr;
|
|
|
|
}
|
|
|
|
/* end quote is missing, treat it as non-quoted. */
|
|
|
|
ptr = word_begin;
|
|
|
|
}
|
|
|
|
|
2017-10-29 08:57:16 -04:00
|
|
|
while(*ptr && *ptr != ';' && *ptr != endchar)
|
2013-01-21 17:20:09 -05:00
|
|
|
++ptr;
|
|
|
|
*str = *end_pos = ptr;
|
|
|
|
return word_begin;
|
|
|
|
}
|
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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 */
|
|
|
|
}
|
|
|
|
|
2017-10-29 08:57:16 -04:00
|
|
|
static int get_param_part(struct OperationConfig *config, char endchar,
|
|
|
|
char **str, char **pdata, char **ptype,
|
|
|
|
char **pfilename, char **pencoder,
|
|
|
|
struct curl_slist **pheaders)
|
2017-09-02 13:17:33 -04:00
|
|
|
{
|
|
|
|
char *p = *str;
|
|
|
|
char *type = NULL;
|
|
|
|
char *filename = NULL;
|
2017-09-05 12:11:59 -04:00
|
|
|
char *encoder = NULL;
|
2017-09-02 13:17:33 -04:00
|
|
|
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;
|
2017-09-05 12:11:59 -04:00
|
|
|
if(pencoder)
|
|
|
|
*pencoder = NULL;
|
2017-09-02 13:17:33 -04:00
|
|
|
while(ISSPACE(*p))
|
|
|
|
p++;
|
|
|
|
tp = p;
|
2017-10-29 08:57:16 -04:00
|
|
|
*pdata = get_param_word(&p, &endpos, endchar);
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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 */
|
2017-10-29 09:31:03 -04:00
|
|
|
p = type + strlen(type_major) + strlen(type_minor) + 1;
|
|
|
|
for(endct = p; *p && *p != ';' && *p != endchar; p++)
|
|
|
|
if(!ISSPACE(*p))
|
|
|
|
endct = p + 1;
|
2017-09-02 13:17:33 -04:00
|
|
|
sep = *p;
|
|
|
|
}
|
|
|
|
else if(checkprefix("filename=", p)) {
|
|
|
|
if(endct) {
|
|
|
|
*endct = '\0';
|
|
|
|
endct = NULL;
|
|
|
|
}
|
|
|
|
for(p += 9; ISSPACE(*p); p++)
|
|
|
|
;
|
|
|
|
tp = p;
|
2017-10-29 08:57:16 -04:00
|
|
|
filename = get_param_word(&p, &endpos, endchar);
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
2017-10-29 08:57:16 -04:00
|
|
|
hdrfile = get_param_word(&p, &endpos, endchar);
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
2017-10-29 08:57:16 -04:00
|
|
|
hdr = get_param_word(&p, &endpos, endchar);
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-05 12:11:59 -04:00
|
|
|
else if(checkprefix("encoder=", p)) {
|
|
|
|
if(endct) {
|
|
|
|
*endct = '\0';
|
|
|
|
endct = NULL;
|
|
|
|
}
|
|
|
|
for(p += 8; ISSPACE(*p); p++)
|
|
|
|
;
|
|
|
|
tp = p;
|
2017-10-29 08:57:16 -04:00
|
|
|
encoder = get_param_word(&p, &endpos, endchar);
|
2017-09-05 12:11:59 -04:00
|
|
|
/* If not quoted, strip trailing spaces. */
|
|
|
|
if(encoder == tp)
|
|
|
|
while(endpos > encoder && ISSPACE(endpos[-1]))
|
|
|
|
endpos--;
|
|
|
|
sep = *p;
|
|
|
|
*endpos = '\0';
|
|
|
|
}
|
2017-10-29 09:31:03 -04:00
|
|
|
else if(endct) {
|
|
|
|
/* This is part of content type. */
|
|
|
|
for(endct = p; *p && *p != ';' && *p != endchar; p++)
|
|
|
|
if(!ISSPACE(*p))
|
|
|
|
endct = p + 1;
|
|
|
|
sep = *p;
|
|
|
|
}
|
2017-09-02 13:17:33 -04:00
|
|
|
else {
|
|
|
|
/* unknown prefix, skip to next block */
|
2017-10-29 08:57:16 -04:00
|
|
|
char *unknown = get_param_word(&p, &endpos, endchar);
|
2017-09-02 13:17:33 -04:00
|
|
|
|
|
|
|
sep = *p;
|
2017-10-29 09:31:03 -04:00
|
|
|
*endpos = '\0';
|
|
|
|
if(*unknown)
|
|
|
|
warnf(config->global, "skip unknown form field: %s\n", unknown);
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-29 09:31:03 -04:00
|
|
|
/* Terminate content type. */
|
|
|
|
if(endct)
|
2017-09-02 13:17:33 -04:00
|
|
|
*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);
|
|
|
|
|
2017-09-05 12:11:59 -04:00
|
|
|
if(pencoder)
|
|
|
|
*pencoder = encoder;
|
|
|
|
else if(encoder)
|
|
|
|
warnf(config->global,
|
|
|
|
"Field encoder not allowed here: %s\n", encoder);
|
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-10-12 09:25:59 -04:00
|
|
|
|
|
|
|
/* 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
|
2017-09-03 09:45:43 -04:00
|
|
|
* workaround Windows DLL file handle caveat).
|
2017-10-12 09:25:59 -04:00
|
|
|
* 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.
|
|
|
|
*/
|
2017-09-03 09:45:43 -04:00
|
|
|
static CURLcode file_or_stdin(curl_mimepart *part, const char *file)
|
|
|
|
{
|
2017-10-12 09:25:59 -04:00
|
|
|
standard_input *sip = NULL;
|
|
|
|
int fd = -1;
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
struct_stat sbuf;
|
|
|
|
|
2017-09-03 09:45:43 -04:00
|
|
|
if(strcmp(file, "-"))
|
|
|
|
return curl_mime_filedata(part, file);
|
|
|
|
|
2017-10-12 09:25:59 -04:00
|
|
|
sip = (standard_input *) malloc(sizeof *sip);
|
|
|
|
if(!sip)
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
memset((char *) sip, 0, sizeof *sip);
|
|
|
|
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;
|
|
|
|
}
|
2017-10-12 11:42:02 -04:00
|
|
|
else { /* Not suitable for direct use, buffer stdin data. */
|
|
|
|
size_t stdinsize = 0;
|
|
|
|
|
2017-10-12 09:25:59 -04:00
|
|
|
sip->origin = 0;
|
2017-10-12 11:42:02 -04:00
|
|
|
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;
|
2017-10-12 09:25:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set remote file name. */
|
2017-10-12 11:42:02 -04:00
|
|
|
if(!result)
|
|
|
|
result = curl_mime_filename(part, file);
|
2017-10-12 09:25:59 -04:00
|
|
|
|
|
|
|
/* 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;
|
2017-09-03 09:45:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-03 16:59:38 -04:00
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* formparse()
|
|
|
|
*
|
|
|
|
* Reads a 'name=value' parameter and builds the appropriate linked list.
|
|
|
|
*
|
2013-01-21 17:20:09 -05:00
|
|
|
* Specify files to upload with 'name=@filename', or 'name=@"filename"'
|
|
|
|
* in case the filename contain ',' or ';'. Supports specified
|
2011-10-03 16:59:38 -04:00
|
|
|
* given Content-Type of the files. Such as ';type=<content-type>'.
|
|
|
|
*
|
|
|
|
* If literal_value is set, any initial '@' or '<' in the value string
|
|
|
|
* loses its special meaning, as does any embedded ';type='.
|
|
|
|
*
|
|
|
|
* You may specify more than one file for a single name (field). Specify
|
|
|
|
* multiple files by writing it like:
|
|
|
|
*
|
|
|
|
* 'name=@filename,filename2,filename3'
|
|
|
|
*
|
2013-01-21 17:20:09 -05:00
|
|
|
* or use double-quotes quote the filename:
|
|
|
|
*
|
|
|
|
* 'name=@"filename","filename2","filename3"'
|
|
|
|
*
|
2011-10-03 16:59:38 -04:00
|
|
|
* If you want content-types specified for each too, write them like:
|
|
|
|
*
|
|
|
|
* 'name=@filename;type=image/gif,filename2,filename3'
|
|
|
|
*
|
|
|
|
* If you want custom headers added for a single part, write them in a separate
|
|
|
|
* file and do like this:
|
|
|
|
*
|
|
|
|
* 'name=foo;headers=@headerfile' or why not
|
|
|
|
* 'name=@filemame;headers=@headerfile'
|
|
|
|
*
|
|
|
|
* To upload a file, but to fake the file name that will be included in the
|
|
|
|
* formpost, do like this:
|
|
|
|
*
|
2013-01-21 17:20:09 -05:00
|
|
|
* 'name=@filename;filename=/dev/null' or quote the faked filename like:
|
|
|
|
* 'name=@filename;filename="play, play, and play.txt"'
|
|
|
|
*
|
|
|
|
* If filename/path contains ',' or ';', it must be quoted by double-quotes,
|
|
|
|
* else curl will fail to figure out the correct filename. if the filename
|
|
|
|
* tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
|
2011-10-03 16:59:38 -04:00
|
|
|
*
|
|
|
|
* This function uses curl_formadd to fulfill it's job. Is heavily based on
|
|
|
|
* the old curl_formparse code.
|
|
|
|
*
|
|
|
|
***************************************************************************/
|
|
|
|
|
2014-02-23 07:59:59 -05:00
|
|
|
int formparse(struct OperationConfig *config,
|
2011-10-03 16:59:38 -04:00
|
|
|
const char *input,
|
2017-09-02 13:17:33 -04:00
|
|
|
curl_mime **mimepost,
|
|
|
|
curl_mime **mimecurrent,
|
2011-10-03 16:59:38 -04:00
|
|
|
bool literal_value)
|
|
|
|
{
|
2017-09-02 13:17:33 -04:00
|
|
|
/* input MUST be a string in the format 'name=contents' and we'll
|
2011-10-03 16:59:38 -04:00
|
|
|
build a linked list with the info */
|
2017-09-02 13:17:33 -04:00
|
|
|
char *name = NULL;
|
2011-10-03 16:59:38 -04:00
|
|
|
char *contents = NULL;
|
|
|
|
char *contp;
|
2017-09-02 13:17:33 -04:00
|
|
|
char *data;
|
2017-05-09 13:20:28 -04:00
|
|
|
char *type = NULL;
|
2017-09-02 13:17:33 -04:00
|
|
|
char *filename = NULL;
|
2017-09-05 12:11:59 -04:00
|
|
|
char *encoder = NULL;
|
2017-09-02 13:17:33 -04:00
|
|
|
struct curl_slist *headers = NULL;
|
|
|
|
curl_mimepart *part = NULL;
|
|
|
|
CURLcode res;
|
|
|
|
int sep = '\0';
|
|
|
|
|
|
|
|
/* 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");
|
2011-10-03 16:59:38 -04:00
|
|
|
return 1;
|
|
|
|
}
|
2017-09-02 13:17:33 -04:00
|
|
|
*mimecurrent = *mimepost;
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Make a copy we can overwrite. */
|
|
|
|
contents = strdup(input);
|
|
|
|
if(!contents) {
|
|
|
|
fprintf(config->global->errors, "out of memory\n");
|
|
|
|
return 2;
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Scan for the end of the name. */
|
|
|
|
contp = strchr(contents, '=');
|
|
|
|
if(contp) {
|
|
|
|
if(contp > contents)
|
|
|
|
name = contents;
|
|
|
|
*contp++ = '\0';
|
|
|
|
|
|
|
|
if(*contp == '(' && !literal_value) {
|
|
|
|
curl_mime *subparts;
|
|
|
|
|
|
|
|
/* Starting a multipart. */
|
2017-10-29 08:57:16 -04:00
|
|
|
sep = get_param_part(config, '\0',
|
|
|
|
&contp, &data, &type, NULL, NULL, &headers);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(sep < 0) {
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
subparts = curl_mime_init(config->easy);
|
|
|
|
if(!subparts) {
|
|
|
|
warnf(config->global, "curl_mime_init failed!\n");
|
|
|
|
curl_slist_free_all(headers);
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
part = curl_mime_addpart(*mimecurrent);
|
|
|
|
if(!part) {
|
|
|
|
warnf(config->global, "curl_mime_addpart failed!\n");
|
|
|
|
curl_mime_free(subparts);
|
|
|
|
curl_slist_free_all(headers);
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
if(curl_mime_subparts(part, subparts)) {
|
|
|
|
warnf(config->global, "curl_mime_subparts failed!\n");
|
|
|
|
curl_mime_free(subparts);
|
|
|
|
curl_slist_free_all(headers);
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
*mimecurrent = subparts;
|
|
|
|
if(curl_mime_headers(part, headers, 1)) {
|
|
|
|
warnf(config->global, "curl_mime_headers failed!\n");
|
|
|
|
curl_slist_free_all(headers);
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 7;
|
|
|
|
}
|
|
|
|
if(curl_mime_type(part, type)) {
|
|
|
|
warnf(config->global, "curl_mime_type failed!\n");
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(!name && !strcmp(contp, ")") && !literal_value) {
|
|
|
|
/* Ending a mutipart. */
|
|
|
|
if(*mimecurrent == *mimepost) {
|
|
|
|
warnf(config->global, "no multipart to terminate!\n");
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 9;
|
|
|
|
}
|
|
|
|
*mimecurrent = (*mimecurrent)->parent->parent;
|
|
|
|
}
|
|
|
|
else if('@' == contp[0] && !literal_value) {
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* we use the @-letter to indicate file name(s) */
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
curl_mime *subparts = NULL;
|
2011-10-03 16:59:38 -04:00
|
|
|
|
|
|
|
do {
|
|
|
|
/* since this was a file, it may have a content-type specifier
|
|
|
|
at the end too, or a filename. Or both. */
|
2017-09-02 13:17:33 -04:00
|
|
|
++contp;
|
2017-10-29 08:57:16 -04:00
|
|
|
sep = get_param_part(config, ',', &contp,
|
2017-09-05 12:11:59 -04:00
|
|
|
&data, &type, &filename, &encoder, &headers);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(sep < 0) {
|
|
|
|
if(subparts != *mimecurrent)
|
|
|
|
curl_mime_free(subparts);
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 10;
|
|
|
|
}
|
2011-12-16 05:43:25 -05:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
2013-01-21 17:20:09 -05:00
|
|
|
else {
|
2017-09-02 13:17:33 -04:00
|
|
|
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;
|
2013-01-21 17:20:09 -05:00
|
|
|
}
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
2013-01-21 17:20:09 -05:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
2011-12-16 05:43:25 -05:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Setup file in part. */
|
2017-09-03 09:45:43 -04:00
|
|
|
res = file_or_stdin(part, data);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(res) {
|
2017-09-03 09:45:43 -04:00
|
|
|
warnf(config->global, "setting file %s failed!\n", data);
|
2017-09-02 13:17:33 -04:00
|
|
|
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;
|
|
|
|
}
|
2017-09-05 12:11:59 -04:00
|
|
|
if(curl_mime_type(part, type)) {
|
2017-09-02 13:17:33 -04:00
|
|
|
warnf(config->global, "curl_mime_type failed!\n");
|
|
|
|
if(subparts != *mimecurrent)
|
|
|
|
curl_mime_free(subparts);
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-02 13:17:33 -04:00
|
|
|
return 16;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
2017-09-05 12:11:59 -04:00
|
|
|
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;
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* *contp could be '\0', so we just check with the delimiter */
|
|
|
|
} while(sep); /* loop if there's another file name */
|
2011-10-03 16:59:38 -04:00
|
|
|
|
|
|
|
/* now we add the multiple files section */
|
2017-09-02 13:17:33 -04:00
|
|
|
if(subparts != *mimecurrent) {
|
|
|
|
part = curl_mime_addpart(*mimecurrent);
|
|
|
|
if(!part) {
|
|
|
|
warnf(config->global, "curl_mime_addpart failed!\n");
|
|
|
|
curl_mime_free(subparts);
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 18;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
2017-09-02 13:17:33 -04:00
|
|
|
if(curl_mime_subparts(part, subparts)) {
|
|
|
|
warnf(config->global, "curl_mime_subparts failed!\n");
|
|
|
|
curl_mime_free(subparts);
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 19;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Allocate a mime part. */
|
|
|
|
part = curl_mime_addpart(*mimecurrent);
|
|
|
|
if(!part) {
|
|
|
|
warnf(config->global, "curl_mime_addpart failed!\n");
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 20;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
if(*contp == '<' && !literal_value) {
|
|
|
|
++contp;
|
2017-10-29 08:57:16 -04:00
|
|
|
sep = get_param_part(config, '\0', &contp,
|
2017-10-12 09:25:59 -04:00
|
|
|
&data, &type, NULL, &encoder, &headers);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(sep < 0) {
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 21;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Set part headers. */
|
|
|
|
if(curl_mime_headers(part, headers, 1)) {
|
|
|
|
warnf(config->global, "curl_mime_headers failed!\n");
|
|
|
|
curl_slist_free_all(headers);
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 22;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup file in part. */
|
2017-09-03 09:45:43 -04:00
|
|
|
res = file_or_stdin(part, data);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(res) {
|
2017-09-03 09:45:43 -04:00
|
|
|
warnf(config->global, "setting file %s failed!\n", data);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(res != CURLE_READ_ERROR) {
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 23;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-09-02 13:17:33 -04:00
|
|
|
if(literal_value)
|
|
|
|
data = contp;
|
|
|
|
else {
|
2017-10-29 08:57:16 -04:00
|
|
|
sep = get_param_part(config, '\0', &contp,
|
2017-09-05 12:11:59 -04:00
|
|
|
&data, &type, &filename, &encoder, &headers);
|
2017-09-02 13:17:33 -04:00
|
|
|
if(sep < 0) {
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 24;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 25;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
|
2011-10-03 16:59:38 -04:00
|
|
|
#ifdef CURL_DOES_CONVERSIONS
|
2017-09-02 13:17:33 -04:00
|
|
|
if(convert_to_network(data, strlen(data))) {
|
2015-02-27 15:48:38 -05:00
|
|
|
warnf(config->global, "curl_formadd failed!\n");
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 26;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
#endif
|
2017-09-02 13:17:33 -04:00
|
|
|
|
2017-09-03 12:13:44 -04:00
|
|
|
if(curl_mime_data(part, data, CURL_ZERO_TERMINATED)) {
|
2017-09-02 13:17:33 -04:00
|
|
|
warnf(config->global, "curl_mime_data failed!\n");
|
2011-10-03 16:59:38 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 27;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
}
|
2017-09-02 13:17:33 -04:00
|
|
|
|
|
|
|
if(curl_mime_filename(part, filename)) {
|
|
|
|
warnf(config->global, "curl_mime_filename failed!\n");
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 28;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
if(curl_mime_type(part, type)) {
|
|
|
|
warnf(config->global, "curl_mime_type failed!\n");
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 29;
|
|
|
|
}
|
|
|
|
if(curl_mime_encoder(part, encoder)) {
|
|
|
|
warnf(config->global, "curl_mime_encoder failed!\n");
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 30;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if(sep) {
|
|
|
|
*contp = (char) sep;
|
|
|
|
warnf(config->global,
|
|
|
|
"garbage at end of field specification: %s\n", contp);
|
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
|
2017-09-02 13:17:33 -04:00
|
|
|
/* Set part name. */
|
2017-09-21 20:08:29 -04:00
|
|
|
if(name && curl_mime_name(part, name)) {
|
2017-09-02 13:17:33 -04:00
|
|
|
warnf(config->global, "curl_mime_name failed!\n");
|
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 31;
|
2017-09-02 13:17:33 -04:00
|
|
|
}
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
else {
|
2015-02-27 15:48:38 -05:00
|
|
|
warnf(config->global, "Illegally formatted input field!\n");
|
2017-09-02 13:17:33 -04:00
|
|
|
Curl_safefree(contents);
|
2017-09-05 12:11:59 -04:00
|
|
|
return 32;
|
2011-10-03 16:59:38 -04:00
|
|
|
}
|
|
|
|
Curl_safefree(contents);
|
|
|
|
return 0;
|
|
|
|
}
|