Newer
Older
Daniel Stenberg
committed
return result; /* no input => no output! */
boundary = Curl_FormBoundary();
if(!boundary)
return CURLE_OUT_OF_MEMORY;
result = AddFormDataf(&form, NULL,
"Content-Type: multipart/form-data;"
" boundary=%s\r\n",
boundary);
if (result) {
free(boundary);
return result;
}
/* we DO NOT include that line in the total size of the POST, since it'll be
part of the header! */
if(size) {
result = AddFormDataf(&form, &size, "\r\n");
break;
}
result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
break;
Daniel Stenberg
committed
result = AddFormDataf(&form, &size,
"Content-Disposition: form-data; name=\"");
break;
Daniel Stenberg
committed
result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
&size);
break;
Daniel Stenberg
committed
result = AddFormDataf(&form, &size, "\"");
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 */
fileboundary = Curl_FormBoundary();
result = AddFormDataf(&form, &size,
"\r\nContent-Type: multipart/mixed,"
" boundary=%s\r\n",
fileboundary);
if (result)
break;
/* 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. */
Daniel Stenberg
committed
/* if multiple-file */
Daniel Stenberg
committed
char *filebasename=
(!file->showfilename)?strippath(file->contents):NULL;
result = AddFormDataf(&form, &size,
"\r\n--%s\r\nContent-Disposition: "
"attachment; filename=\"%s\"",
fileboundary,
(file->showfilename?file->showfilename:
Daniel Stenberg
committed
filebasename));
if (filebasename)
free(filebasename);
break;
Daniel Stenberg
committed
else if((post->flags & HTTPPOST_FILENAME) ||
(post->flags & HTTPPOST_BUFFER)) {
Daniel Stenberg
committed
char *filebasename=
(!post->showfilename)?strippath(post->contents):NULL;
result = AddFormDataf(&form, &size,
"; filename=\"%s\"",
(post->showfilename?post->showfilename:
Daniel Stenberg
committed
filebasename));
if (filebasename)
free(filebasename);
break;
Daniel Stenberg
committed
/* 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 );
break;
curList = curList->next;
}
Curl_formclean(firstform);
free(boundary);
return result;
}
#if 0
/* The header Content-Transfer-Encoding: seems to confuse some receivers
* (like the built-in PHP engine). While I can't see any reason why it
* should, I can just as well skip this to the benefit of the users who
* are using such confused receivers.
*/
!checkprefix("text/", file->contenttype)) {
Daniel Stenberg
committed
/* this is not a text content, mention our binary encoding */
size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
Daniel Stenberg
committed
result = AddFormDataf(&form, &size, "\r\n\r\n");
break;
if((post->flags & HTTPPOST_FILENAME) ||
Daniel Stenberg
committed
(post->flags & HTTPPOST_READFILE)) {
/* we should include the contents from the specified file */
FILE *fileread;
fileread = strequal("-", file->contents)?
stdin:fopen(file->contents, "rb"); /* binary read for win32 */
Daniel Stenberg
committed
/*
* 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
*/
Daniel Stenberg
committed
if(fileread) {
Daniel Stenberg
committed
if(fileread != stdin) {
/* close the file again */
fclose(fileread);
Daniel Stenberg
committed
/* 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))) {
result = AddFormData(&form, FORM_DATA, buffer, nread, &size);
if (result)
break;
}
}
Curl_formclean(firstform);
free(boundary);
return result;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
Curl_formclean(firstform);
free(boundary);
*finalform = NULL;
return CURLE_READ_ERROR;
Daniel Stenberg
committed
}
else if (post->flags & HTTPPOST_BUFFER) {
/* include contents of buffer */
Daniel Stenberg
committed
result = AddFormData(&form, FORM_DATA, post->buffer,
post->bufferlength, &size);
break;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
/* include the contents we got */
Daniel Stenberg
committed
result = AddFormData(&form, FORM_DATA, post->contents,
post->contentslength, &size);
break;
}
} while((file = file->more)); /* for each specified file for this field */
Curl_formclean(firstform);
free(boundary);
return result;
}
if(post->more) {
/* this was a multiple-file inclusion, make a termination file
boundary: */
result = AddFormDataf(&form, &size,
Daniel Stenberg
committed
"\r\n--%s--",
break;
}
} while((post=post->next)); /* for each field */
Curl_formclean(firstform);
free(boundary);
return result;
}
result = AddFormDataf(&form, &size,
Daniel Stenberg
committed
"\r\n--%s--\r\n",
boundary);
Curl_formclean(firstform);
free(boundary);
return result;
}
Daniel Stenberg
committed
*finalform=firstform;
return result;
Daniel Stenberg
committed
/*
* 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 )
form->data = formdata;
form->sent = 0;
Daniel Stenberg
committed
form->fp = NULL;
Daniel Stenberg
committed
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
static size_t readfromfile(struct Form *form, char *buffer, size_t size)
{
size_t nread;
if(!form->fp) {
/* this file hasn't yet been opened */
form->fp = fopen(form->data->line, "rb"); /* b is for binary */
if(!form->fp)
return -1; /* failure */
}
nread = fread(buffer, 1, size, form->fp);
if(nread != size) {
/* this is the last chunk form the file, move on */
fclose(form->fp);
form->fp = NULL;
form->data = form->data->next;
}
return nread;
}
Daniel Stenberg
committed
/*
* 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)
Daniel Stenberg
committed
return 0; /* nothing, error, empty */
if(form->data->type == FORM_FILE) {
gotsize = readfromfile(form, buffer, wantedsize);
Daniel Stenberg
committed
if(gotsize)
/* If positive or -1, return. If zero, continue! */
return gotsize;
}
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 */
Daniel Stenberg
committed
} while(form->data && (form->data->type == FORM_DATA));
/* 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.
This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
return gotsize;
}
Daniel Stenberg
committed
/*
* 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)
Daniel Stenberg
committed
char *header;
struct Form *form=(struct Form *)formp;
Daniel Stenberg
committed
return 0; /* nothing, ERROR! */
Daniel Stenberg
committed
header = form->data->line;
*len = form->data->length;
Daniel Stenberg
committed
form->data = form->data->next; /* advance */
Daniel Stenberg
committed
return header;
int FormAddTest(const char * errormsg,
struct curl_httppost **httppost,
struct curl_httppost **last_post,
...)
{
int result;
va_list arg;
va_start(arg, last_post);
if ((result = FormAdd(httppost, last_post, arg)))
fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
errormsg);
va_end(arg);
return result;
}
int main()
{
char name1[] = "simple_COPYCONTENTS";
char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
char name4[] = "simple_PTRCONTENTS";
char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
char name7[] = "FILE1_+_CONTENTTYPE";
char name8[] = "FILE1_+_FILE2";
char name9[] = "FILE1_+_FILE2_+_FILE3";
char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
char name11[] = "FILECONTENT";
char value1[] = "value for simple COPYCONTENTS";
char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
char value4[] = "value for simple PTRCONTENTS";
char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
char value7[] = "inet_ntoa_r.h";
char value8[] = "Makefile.b32.resp";
char type2[] = "image/gif";
char type6[] = "text/plain";
char type7[] = "text/html";
int name3length = strlen(name3);
int value3length = strlen(value3);
int value5length = strlen(value4);
int value6length = strlen(value5);
int errors = 0;
int size;
char buffer[4096];
struct curl_httppost *httppost=NULL;
struct curl_httppost *last_post=NULL;
struct FormData *form;
struct Form formread;
if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
CURLFORM_END))
++errors;
if (FormAddTest("COPYCONTENTS + CONTENTTYPE test", &httppost, &last_post,
CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
++errors;
/* make null character at start to check that contentslength works
correctly */
name3[1] = '\0';
value3[1] = '\0';
if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
&httppost, &last_post,
CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
CURLFORM_CONTENTSLENGTH, value3length,
CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
++errors;
if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
CURLFORM_END))
++errors;
/* make null character at start to check that contentslength works
correctly */
value5[1] = '\0';
if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
++errors;
/* make null character at start to check that contentslength works
correctly */
value6[1] = '\0';
if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
&httppost, &last_post,
CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
CURLFORM_CONTENTSLENGTH, value6length,
CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
++errors;
if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
++errors;
if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
CURLFORM_FILE, value8, CURLFORM_END))
++errors;
if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
forms[0].option = CURLFORM_FILE;
forms[0].value = value7;
forms[1].option = CURLFORM_FILE;
forms[1].value = value8;
forms[2].option = CURLFORM_FILE;
forms[2].value = value7;
forms[3].option = CURLFORM_END;
if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
CURLFORM_END))
++errors;
if (FormAddTest("FILECONTENT test", &httppost, &last_post,
CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
CURLFORM_END))
++errors;
form=Curl_getFormData(httppost, &size);
Curl_FormInit(&formread, form);
do {
nread = Curl_FormReader(buffer, 1, sizeof(buffer),
(FILE *)&formread);
if(-1 == nread)
break;
fwrite(buffer, nread, 1, stdout);
} while(1);
fprintf(stdout, "size: %d\n", size);
if (errors)
fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
else
fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
return 0;
}
#endif
Daniel Stenberg
committed
#else /* CURL_DISABLE_HTTP */
CURLFORMcode curl_formadd(struct curl_httppost **httppost,
struct curl_httppost **last_post,
...)
{
(void)httppost;
(void)last_post;
return CURL_FORMADD_DISABLED;
}
void curl_formfree(struct curl_httppost *form)
{
(void)form;
/* does nothing HTTP is disabled */
}
Daniel Stenberg
committed
#endif /* CURL_DISABLE_HTTP */
Daniel Stenberg
committed
/*
* Curl_FormBoundary() creates a suitable boundary string and returns an
* allocated one. This is also used by SSL-code so it must be present even
* if HTTP is disabled!
*/
char *Curl_FormBoundary(void)
{
char *retstring;
static int randomizer=0; /* this is just so that two boundaries within
the same form won't be identical */
Daniel Stenberg
committed
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
size_t i;
static char table16[]="abcdef0123456789";
retstring = (char *)malloc(BOUNDARY_LENGTH+1);
if(!retstring)
return NULL; /* failed */
srand(time(NULL)+randomizer++); /* seed */
strcpy(retstring, "----------------------------");
for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
retstring[i] = table16[rand()%16];
/* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
combinations */
retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
return retstring;
}