mirror of
https://github.com/curl/curl.git
synced 2026-08-26 00:23:32 +03:00
mime: new MIME API.
Available in HTTP, SMTP and IMAP. Deprecates the FORM API. See CURLOPT_MIMEPOST. Lib code and associated documentation.
This commit is contained in:
parent
5bae72734b
commit
ce0881edee
38 changed files with 2946 additions and 994 deletions
|
|
@ -53,7 +53,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
|||
http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \
|
||||
http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \
|
||||
curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \
|
||||
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c
|
||||
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
|
||||
mime.c
|
||||
|
||||
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
||||
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
|
||||
|
|
@ -72,7 +73,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
|||
curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \
|
||||
curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \
|
||||
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
|
||||
curl_printf.h system_win32.h rand.h
|
||||
curl_printf.h system_win32.h rand.h mime.h
|
||||
|
||||
LIB_RCFILES = libcurl.rc
|
||||
|
||||
|
|
|
|||
869
lib/formdata.c
869
lib/formdata.c
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "urldata.h" /* for struct Curl_easy */
|
||||
#include "formdata.h"
|
||||
#include "mime.h"
|
||||
#include "non-ascii.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "strcase.h"
|
||||
#include "sendf.h"
|
||||
|
|
@ -42,13 +44,6 @@
|
|||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
#ifndef HAVE_BASENAME
|
||||
static char *Curl_basename(char *path);
|
||||
#define basename(x) Curl_basename((x))
|
||||
#endif
|
||||
|
||||
static size_t readfromfile(struct Form *form, char *buffer, size_t size);
|
||||
static CURLcode formboundary(struct Curl_easy *data, char *buffer, size_t len);
|
||||
|
||||
/* What kind of Content-Type to use on un-specified files with unrecognized
|
||||
extensions. */
|
||||
|
|
@ -746,211 +741,6 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
|
|||
return result;
|
||||
}
|
||||
|
||||
#ifdef __VMS
|
||||
#include <fabdef.h>
|
||||
/*
|
||||
* get_vms_file_size does what it takes to get the real size of the file
|
||||
*
|
||||
* For fixed files, find out the size of the EOF block and adjust.
|
||||
*
|
||||
* For all others, have to read the entire file in, discarding the contents.
|
||||
* Most posted text files will be small, and binary files like zlib archives
|
||||
* and CD/DVD images should be either a STREAM_LF format or a fixed format.
|
||||
*
|
||||
*/
|
||||
curl_off_t VmsRealFileSize(const char *name,
|
||||
const struct_stat *stat_buf)
|
||||
{
|
||||
char buffer[8192];
|
||||
curl_off_t count;
|
||||
int ret_stat;
|
||||
FILE * file;
|
||||
|
||||
file = fopen(name, FOPEN_READTEXT); /* VMS */
|
||||
if(file == NULL)
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
ret_stat = 1;
|
||||
while(ret_stat > 0) {
|
||||
ret_stat = fread(buffer, 1, sizeof(buffer), file);
|
||||
if(ret_stat != 0)
|
||||
count += ret_stat;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* VmsSpecialSize checks to see if the stat st_size can be trusted and
|
||||
* if not to call a routine to get the correct size.
|
||||
*
|
||||
*/
|
||||
static curl_off_t VmsSpecialSize(const char *name,
|
||||
const struct_stat *stat_buf)
|
||||
{
|
||||
switch(stat_buf->st_fab_rfm) {
|
||||
case FAB$C_VAR:
|
||||
case FAB$C_VFC:
|
||||
return VmsRealFileSize(name, stat_buf);
|
||||
break;
|
||||
default:
|
||||
return stat_buf->st_size;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __VMS
|
||||
#define filesize(name, stat_data) (stat_data.st_size)
|
||||
#else
|
||||
/* Getting the expected file size needs help on VMS */
|
||||
#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AddFormData() adds a chunk of data to the FormData linked list.
|
||||
*
|
||||
* size is incremented by the chunk length, unless it is NULL
|
||||
*/
|
||||
static CURLcode AddFormData(struct FormData **formp,
|
||||
enum formtype type,
|
||||
const void *line,
|
||||
curl_off_t length,
|
||||
curl_off_t *size)
|
||||
{
|
||||
struct FormData *newform;
|
||||
char *alloc2 = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
if(length < 0 || (size && *size < 0))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
newform = malloc(sizeof(struct FormData));
|
||||
if(!newform)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
newform->next = NULL;
|
||||
|
||||
if(type <= FORM_CONTENT) {
|
||||
/* we make it easier for plain strings: */
|
||||
if(!length)
|
||||
length = strlen((char *)line);
|
||||
#if (SIZEOF_SIZE_T < CURL_SIZEOF_CURL_OFF_T)
|
||||
else if(length >= (curl_off_t)(size_t)-1) {
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
if(type != FORM_DATAMEM) {
|
||||
newform->line = malloc((size_t)length+1);
|
||||
if(!newform->line) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto error;
|
||||
}
|
||||
alloc2 = newform->line;
|
||||
memcpy(newform->line, line, (size_t)length);
|
||||
|
||||
/* zero terminate for easier debugging */
|
||||
newform->line[(size_t)length]=0;
|
||||
}
|
||||
else {
|
||||
newform->line = (char *)line;
|
||||
type = FORM_DATA; /* in all other aspects this is just FORM_DATA */
|
||||
}
|
||||
newform->length = (size_t)length;
|
||||
}
|
||||
else
|
||||
/* For callbacks and files we don't have any actual data so we just keep a
|
||||
pointer to whatever this points to */
|
||||
newform->line = (char *)line;
|
||||
|
||||
newform->type = type;
|
||||
|
||||
if(size) {
|
||||
if(type != FORM_FILE)
|
||||
/* for static content as well as callback data we add the size given
|
||||
as input argument */
|
||||
*size += length;
|
||||
else {
|
||||
/* Since this is a file to be uploaded here, add the size of the actual
|
||||
file */
|
||||
if(strcmp("-", newform->line)) {
|
||||
struct_stat file;
|
||||
if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode))
|
||||
*size += filesize(newform->line, file);
|
||||
else {
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*formp) {
|
||||
(*formp)->next = newform;
|
||||
*formp = newform;
|
||||
}
|
||||
else
|
||||
*formp = newform;
|
||||
|
||||
return CURLE_OK;
|
||||
error:
|
||||
if(newform)
|
||||
free(newform);
|
||||
if(alloc2)
|
||||
free(alloc2);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* AddFormDataf() adds printf()-style formatted data to the formdata chain.
|
||||
*/
|
||||
|
||||
static CURLcode AddFormDataf(struct FormData **formp,
|
||||
curl_off_t *size,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char *s;
|
||||
CURLcode result;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
s = curl_mvaprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(!s)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
result = AddFormData(formp, FORM_DATAMEM, s, 0, size);
|
||||
if(result)
|
||||
free(s);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_formclean() is used from http.c, this cleans a built FormData linked
|
||||
* list
|
||||
*/
|
||||
void Curl_formclean(struct FormData **form_ptr)
|
||||
{
|
||||
struct FormData *next, *form;
|
||||
|
||||
form = *form_ptr;
|
||||
if(!form)
|
||||
return;
|
||||
|
||||
do {
|
||||
next=form->next; /* the following form line */
|
||||
if(form->type <= FORM_CONTENT)
|
||||
free(form->line); /* free the line */
|
||||
free(form); /* free the struct */
|
||||
form = next;
|
||||
} while(form); /* continue */
|
||||
|
||||
*form_ptr = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* curl_formget()
|
||||
* Serialize a curl_httppost struct.
|
||||
|
|
@ -962,42 +752,34 @@ int curl_formget(struct curl_httppost *form, void *arg,
|
|||
curl_formget_callback append)
|
||||
{
|
||||
CURLcode result;
|
||||
curl_off_t size;
|
||||
struct FormData *data, *ptr;
|
||||
struct Curl_mimepart toppart;
|
||||
|
||||
result = Curl_getformdata(NULL, &data, form, NULL, &size);
|
||||
if(result)
|
||||
return (int)result;
|
||||
Curl_mime_initpart(&toppart, NULL); /* default form is empty */
|
||||
result = Curl_getformdata(NULL, &toppart, form, NULL);
|
||||
if(!result)
|
||||
result = Curl_mime_prepare_headers(&toppart, "multipart/form-data",
|
||||
NULL, MIMESTRATEGY_FORM);
|
||||
|
||||
for(ptr = data; ptr; ptr = ptr->next) {
|
||||
if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
|
||||
char buffer[8192];
|
||||
size_t nread;
|
||||
struct Form temp;
|
||||
while(!result) {
|
||||
char buffer[8192];
|
||||
size_t nread = Curl_mime_read(buffer, 1, sizeof buffer, &toppart);
|
||||
|
||||
Curl_FormInit(&temp, ptr);
|
||||
if(!nread)
|
||||
break;
|
||||
|
||||
do {
|
||||
nread = readfromfile(&temp, buffer, sizeof(buffer));
|
||||
if((nread == (size_t) -1) ||
|
||||
(nread > sizeof(buffer)) ||
|
||||
(nread != append(arg, buffer, nread))) {
|
||||
if(temp.fp)
|
||||
fclose(temp.fp);
|
||||
Curl_formclean(&data);
|
||||
return -1;
|
||||
}
|
||||
} while(nread);
|
||||
}
|
||||
else {
|
||||
if(ptr->length != append(arg, ptr->line, ptr->length)) {
|
||||
Curl_formclean(&data);
|
||||
return -1;
|
||||
}
|
||||
switch(nread) {
|
||||
default:
|
||||
if(append(arg, buffer, nread) != nread)
|
||||
result = CURLE_READ_ERROR;
|
||||
break;
|
||||
case CURL_READFUNC_ABORT:
|
||||
case CURL_READFUNC_PAUSE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Curl_formclean(&data);
|
||||
return 0;
|
||||
|
||||
Curl_mime_cleanpart(&toppart);
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1031,118 +813,11 @@ void curl_formfree(struct curl_httppost *form)
|
|||
} while(form); /* continue */
|
||||
}
|
||||
|
||||
#ifndef HAVE_BASENAME
|
||||
/*
|
||||
(Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
|
||||
Edition)
|
||||
|
||||
The basename() function shall take the pathname pointed to by path and
|
||||
return a pointer to the final component of the pathname, deleting any
|
||||
trailing '/' characters.
|
||||
|
||||
If the string pointed to by path consists entirely of the '/' character,
|
||||
basename() shall return a pointer to the string "/". If the string pointed
|
||||
to by path is exactly "//", it is implementation-defined whether '/' or "//"
|
||||
is returned.
|
||||
|
||||
If path is a null pointer or points to an empty string, basename() shall
|
||||
return a pointer to the string ".".
|
||||
|
||||
The basename() function may modify the string pointed to by path, and may
|
||||
return a pointer to static storage that may then be overwritten by a
|
||||
subsequent call to basename().
|
||||
|
||||
The basename() function need not be reentrant. A function that is not
|
||||
required to be reentrant is not required to be thread-safe.
|
||||
|
||||
*/
|
||||
static char *Curl_basename(char *path)
|
||||
{
|
||||
/* Ignore all the details above for now and make a quick and simple
|
||||
implementaion here */
|
||||
char *s1;
|
||||
char *s2;
|
||||
|
||||
s1=strrchr(path, '/');
|
||||
s2=strrchr(path, '\\');
|
||||
|
||||
if(s1 && s2) {
|
||||
path = (s1 > s2? s1 : s2)+1;
|
||||
}
|
||||
else if(s1)
|
||||
path = s1 + 1;
|
||||
else if(s2)
|
||||
path = s2 + 1;
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *strippath(const char *fullfile)
|
||||
{
|
||||
char *filename;
|
||||
char *base;
|
||||
filename = strdup(fullfile); /* duplicate since basename() may ruin the
|
||||
buffer it works on */
|
||||
if(!filename)
|
||||
return NULL;
|
||||
base = strdup(basename(filename));
|
||||
|
||||
free(filename); /* free temporary buffer */
|
||||
|
||||
return base; /* returns an allocated string or NULL ! */
|
||||
}
|
||||
|
||||
static CURLcode formdata_add_filename(const struct curl_httppost *file,
|
||||
struct FormData **form,
|
||||
curl_off_t *size)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *filename = file->showfilename;
|
||||
char *filebasename = NULL;
|
||||
char *filename_escaped = NULL;
|
||||
|
||||
if(!filename) {
|
||||
filebasename = strippath(file->contents);
|
||||
if(!filebasename)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
filename = filebasename;
|
||||
}
|
||||
|
||||
if(strchr(filename, '\\') || strchr(filename, '"')) {
|
||||
char *p0, *p1;
|
||||
|
||||
/* filename need be escaped */
|
||||
filename_escaped = malloc(strlen(filename)*2+1);
|
||||
if(!filename_escaped) {
|
||||
free(filebasename);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
p0 = filename_escaped;
|
||||
p1 = filename;
|
||||
while(*p1) {
|
||||
if(*p1 == '\\' || *p1 == '"')
|
||||
*p0++ = '\\';
|
||||
*p0++ = *p1++;
|
||||
}
|
||||
*p0 = '\0';
|
||||
filename = filename_escaped;
|
||||
}
|
||||
result = AddFormDataf(form, size,
|
||||
"; filename=\"%s\"",
|
||||
filename);
|
||||
free(filename_escaped);
|
||||
free(filebasename);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_getformdata() converts a linked list of "meta data" into a complete
|
||||
* (possibly huge) multipart formdata. The input list is in 'post', while the
|
||||
* output resulting linked lists gets stored in '*finalform'. *sizep will get
|
||||
* the total size of the whole POST.
|
||||
* A multipart/form_data content-type is built, unless a custom content-type
|
||||
* is passed in 'custom_content_type'.
|
||||
* Curl_getformdata() converts a linked list of "meta data" into a mime
|
||||
* structure. The input list is in 'post', while the output is stored in
|
||||
* mime part at '*finalform'.
|
||||
*
|
||||
* This function will not do a failf() for the potential memory failures but
|
||||
* should for all other errors it spots. Just note that this function MAY get
|
||||
|
|
@ -1150,422 +825,114 @@ static CURLcode formdata_add_filename(const struct curl_httppost *file,
|
|||
*/
|
||||
|
||||
CURLcode Curl_getformdata(struct Curl_easy *data,
|
||||
struct FormData **finalform,
|
||||
struct Curl_mimepart *finalform,
|
||||
struct curl_httppost *post,
|
||||
const char *custom_content_type,
|
||||
curl_off_t *sizep)
|
||||
curl_read_callback fread_func)
|
||||
{
|
||||
struct FormData *form = NULL;
|
||||
struct FormData *firstform;
|
||||
struct curl_httppost *file;
|
||||
CURLcode result = CURLE_OK;
|
||||
curl_off_t size = 0; /* support potentially ENORMOUS formposts */
|
||||
char fileboundary[42];
|
||||
struct curl_slist *curList;
|
||||
char boundary[42];
|
||||
struct Curl_mime *form = NULL;
|
||||
struct Curl_mime *multipart;
|
||||
struct Curl_mimepart *part;
|
||||
struct curl_httppost *file;
|
||||
|
||||
*finalform = NULL; /* default form is empty */
|
||||
Curl_mime_cleanpart(finalform); /* default form is empty */
|
||||
|
||||
if(!post)
|
||||
return result; /* no input => no output! */
|
||||
|
||||
result = formboundary(data, boundary, sizeof(boundary));
|
||||
if(result)
|
||||
return result;
|
||||
form = curl_mime_init(data);
|
||||
if(!form)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* Make the first line of the output */
|
||||
result = AddFormDataf(&form, NULL,
|
||||
"%s; boundary=%s\r\n",
|
||||
custom_content_type?custom_content_type:
|
||||
"Content-Type: multipart/form-data",
|
||||
boundary);
|
||||
|
||||
if(result) {
|
||||
return result;
|
||||
}
|
||||
/* we DO NOT include that line in the total size of the POST, since it'll be
|
||||
part of the header! */
|
||||
|
||||
firstform = form;
|
||||
|
||||
do {
|
||||
|
||||
if(size) {
|
||||
result = AddFormDataf(&form, &size, "\r\n");
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
/* boundary */
|
||||
result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
/* Maybe later this should be disabled when a custom_content_type is
|
||||
passed, since Content-Disposition is not meaningful for all multipart
|
||||
types.
|
||||
*/
|
||||
result = AddFormDataf(&form, &size,
|
||||
"Content-Disposition: form-data; name=\"");
|
||||
if(result)
|
||||
break;
|
||||
|
||||
result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
|
||||
&size);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
result = AddFormDataf(&form, &size, "\"");
|
||||
if(result)
|
||||
break;
|
||||
|
||||
if(post->more) {
|
||||
/* If used, this is a link to more file names, we must then do
|
||||
the magic to include several files with the same field name */
|
||||
|
||||
result = formboundary(data, fileboundary, sizeof(fileboundary));
|
||||
if(result) {
|
||||
break;
|
||||
}
|
||||
|
||||
result = AddFormDataf(&form, &size,
|
||||
"\r\nContent-Type: multipart/mixed;"
|
||||
" boundary=%s\r\n",
|
||||
fileboundary);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
file = post;
|
||||
|
||||
do {
|
||||
|
||||
/* If 'showfilename' is set, that is a faked name passed on to us
|
||||
to use to in the formpost. If that is not set, the actually used
|
||||
local file name should be added. */
|
||||
|
||||
if(post->more) {
|
||||
/* if multiple-file */
|
||||
result = AddFormDataf(&form, &size,
|
||||
"\r\n--%s\r\nContent-Disposition: "
|
||||
"attachment",
|
||||
fileboundary);
|
||||
if(result)
|
||||
break;
|
||||
result = formdata_add_filename(file, &form, &size);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
|
||||
HTTPPOST_CALLBACK)) {
|
||||
/* it should be noted that for the HTTPPOST_FILENAME and
|
||||
HTTPPOST_CALLBACK cases the ->showfilename struct member is always
|
||||
assigned at this point */
|
||||
if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
|
||||
result = formdata_add_filename(post, &form, &size);
|
||||
}
|
||||
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
if(file->contenttype) {
|
||||
/* we have a specified type */
|
||||
result = AddFormDataf(&form, &size,
|
||||
"\r\nContent-Type: %s",
|
||||
file->contenttype);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
curList = file->contentheader;
|
||||
while(curList) {
|
||||
/* Process the additional headers specified for this form */
|
||||
result = AddFormDataf(&form, &size, "\r\n%s", curList->data);
|
||||
if(result)
|
||||
break;
|
||||
curList = curList->next;
|
||||
}
|
||||
if(result)
|
||||
break;
|
||||
|
||||
result = AddFormDataf(&form, &size, "\r\n\r\n");
|
||||
if(result)
|
||||
break;
|
||||
|
||||
if((post->flags & HTTPPOST_FILENAME) ||
|
||||
(post->flags & HTTPPOST_READFILE)) {
|
||||
/* we should include the contents from the specified file */
|
||||
FILE *fileread;
|
||||
|
||||
fileread = !strcmp("-", file->contents)?
|
||||
stdin:fopen(file->contents, "rb"); /* binary read for win32 */
|
||||
|
||||
/*
|
||||
* VMS: This only allows for stream files on VMS. Stream files are
|
||||
* OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
|
||||
* every record needs to have a \n appended & 1 added to SIZE
|
||||
*/
|
||||
|
||||
if(fileread) {
|
||||
if(fileread != stdin) {
|
||||
/* close the file */
|
||||
fclose(fileread);
|
||||
/* add the file name only - for later reading from this */
|
||||
result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
|
||||
}
|
||||
else {
|
||||
/* When uploading from stdin, we can't know the size of the file,
|
||||
* thus must read the full file as before. We *could* use chunked
|
||||
* transfer-encoding, but that only works for HTTP 1.1 and we
|
||||
* can't be sure we work with such a server.
|
||||
*/
|
||||
size_t nread;
|
||||
char buffer[512];
|
||||
while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
|
||||
result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
|
||||
if(result || feof(fileread) || ferror(fileread))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data)
|
||||
failf(data, "couldn't open file \"%s\"", file->contents);
|
||||
*finalform = NULL;
|
||||
result = CURLE_READ_ERROR;
|
||||
}
|
||||
}
|
||||
else if(post->flags & HTTPPOST_BUFFER)
|
||||
/* include contents of buffer */
|
||||
result = AddFormData(&form, FORM_CONTENT, post->buffer,
|
||||
post->bufferlength, &size);
|
||||
else if(post->flags & HTTPPOST_CALLBACK)
|
||||
/* the contents should be read with the callback and the size is set
|
||||
with the contentslength */
|
||||
result = AddFormData(&form, FORM_CALLBACK, post->userp,
|
||||
post->flags&CURL_HTTPPOST_LARGE?
|
||||
post->contentlen:post->contentslength, &size);
|
||||
else
|
||||
/* include the contents we got */
|
||||
result = AddFormData(&form, FORM_CONTENT, post->contents,
|
||||
post->flags&CURL_HTTPPOST_LARGE?
|
||||
post->contentlen:post->contentslength, &size);
|
||||
file = file->more;
|
||||
} while(file && !result); /* for each specified file for this field */
|
||||
|
||||
if(result)
|
||||
break;
|
||||
|
||||
if(post->more) {
|
||||
/* this was a multiple-file inclusion, make a termination file
|
||||
boundary: */
|
||||
result = AddFormDataf(&form, &size,
|
||||
"\r\n--%s--",
|
||||
fileboundary);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
post = post->next;
|
||||
} while(post); /* for each field */
|
||||
|
||||
/* end-boundary for everything */
|
||||
if(!result)
|
||||
result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary);
|
||||
result = curl_mime_subparts(finalform, form);
|
||||
|
||||
if(result) {
|
||||
Curl_formclean(&firstform);
|
||||
return result;
|
||||
/* Process each top part. */
|
||||
for(; !result && post; post = post->next) {
|
||||
/* If we have more than a file here, create a mime subpart and fill it. */
|
||||
multipart = form;
|
||||
if(post->more) {
|
||||
part = curl_mime_addpart(form);
|
||||
if(!part)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
if(!result)
|
||||
result = curl_mime_name(part, post->name,
|
||||
post->namelength? post->namelength: -1);
|
||||
if(!result) {
|
||||
multipart = curl_mime_init(data);
|
||||
if(!multipart)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
if(!result)
|
||||
result = curl_mime_subparts(part, multipart);
|
||||
}
|
||||
|
||||
/* Generate all the part contents. */
|
||||
for(file = post; !result && file; file = file->more) {
|
||||
/* Create the part. */
|
||||
part = curl_mime_addpart(multipart);
|
||||
if(!part)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* Set the headers. */
|
||||
if(!result)
|
||||
result = curl_mime_headers(part, file->contentheader, 0);
|
||||
|
||||
/* Set the content type. */
|
||||
if(!result &&file->contenttype)
|
||||
result = curl_mime_type(part, file->contenttype);
|
||||
|
||||
/* Set field name. */
|
||||
if(!result && !post->more)
|
||||
result = curl_mime_name(part, post->name,
|
||||
post->namelength? post->namelength: -1);
|
||||
|
||||
/* Process contents. */
|
||||
if(!result) {
|
||||
curl_off_t clen = post->contentslength;
|
||||
|
||||
if(post->flags & CURL_HTTPPOST_LARGE)
|
||||
clen = post->contentlen;
|
||||
if(!clen)
|
||||
clen = -1;
|
||||
|
||||
if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
|
||||
result = curl_mime_filedata(part, file->contents);
|
||||
if(!result && (post->flags & HTTPPOST_READFILE))
|
||||
result = curl_mime_filename(part, NULL);
|
||||
}
|
||||
else if(post->flags & HTTPPOST_BUFFER)
|
||||
result = curl_mime_data(part, post->buffer,
|
||||
post->bufferlength? post->bufferlength: -1);
|
||||
else if(post->flags & HTTPPOST_CALLBACK)
|
||||
/* the contents should be read with the callback and the size is set
|
||||
with the contentslength */
|
||||
result = curl_mime_data_cb(part, clen,
|
||||
fread_func, NULL, NULL, post->userp);
|
||||
else {
|
||||
result = curl_mime_data(part, post->contents, (ssize_t) clen);
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
/* Convert textual contents now. */
|
||||
if(!result && data && part->datasize)
|
||||
result = Curl_convert_to_network(data, part->data, part->datasize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Set fake file name. */
|
||||
if(!result && post->showfilename)
|
||||
if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER |
|
||||
HTTPPOST_CALLBACK)))
|
||||
result = curl_mime_filename(part, post->showfilename);
|
||||
}
|
||||
}
|
||||
|
||||
*sizep = size;
|
||||
*finalform = firstform;
|
||||
if(result)
|
||||
Curl_mime_cleanpart(finalform);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_FormInit() inits the struct 'form' points to with the 'formdata'
|
||||
* and resets the 'sent' counter.
|
||||
*/
|
||||
int Curl_FormInit(struct Form *form, struct FormData *formdata)
|
||||
{
|
||||
if(!formdata)
|
||||
return 1; /* error */
|
||||
|
||||
form->data = formdata;
|
||||
form->sent = 0;
|
||||
form->fp = NULL;
|
||||
form->fread_func = ZERO_NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __VMS
|
||||
# define fopen_read fopen
|
||||
#else
|
||||
/*
|
||||
* vmsfopenread
|
||||
*
|
||||
* For upload to work as expected on VMS, different optional
|
||||
* parameters must be added to the fopen command based on
|
||||
* record format of the file.
|
||||
*
|
||||
*/
|
||||
# define fopen_read vmsfopenread
|
||||
static FILE * vmsfopenread(const char *file, const char *mode)
|
||||
{
|
||||
struct_stat statbuf;
|
||||
int result;
|
||||
|
||||
result = stat(file, &statbuf);
|
||||
|
||||
switch(statbuf.st_fab_rfm) {
|
||||
case FAB$C_VAR:
|
||||
case FAB$C_VFC:
|
||||
case FAB$C_STMCR:
|
||||
return fopen(file, FOPEN_READTEXT); /* VMS */
|
||||
break;
|
||||
default:
|
||||
return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* readfromfile()
|
||||
*
|
||||
* The read callback that this function may use can return a value larger than
|
||||
* 'size' (which then this function returns) that indicates a problem and it
|
||||
* must be properly dealt with
|
||||
*/
|
||||
static size_t readfromfile(struct Form *form, char *buffer,
|
||||
size_t size)
|
||||
{
|
||||
size_t nread;
|
||||
bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
|
||||
|
||||
if(callback) {
|
||||
if(form->fread_func == ZERO_NULL)
|
||||
return 0;
|
||||
nread = form->fread_func(buffer, 1, size, form->data->line);
|
||||
}
|
||||
else {
|
||||
if(!form->fp) {
|
||||
/* this file hasn't yet been opened */
|
||||
form->fp = fopen_read(form->data->line, "rb"); /* b is for binary */
|
||||
if(!form->fp)
|
||||
return (size_t)-1; /* failure */
|
||||
}
|
||||
nread = fread(buffer, 1, size, form->fp);
|
||||
}
|
||||
if(!nread) {
|
||||
/* this is the last chunk from the file, move on */
|
||||
if(form->fp) {
|
||||
fclose(form->fp);
|
||||
form->fp = NULL;
|
||||
}
|
||||
form->data = form->data->next;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_FormReader() is the fread() emulation function that will be used to
|
||||
* deliver the formdata to the transfer loop and then sent away to the peer.
|
||||
*/
|
||||
size_t Curl_FormReader(char *buffer,
|
||||
size_t size,
|
||||
size_t nitems,
|
||||
FILE *mydata)
|
||||
{
|
||||
struct Form *form;
|
||||
size_t wantedsize;
|
||||
size_t gotsize = 0;
|
||||
|
||||
form=(struct Form *)mydata;
|
||||
|
||||
wantedsize = size * nitems;
|
||||
|
||||
if(!form->data)
|
||||
return 0; /* nothing, error, empty */
|
||||
|
||||
if((form->data->type == FORM_FILE) ||
|
||||
(form->data->type == FORM_CALLBACK)) {
|
||||
gotsize = readfromfile(form, buffer, wantedsize);
|
||||
|
||||
if(gotsize)
|
||||
/* If positive or -1, return. If zero, continue! */
|
||||
return gotsize;
|
||||
}
|
||||
do {
|
||||
|
||||
if((form->data->length - form->sent) > wantedsize - gotsize) {
|
||||
|
||||
memcpy(buffer + gotsize, form->data->line + form->sent,
|
||||
wantedsize - gotsize);
|
||||
|
||||
form->sent += wantedsize-gotsize;
|
||||
|
||||
return wantedsize;
|
||||
}
|
||||
|
||||
memcpy(buffer+gotsize,
|
||||
form->data->line + form->sent,
|
||||
(form->data->length - form->sent) );
|
||||
gotsize += form->data->length - form->sent;
|
||||
|
||||
form->sent = 0;
|
||||
|
||||
form->data = form->data->next; /* advance */
|
||||
|
||||
} while(form->data && (form->data->type < FORM_CALLBACK));
|
||||
/* If we got an empty line and we have more data, we proceed to the next
|
||||
line immediately to avoid returning zero before we've reached the end. */
|
||||
|
||||
return gotsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_formpostheader() returns the first line of the formpost, the
|
||||
* request-header part (which is not part of the request-body like the rest of
|
||||
* the post).
|
||||
*/
|
||||
char *Curl_formpostheader(void *formp, size_t *len)
|
||||
{
|
||||
char *header;
|
||||
struct Form *form=(struct Form *)formp;
|
||||
|
||||
if(!form->data)
|
||||
return NULL; /* nothing, ERROR! */
|
||||
|
||||
header = form->data->line;
|
||||
*len = form->data->length;
|
||||
|
||||
form->data = form->data->next; /* advance */
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/*
|
||||
* formboundary() creates a suitable boundary string and returns an allocated
|
||||
* one.
|
||||
*/
|
||||
static CURLcode formboundary(struct Curl_easy *data,
|
||||
char *buffer, size_t buflen)
|
||||
{
|
||||
/* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615)
|
||||
combinations */
|
||||
if(buflen < 41)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
memset(buffer, '-', 24);
|
||||
Curl_rand_hex(data, (unsigned char *)&buffer[24], 17);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#else /* CURL_DISABLE_HTTP */
|
||||
CURLFORMcode curl_formadd(struct curl_httppost **httppost,
|
||||
struct curl_httppost **last_post,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,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
|
||||
|
|
@ -22,32 +22,6 @@
|
|||
*
|
||||
***************************************************************************/
|
||||
|
||||
enum formtype {
|
||||
FORM_DATAMEM, /* already allocated FORM_DATA memory */
|
||||
FORM_DATA, /* form metadata (convert to network encoding if necessary) */
|
||||
FORM_CONTENT, /* form content (never convert) */
|
||||
FORM_CALLBACK, /* 'line' points to the custom pointer we pass to the callback
|
||||
*/
|
||||
FORM_FILE /* 'line' points to a file name we should read from
|
||||
to create the form data (never convert) */
|
||||
};
|
||||
|
||||
/* plain and simple linked list with lines to send */
|
||||
struct FormData {
|
||||
struct FormData *next;
|
||||
enum formtype type;
|
||||
char *line;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct Form {
|
||||
struct FormData *data; /* current form line to send */
|
||||
size_t sent; /* number of bytes of the current line that has
|
||||
already been sent in a previous invoke */
|
||||
FILE *fp; /* file to read from */
|
||||
curl_read_callback fread_func; /* fread callback pointer */
|
||||
};
|
||||
|
||||
/* used by FormAdd for temporary storage */
|
||||
typedef struct FormInfo {
|
||||
char *name;
|
||||
|
|
@ -69,31 +43,9 @@ typedef struct FormInfo {
|
|||
struct FormInfo *more;
|
||||
} FormInfo;
|
||||
|
||||
int Curl_FormInit(struct Form *form, struct FormData *formdata);
|
||||
|
||||
CURLcode Curl_getformdata(struct Curl_easy *data,
|
||||
struct FormData **,
|
||||
struct Curl_mimepart *,
|
||||
struct curl_httppost *post,
|
||||
const char *custom_contenttype,
|
||||
curl_off_t *size);
|
||||
|
||||
/* fread() emulation */
|
||||
size_t Curl_FormReader(char *buffer,
|
||||
size_t size,
|
||||
size_t nitems,
|
||||
FILE *mydata);
|
||||
|
||||
/*
|
||||
* Curl_formpostheader() returns the first line of the formpost, the
|
||||
* request-header part (which is not part of the request-body like the rest of
|
||||
* the post).
|
||||
*/
|
||||
char *Curl_formpostheader(void *formp, size_t *len);
|
||||
|
||||
char *Curl_FormBoundary(void);
|
||||
|
||||
void Curl_formclean(struct FormData **);
|
||||
|
||||
CURLcode Curl_formconvert(struct Curl_easy *, struct FormData *);
|
||||
curl_read_callback fread_func);
|
||||
|
||||
#endif /* HEADER_CURL_FORMDATA_H */
|
||||
|
|
|
|||
289
lib/http.c
289
lib/http.c
|
|
@ -50,6 +50,7 @@
|
|||
#include "transfer.h"
|
||||
#include "sendf.h"
|
||||
#include "formdata.h"
|
||||
#include "mime.h"
|
||||
#include "progress.h"
|
||||
#include "curl_base64.h"
|
||||
#include "cookie.h"
|
||||
|
|
@ -162,6 +163,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
|
|||
if(!http)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
Curl_mime_initpart(&http->form, conn->data);
|
||||
conn->data->req.protop = http;
|
||||
|
||||
Curl_http2_setup_conn(conn);
|
||||
|
|
@ -427,6 +429,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
|
|||
expectsend = data->state.infilesize;
|
||||
break;
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
expectsend = http->postsize;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1470,18 +1473,17 @@ CURLcode Curl_http_done(struct connectdata *conn,
|
|||
|
||||
Curl_http2_done(conn, premature);
|
||||
|
||||
if(HTTPREQ_POST_FORM == data->set.httpreq) {
|
||||
data->req.bytecount = http->readbytecount + http->writebytecount;
|
||||
Curl_mime_cleanpart(&http->form);
|
||||
|
||||
Curl_formclean(&http->sendit); /* Now free that whole lot */
|
||||
if(http->form.fp) {
|
||||
/* a file being uploaded was left opened, close it! */
|
||||
fclose(http->form.fp);
|
||||
http->form.fp = NULL;
|
||||
}
|
||||
}
|
||||
else if(HTTPREQ_PUT == data->set.httpreq)
|
||||
switch(data->set.httpreq) {
|
||||
case HTTPREQ_PUT:
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
data->req.bytecount = http->readbytecount + http->writebytecount;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(status)
|
||||
return status;
|
||||
|
|
@ -1637,6 +1639,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|||
/* this header (extended by formdata.c) is sent later */
|
||||
checkprefix("Content-Type:", headers->data))
|
||||
;
|
||||
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
|
||||
/* this header is sent later */
|
||||
checkprefix("Content-Type:", headers->data))
|
||||
;
|
||||
else if(conn->bits.authneg &&
|
||||
/* while doing auth neg, don't allow the custom length since
|
||||
we will force length zero then */
|
||||
|
|
@ -1775,7 +1781,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
const char *httpstring;
|
||||
Curl_send_buffer *req_buffer;
|
||||
curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
|
||||
int seekerr = CURL_SEEKFUNC_OK;
|
||||
int seekerr = CURL_SEEKFUNC_CANTSEEK;
|
||||
|
||||
/* Always consider the DO phase done after this function call, even if there
|
||||
may be parts of the request that is not yet sent, since we can deal with
|
||||
|
|
@ -1848,6 +1854,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
switch(httpreq) {
|
||||
case HTTPREQ_POST:
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
request = "POST";
|
||||
break;
|
||||
case HTTPREQ_PUT:
|
||||
|
|
@ -1942,6 +1949,48 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
}
|
||||
#endif
|
||||
|
||||
switch(httpreq) {
|
||||
case HTTPREQ_POST_MIME:
|
||||
http->sendit = &data->set.mimepost;
|
||||
break;
|
||||
case HTTPREQ_POST_FORM:
|
||||
/* Convert the form structure into a mime structure. */
|
||||
Curl_mime_cleanpart(&http->form);
|
||||
result = Curl_getformdata(data, &http->form, data->set.httppost,
|
||||
data->state.fread_func);
|
||||
if(result)
|
||||
return result;
|
||||
http->sendit = &http->form;
|
||||
break;
|
||||
default:
|
||||
http->sendit = NULL;
|
||||
}
|
||||
|
||||
if(http->sendit) {
|
||||
const char *cthdr = Curl_checkheaders(conn, "Content-Type:");
|
||||
|
||||
/* Read and seek body only. */
|
||||
http->sendit->flags |= MIME_BODY_ONLY;
|
||||
|
||||
/* Prepare the mime structure headers & set content type. */
|
||||
|
||||
if(cthdr)
|
||||
for(cthdr += 13; *cthdr == ' '; cthdr++)
|
||||
;
|
||||
else if(http->sendit->kind == MIMEKIND_MULTIPART)
|
||||
cthdr = "multipart/form-data";
|
||||
|
||||
curl_mime_headers(http->sendit, data->set.headers, 0);
|
||||
result = Curl_mime_prepare_headers(http->sendit, cthdr,
|
||||
NULL, MIMESTRATEGY_FORM);
|
||||
curl_mime_headers(http->sendit, NULL, 0);
|
||||
if(!result)
|
||||
result = Curl_mime_rewind(http->sendit);
|
||||
if(result)
|
||||
return result;
|
||||
http->postsize = Curl_mime_size(http->sendit);
|
||||
}
|
||||
|
||||
ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
|
||||
if(ptr) {
|
||||
/* Some kind of TE is requested, check if 'chunked' is chosen */
|
||||
|
|
@ -1949,9 +1998,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
|
||||
}
|
||||
else {
|
||||
if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
data->set.upload &&
|
||||
(data->state.infilesize == -1)) {
|
||||
if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
|
||||
(((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
|
||||
http->postsize < 0) ||
|
||||
(data->set.upload && data->state.infilesize == -1))) {
|
||||
if(conn->bits.authneg)
|
||||
/* don't enable chunked during auth neg */
|
||||
;
|
||||
|
|
@ -2123,21 +2173,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
}
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
|
||||
if(HTTPREQ_POST_FORM == httpreq) {
|
||||
/* we must build the whole post sequence first, so that we have a size of
|
||||
the whole transfer before we start to send it */
|
||||
result = Curl_getformdata(data, &http->sendit, data->set.httppost,
|
||||
Curl_checkheaders(conn, "Content-Type:"),
|
||||
&http->postsize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";
|
||||
|
||||
if(( (HTTPREQ_POST == httpreq) ||
|
||||
(HTTPREQ_POST_FORM == httpreq) ||
|
||||
(HTTPREQ_PUT == httpreq) ) &&
|
||||
if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
|
||||
data->state.resume_from) {
|
||||
/**********************************************************************
|
||||
* Resuming upload in HTTP means that we PUT or POST and that we have
|
||||
|
|
@ -2145,6 +2183,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
* a Range: header that will be passed along. We need to "fast forward"
|
||||
* the file the given number of bytes and decrease the assume upload
|
||||
* file size before we continue this venture in the dark lands of HTTP.
|
||||
* Resuming mime/form posting at an offset > 0 has no sense and is ignored.
|
||||
*********************************************************************/
|
||||
|
||||
if(data->state.resume_from < 0) {
|
||||
|
|
@ -2219,7 +2258,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
|
||||
data->state.range);
|
||||
}
|
||||
else if((httpreq != HTTPREQ_GET) &&
|
||||
else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
|
||||
!Curl_checkheaders(conn, "Content-Range:")) {
|
||||
|
||||
/* if a line like this was already allocated, free the previous one */
|
||||
|
|
@ -2415,107 +2454,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
|
||||
switch(httpreq) {
|
||||
|
||||
case HTTPREQ_POST_FORM:
|
||||
if(!http->sendit || conn->bits.authneg) {
|
||||
/* nothing to post! */
|
||||
result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_add_buffer_send(req_buffer, conn,
|
||||
&data->info.request_size, 0, FIRSTSOCKET);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
|
||||
-1, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if(Curl_FormInit(&http->form, http->sendit)) {
|
||||
failf(data, "Internal HTTP POST error!");
|
||||
return CURLE_HTTP_POST_ERROR;
|
||||
}
|
||||
|
||||
/* Get the currently set callback function pointer and store that in the
|
||||
form struct since we might want the actual user-provided callback later
|
||||
on. The data->set.fread_func pointer itself will be changed for the
|
||||
multipart case to the function that returns a multipart formatted
|
||||
stream. */
|
||||
http->form.fread_func = data->state.fread_func;
|
||||
|
||||
/* Set the read function to read from the generated form data */
|
||||
data->state.fread_func = (curl_read_callback)Curl_FormReader;
|
||||
data->state.in = &http->form;
|
||||
|
||||
http->sending = HTTPSEND_BODY;
|
||||
|
||||
if(!data->req.upload_chunky &&
|
||||
!Curl_checkheaders(conn, "Content-Length:")) {
|
||||
/* only add Content-Length if not uploading chunked */
|
||||
result = Curl_add_bufferf(req_buffer,
|
||||
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
|
||||
"\r\n", http->postsize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = expect100(data, conn, req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
{
|
||||
|
||||
/* Get Content-Type: line from Curl_formpostheader.
|
||||
*/
|
||||
char *contentType;
|
||||
size_t linelength=0;
|
||||
contentType = Curl_formpostheader((void *)&http->form,
|
||||
&linelength);
|
||||
if(!contentType) {
|
||||
failf(data, "Could not get Content-Type header line!");
|
||||
return CURLE_HTTP_POST_ERROR;
|
||||
}
|
||||
|
||||
result = Curl_add_buffer(req_buffer, contentType, linelength);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* make the request end in a true CRLF */
|
||||
result = Curl_add_buffer(req_buffer, "\r\n", 2);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* set upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
|
||||
/* fire away the whole request to the server */
|
||||
result = Curl_add_buffer_send(req_buffer, conn,
|
||||
&data->info.request_size, 0, FIRSTSOCKET);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
|
||||
&http->readbytecount, FIRSTSOCKET,
|
||||
&http->writebytecount);
|
||||
|
||||
if(result) {
|
||||
Curl_formclean(&http->sendit); /* free that whole lot */
|
||||
return result;
|
||||
}
|
||||
|
||||
/* convert the form data */
|
||||
result = Curl_convert_form(data, http->sendit);
|
||||
if(result) {
|
||||
Curl_formclean(&http->sendit); /* free that whole lot */
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HTTPREQ_PUT: /* Let's PUT the data to the server! */
|
||||
|
||||
if(conn->bits.authneg)
|
||||
|
|
@ -2560,6 +2498,98 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|||
return result;
|
||||
break;
|
||||
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
/* This is form posting using mime data. */
|
||||
if(conn->bits.authneg) {
|
||||
/* nothing to post! */
|
||||
result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_add_buffer_send(req_buffer, conn,
|
||||
&data->info.request_size, 0, FIRSTSOCKET);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
|
||||
-1, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
postsize = http->postsize;
|
||||
|
||||
/* We only set Content-Length and allow a custom Content-Length if
|
||||
we don't upload data chunked, as RFC2616 forbids us to set both
|
||||
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
|
||||
if(postsize != -1 && !data->req.upload_chunky &&
|
||||
(conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
|
||||
/* we allow replacing this header if not during auth negotiation,
|
||||
although it isn't very wise to actually set your own */
|
||||
result = Curl_add_bufferf(req_buffer,
|
||||
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
|
||||
"\r\n", postsize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Output mime-generated headers. */
|
||||
{
|
||||
struct curl_slist *hdr;
|
||||
|
||||
for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
|
||||
result = Curl_add_bufferf(req_buffer, "%s\r\n", hdr->data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* For really small posts we don't use Expect: headers at all, and for
|
||||
the somewhat bigger ones we allow the app to disable it. Just make
|
||||
sure that the expect100header is always set to the preferred value
|
||||
here. */
|
||||
ptr = Curl_checkheaders(conn, "Expect:");
|
||||
if(ptr) {
|
||||
data->state.expect100header =
|
||||
Curl_compareheader(ptr, "Expect:", "100-continue");
|
||||
}
|
||||
else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) {
|
||||
result = expect100(data, conn, req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else
|
||||
data->state.expect100header = FALSE;
|
||||
|
||||
/* make the request end in a true CRLF */
|
||||
result = Curl_add_buffer(req_buffer, "\r\n", 2);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* set the upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, postsize);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) http->sendit;
|
||||
http->sending = HTTPSEND_BODY;
|
||||
|
||||
/* this sends the buffer and frees all the buffer resources */
|
||||
result = Curl_add_buffer_send(req_buffer, conn,
|
||||
&data->info.request_size, 0, FIRSTSOCKET);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* prepare for transfer */
|
||||
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
|
||||
&http->readbytecount, postsize?FIRSTSOCKET:-1,
|
||||
postsize?&http->writebytecount:NULL);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
break;
|
||||
|
||||
case HTTPREQ_POST:
|
||||
/* this is the simple POST, using x-www-form-urlencoded style */
|
||||
|
||||
|
|
@ -3171,6 +3201,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|||
case HTTPREQ_PUT:
|
||||
case HTTPREQ_POST:
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
/* We got an error response. If this happened before the whole
|
||||
* request body has been sent we stop sending and mark the
|
||||
* connection for closure after we've read the entire response.
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ CURLcode Curl_http_perhapsrewind(struct connectdata *conn);
|
|||
* HTTP unique setup
|
||||
***************************************************************************/
|
||||
struct HTTP {
|
||||
struct FormData *sendit;
|
||||
struct Curl_mimepart *sendit;
|
||||
curl_off_t postsize; /* off_t to handle large file sizes */
|
||||
const char *postdata;
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ struct HTTP {
|
|||
curl_off_t writebytecount;
|
||||
|
||||
/* For FORM posting */
|
||||
struct Form form;
|
||||
struct Curl_mimepart form;
|
||||
|
||||
struct back {
|
||||
curl_read_callback fread_func; /* backup storage for fread pointer */
|
||||
|
|
|
|||
|
|
@ -1955,6 +1955,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
|
|||
switch(conn->data->set.httpreq) {
|
||||
case HTTPREQ_POST:
|
||||
case HTTPREQ_POST_FORM:
|
||||
case HTTPREQ_POST_MIME:
|
||||
case HTTPREQ_PUT:
|
||||
if(conn->data->state.infilesize != -1)
|
||||
stream->upload_left = conn->data->state.infilesize;
|
||||
|
|
|
|||
48
lib/imap.c
48
lib/imap.c
|
|
@ -68,6 +68,7 @@
|
|||
#include "http.h" /* for HTTP proxy tunnel stuff */
|
||||
#include "socks.h"
|
||||
#include "imap.h"
|
||||
#include "mime.h"
|
||||
#include "strtoofft.h"
|
||||
#include "strcase.h"
|
||||
#include "vtls/vtls.h"
|
||||
|
|
@ -708,18 +709,48 @@ static CURLcode imap_perform_fetch(struct connectdata *conn)
|
|||
static CURLcode imap_perform_append(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct IMAP *imap = conn->data->req.protop;
|
||||
struct Curl_easy *data = conn->data;
|
||||
struct IMAP *imap = data->req.protop;
|
||||
char *mailbox;
|
||||
|
||||
/* Check we have a mailbox */
|
||||
if(!imap->mailbox) {
|
||||
failf(conn->data, "Cannot APPEND without a mailbox.");
|
||||
failf(data, "Cannot APPEND without a mailbox.");
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
|
||||
/* Prepare the mime data if some. */
|
||||
if(data->set.mimepost.kind != MIMEKIND_NONE) {
|
||||
/* Use the whole structure as data. */
|
||||
data->set.mimepost.flags &= ~MIME_BODY_ONLY;
|
||||
|
||||
/* Add external headers and mime version. */
|
||||
curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
|
||||
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
|
||||
NULL, MIMESTRATEGY_MAIL);
|
||||
|
||||
if(!result)
|
||||
if(!Curl_checkheaders(conn, "Mime-Version"))
|
||||
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
|
||||
"Mime-Version: 1.0");
|
||||
|
||||
/* Make sure we will read the entire mime structure. */
|
||||
if(!result)
|
||||
result = Curl_mime_rewind(&data->set.mimepost);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
data->state.infilesize = Curl_mime_size(&data->set.mimepost);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) &data->set.mimepost;
|
||||
}
|
||||
|
||||
/* Check we know the size of the upload */
|
||||
if(conn->data->state.infilesize < 0) {
|
||||
failf(conn->data, "Cannot APPEND with unknown input file size\n");
|
||||
if(data->state.infilesize < 0) {
|
||||
failf(data, "Cannot APPEND with unknown input file size\n");
|
||||
return CURLE_UPLOAD_FAILED;
|
||||
}
|
||||
|
||||
|
|
@ -730,7 +761,7 @@ static CURLcode imap_perform_append(struct connectdata *conn)
|
|||
|
||||
/* Send the APPEND command */
|
||||
result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
|
||||
mailbox, conn->data->state.infilesize);
|
||||
mailbox, data->state.infilesize);
|
||||
|
||||
free(mailbox);
|
||||
|
||||
|
|
@ -1423,9 +1454,10 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
|
|||
result = status; /* use the already set error code */
|
||||
}
|
||||
else if(!data->set.connect_only && !imap->custom &&
|
||||
(imap->uid || data->set.upload)) {
|
||||
(imap->uid || data->set.upload ||
|
||||
data->set.mimepost.kind != MIMEKIND_NONE)) {
|
||||
/* Handle responses after FETCH or APPEND transfer has finished */
|
||||
if(!data->set.upload)
|
||||
if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
|
||||
state(conn, IMAP_FETCH_FINAL);
|
||||
else {
|
||||
/* End the APPEND command first by sending an empty line */
|
||||
|
|
@ -1495,7 +1527,7 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
|
|||
selected = TRUE;
|
||||
|
||||
/* Start the first command in the DO phase */
|
||||
if(conn->data->set.upload)
|
||||
if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
|
||||
/* APPEND can be executed directly */
|
||||
result = imap_perform_append(conn);
|
||||
else if(imap->custom && (selected || !imap->mailbox))
|
||||
|
|
|
|||
1497
lib/mime.c
Normal file
1497
lib/mime.c
Normal file
File diff suppressed because it is too large
Load diff
119
lib/mime.h
Normal file
119
lib/mime.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef HEADER_CURL_MIME_H
|
||||
#define HEADER_CURL_MIME_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */
|
||||
|
||||
/* Part flags. */
|
||||
#define MIME_USERHEADERS_OWNER (1 << 0)
|
||||
#define MIME_BODY_ONLY (1 << 1)
|
||||
|
||||
/* Part source kinds. */
|
||||
enum mimekind {
|
||||
MIMEKIND_NONE = 0, /* Part not set. */
|
||||
MIMEKIND_DATA, /* Allocated mime data. */
|
||||
MIMEKIND_NAMEDFILE, /* Data from named file. */
|
||||
MIMEKIND_FILE, /* Data from file pointer. */
|
||||
MIMEKIND_CALLBACK, /* Data from `read' callback. */
|
||||
MIMEKIND_MULTIPART, /* Data is a mime subpart. */
|
||||
MIMEKIND_LAST
|
||||
};
|
||||
|
||||
/* Readback state tokens. */
|
||||
enum mimestate {
|
||||
MIMESTATE_BEGIN, /* Readback has not yet started. */
|
||||
MIMESTATE_CURLHEADERS, /* In curl-generated headers. */
|
||||
MIMESTATE_USERHEADERS, /* In caller's supplied headers. */
|
||||
MIMESTATE_EOH, /* End of headers. */
|
||||
MIMESTATE_BODY, /* Placeholder. */
|
||||
MIMESTATE_BOUNDARY1, /* In boundary prefix. */
|
||||
MIMESTATE_BOUNDARY2, /* In boundary. */
|
||||
MIMESTATE_CONTENT, /* In content. */
|
||||
MIMESTATE_END, /* End of part reached. */
|
||||
MIMESTATE_LAST
|
||||
};
|
||||
|
||||
/* Mime headers strategies. */
|
||||
enum mimestrategy {
|
||||
MIMESTRATEGY_MAIL, /* Mime mail. */
|
||||
MIMESTRATEGY_FORM, /* HTTP post form. */
|
||||
MIMESTRATEGY_LAST
|
||||
};
|
||||
|
||||
/* Mime readback state. */
|
||||
struct mime_state {
|
||||
enum mimestate state; /* Current state token. */
|
||||
void * ptr; /* State-dependent pointer. */
|
||||
size_t offset; /* State-dependent offset. */
|
||||
};
|
||||
|
||||
/* A mime context. */
|
||||
struct Curl_mime {
|
||||
struct Curl_easy * easy; /* The associated easy handle. */
|
||||
struct Curl_mimepart * parent; /* Parent part. */
|
||||
struct Curl_mimepart * firstpart; /* First part. */
|
||||
struct Curl_mimepart * lastpart; /* Last part. */
|
||||
char * boundary; /* The part boundary. */
|
||||
struct mime_state state; /* Current readback state. */
|
||||
};
|
||||
|
||||
/* A mime part. */
|
||||
struct Curl_mimepart {
|
||||
struct Curl_easy * easy; /* The associated easy handle. */
|
||||
struct Curl_mime * parent; /* Parent mime structure. */
|
||||
struct Curl_mimepart * nextpart; /* Forward linked list. */
|
||||
enum mimekind kind; /* The part kind. */
|
||||
char * data; /* Memory data or file name. */
|
||||
curl_read_callback readfunc; /* Read function. */
|
||||
curl_seek_callback seekfunc; /* Seek function. */
|
||||
curl_free_callback freefunc; /* Argument free function. */
|
||||
void * arg; /* Argument to callback functions. */
|
||||
FILE * namedfp; /* Named file pointer. */
|
||||
struct curl_slist * curlheaders; /* Part headers. */
|
||||
struct curl_slist * userheaders; /* Part headers. */
|
||||
char * mimetype; /* Part mime type. */
|
||||
char * filename; /* Remote file name. */
|
||||
char * name; /* Data name. */
|
||||
size_t namesize; /* Data name size. */
|
||||
curl_off_t origin; /* Origin file offset. */
|
||||
curl_off_t datasize; /* Expected data size. */
|
||||
unsigned int flags; /* Flags. */
|
||||
struct mime_state state; /* Current readback state. */
|
||||
};
|
||||
|
||||
|
||||
/* Prototypes. */
|
||||
void Curl_mime_initpart(struct Curl_mimepart *part, struct Curl_easy *easy);
|
||||
void Curl_mime_cleanpart(struct Curl_mimepart *part);
|
||||
CURLcode Curl_mime_prepare_headers(struct Curl_mimepart *part,
|
||||
const char *contenttype,
|
||||
const char *disposition,
|
||||
enum mimestrategy strategy);
|
||||
curl_off_t Curl_mime_size(struct Curl_mimepart *part);
|
||||
size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
|
||||
void *instream);
|
||||
CURLcode Curl_mime_rewind(struct Curl_mimepart *part);
|
||||
CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
|
||||
CURLcode Curl_mime_file(curl_mimepart *part, FILE *fp, int closewhendone);
|
||||
|
||||
#endif /* HEADER_CURL_MIME_H */
|
||||
|
|
@ -319,29 +319,4 @@ void Curl_convert_close(struct Curl_easy *data)
|
|||
#endif /* HAVE_ICONV */
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_convert_form() is used from http.c, this converts any form items that
|
||||
need to be sent in the network encoding. Returns CURLE_OK on success.
|
||||
*/
|
||||
CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
if(!data)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
while(form) {
|
||||
if(form->type == FORM_DATA) {
|
||||
result = Curl_convert_to_network(data, form->line, form->length);
|
||||
/* Curl_convert_to_network calls failf if unsuccessful */
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
form = form->next;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#endif /* CURL_DOES_CONVERSIONS */
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2011, 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
|
||||
|
|
@ -48,7 +48,6 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
|
|||
char *buffer, size_t length);
|
||||
CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
|
||||
char *buffer, size_t length);
|
||||
CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form);
|
||||
#else
|
||||
#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK)
|
||||
#define Curl_convert_init(x) Curl_nop_stmt
|
||||
|
|
@ -57,7 +56,6 @@ CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form);
|
|||
#define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK)
|
||||
#define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK)
|
||||
#define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK)
|
||||
#define Curl_convert_form(a,b) CURLE_OK
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_NON_ASCII_H */
|
||||
|
|
|
|||
37
lib/smtp.c
37
lib/smtp.c
|
|
@ -67,6 +67,7 @@
|
|||
#include "transfer.h"
|
||||
#include "escape.h"
|
||||
#include "http.h" /* for HTTP proxy tunnel stuff */
|
||||
#include "mime.h"
|
||||
#include "socks.h"
|
||||
#include "smtp.h"
|
||||
#include "strtoofft.h"
|
||||
|
|
@ -530,8 +531,37 @@ static CURLcode smtp_perform_mail(struct connectdata *conn)
|
|||
}
|
||||
}
|
||||
|
||||
/* Prepare the mime data if some. */
|
||||
if(data->set.mimepost.kind != MIMEKIND_NONE) {
|
||||
/* Use the whole structure as data. */
|
||||
data->set.mimepost.flags &= ~MIME_BODY_ONLY;
|
||||
|
||||
/* Add external headers and mime version. */
|
||||
curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
|
||||
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
|
||||
NULL, MIMESTRATEGY_MAIL);
|
||||
|
||||
if(!result)
|
||||
if(!Curl_checkheaders(conn, "Mime-Version"))
|
||||
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
|
||||
"Mime-Version: 1.0");
|
||||
|
||||
/* Make sure we will read the entire mime structure. */
|
||||
if(!result)
|
||||
result = Curl_mime_rewind(&data->set.mimepost);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
data->state.infilesize = Curl_mime_size(&data->set.mimepost);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) &data->set.mimepost;
|
||||
}
|
||||
|
||||
/* Calculate the optional SIZE parameter */
|
||||
if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
|
||||
if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
|
||||
size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
|
||||
|
||||
if(!size) {
|
||||
|
|
@ -1159,7 +1189,8 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
|
|||
connclose(conn, "SMTP done with bad status"); /* marked for closure */
|
||||
result = status; /* use the already set error code */
|
||||
}
|
||||
else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
|
||||
else if(!data->set.connect_only && data->set.mail_rcpt &&
|
||||
(data->set.upload || data->set.mimepost.kind)) {
|
||||
/* Calculate the EOB taking into account any terminating CRLF from the
|
||||
previous line of the email or the CRLF of the DATA command when there
|
||||
is "no mail data". RFC-5321, sect. 4.1.1.4.
|
||||
|
|
@ -1249,7 +1280,7 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
|
|||
smtp->rcpt = data->set.mail_rcpt;
|
||||
|
||||
/* Start the first command in the DO phase */
|
||||
if(data->set.upload && data->set.mail_rcpt)
|
||||
if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
|
||||
/* MAIL transfer */
|
||||
result = smtp_perform_mail(conn);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
#include "connect.h"
|
||||
#include "non-ascii.h"
|
||||
#include "http2.h"
|
||||
#include "mime.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
|
|
@ -241,6 +242,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
|
|||
CURLcode Curl_readrewind(struct connectdata *conn)
|
||||
{
|
||||
struct Curl_easy *data = conn->data;
|
||||
struct Curl_mimepart *mimepart = &data->set.mimepost;
|
||||
|
||||
conn->bits.rewindaftersend = FALSE; /* we rewind now */
|
||||
|
||||
|
|
@ -253,9 +255,21 @@ CURLcode Curl_readrewind(struct connectdata *conn)
|
|||
/* We have sent away data. If not using CURLOPT_POSTFIELDS or
|
||||
CURLOPT_HTTPPOST, call app to rewind
|
||||
*/
|
||||
if(data->set.postfields ||
|
||||
(data->set.httpreq == HTTPREQ_POST_FORM))
|
||||
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
|
||||
struct HTTP *http = data->req.protop;
|
||||
|
||||
if(http->sendit)
|
||||
mimepart = http->sendit;
|
||||
}
|
||||
if(data->set.postfields)
|
||||
; /* do nothing */
|
||||
else if(data->set.httpreq == HTTPREQ_POST_MIME ||
|
||||
data->set.httpreq == HTTPREQ_POST_FORM) {
|
||||
if(Curl_mime_rewind(mimepart)) {
|
||||
failf(data, "Cannot rewind mime/post data");
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data->set.seek_func) {
|
||||
int err;
|
||||
|
|
@ -1826,7 +1840,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
|
|||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->set.httpreq == HTTPREQ_POST
|
||||
|| data->set.httpreq == HTTPREQ_POST_FORM)
|
||||
|| data->set.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->set.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_301)) {
|
||||
infof(data, "Switch from POST to GET\n");
|
||||
data->set.httpreq = HTTPREQ_GET;
|
||||
|
|
@ -1850,7 +1865,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
|
|||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->set.httpreq == HTTPREQ_POST
|
||||
|| data->set.httpreq == HTTPREQ_POST_FORM)
|
||||
|| data->set.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->set.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_302)) {
|
||||
infof(data, "Switch from POST to GET\n");
|
||||
data->set.httpreq = HTTPREQ_GET;
|
||||
|
|
|
|||
22
lib/url.c
22
lib/url.c
|
|
@ -71,6 +71,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
|
|||
#include "netrc.h"
|
||||
|
||||
#include "formdata.h"
|
||||
#include "mime.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "hostip.h"
|
||||
#include "transfer.h"
|
||||
|
|
@ -479,6 +480,8 @@ CURLcode Curl_close(struct Curl_easy *data)
|
|||
Curl_http2_cleanup_dependencies(data);
|
||||
Curl_convert_close(data);
|
||||
|
||||
Curl_mime_cleanpart(&data->set.mimepost);
|
||||
|
||||
/* No longer a dirty share, if it exists */
|
||||
if(data->share) {
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
|
||||
|
|
@ -668,6 +671,8 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
|||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
Curl_mime_initpart(&data->set.mimepost, data);
|
||||
|
||||
data->state.headerbuff = malloc(HEADERSIZE);
|
||||
if(!data->state.headerbuff) {
|
||||
DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
|
||||
|
|
@ -1148,6 +1153,19 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
|
|||
data->set.opt_no_body = FALSE; /* this is implied */
|
||||
break;
|
||||
|
||||
case CURLOPT_MIMEPOST:
|
||||
/*
|
||||
* Set to make us do MIME/form POST
|
||||
*/
|
||||
result = curl_mime_subparts(&data->set.mimepost,
|
||||
va_arg(param, curl_mime *));
|
||||
if(!result) {
|
||||
data->set.mimepost.freefunc = NULL; /* Avoid free upon easy cleanup. */
|
||||
data->set.httpreq = HTTPREQ_POST_MIME;
|
||||
data->set.opt_no_body = FALSE; /* this is implied */
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLOPT_REFERER:
|
||||
/*
|
||||
* String to set in the HTTP Referer: field.
|
||||
|
|
@ -1875,13 +1893,13 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
|
|||
break;
|
||||
case CURLOPT_RESUME_FROM:
|
||||
/*
|
||||
* Resume transfer at the give file position
|
||||
* Resume transfer at the given file position
|
||||
*/
|
||||
data->set.set_resume_from = va_arg(param, long);
|
||||
break;
|
||||
case CURLOPT_RESUME_FROM_LARGE:
|
||||
/*
|
||||
* Resume transfer at the give file position
|
||||
* Resume transfer at the given file position
|
||||
*/
|
||||
data->set.set_resume_from = va_arg(param, curl_off_t);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@
|
|||
#include "hash.h"
|
||||
#include "splay.h"
|
||||
|
||||
#include "mime.h"
|
||||
#include "imap.h"
|
||||
#include "pop3.h"
|
||||
#include "smtp.h"
|
||||
|
|
@ -1140,6 +1141,7 @@ typedef enum {
|
|||
HTTPREQ_GET,
|
||||
HTTPREQ_POST,
|
||||
HTTPREQ_POST_FORM, /* we make a difference internally */
|
||||
HTTPREQ_POST_MIME, /* we make a difference internally */
|
||||
HTTPREQ_PUT,
|
||||
HTTPREQ_HEAD,
|
||||
HTTPREQ_OPTIONS,
|
||||
|
|
@ -1557,7 +1559,8 @@ struct UserDefined {
|
|||
curl_off_t set_resume_from; /* continue [ftp] transfer from here */
|
||||
struct curl_slist *headers; /* linked list of extra headers */
|
||||
struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
|
||||
struct curl_httppost *httppost; /* linked list of POST data */
|
||||
struct curl_httppost *httppost; /* linked list of old POST data */
|
||||
struct Curl_mimepart mimepost; /* MIME/POST data. */
|
||||
bool sep_headers; /* handle host and proxy headers separately */
|
||||
bool cookiesession; /* new cookie session? */
|
||||
bool crlf; /* convert crlf on ftp upload(?) */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue