Newer
Older
struct FormData *newform = (struct FormData *)
malloc(sizeof(struct FormData));
newform->next = NULL;
/* we make it easier for plain strings: */
if(!length)
length = strlen((char *)line);
newform->line = (char *)malloc(length+1);
memcpy(newform->line, line, length);
newform->line[length]=0; /* zero terminate for easier debugging */
if(*formp) {
(*formp)->next = newform;
*formp = newform;
}
else
*formp = newform;
return length;
}
Daniel Stenberg
committed
/*
* AddFormDataf() adds printf()-style formatted data to the formdata chain.
*/
Daniel Stenberg
committed
static size_t AddFormDataf(struct FormData **formp,
Daniel Stenberg
committed
const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
vsprintf(s, fmt, ap);
va_end(ap);
return AddFormData(formp, s, 0);
}
Daniel Stenberg
committed
/*
* Curl_FormBoundary() creates a suitable boundary string and returns an
* allocated one.
*/
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 */
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 */
Daniel Stenberg
committed
/*
* Curl_formclean() is used from http.c, this cleans a built FormData linked
* list
*/
void Curl_formclean(struct FormData *form)
do {
next=form->next; /* the following form line */
free(form->line); /* free the line */
free(form); /* free the struct */
} while((form=next)); /* continue */
}
Daniel Stenberg
committed
/*
* curl_formfree() is an external function to free up a whole form post
* chain
*/
void curl_formfree(struct curl_httppost *form)
struct curl_httppost *next;
if(!form)
/* no form to free, just get out of this */
return;
do {
next=form->next; /* the following form line */
/* recurse to sub-contents */
if(form->more)
curl_formfree(form->more);
if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
free(form->contents); /* free the contents */
if(form->contenttype)
free(form->contenttype); /* free the content type */
if(form->showfilename)
free(form->showfilename); /* free the faked file name */
} while((form=next)); /* continue */
Daniel Stenberg
committed
/*
* 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.
*/
Daniel Stenberg
committed
CURLcode Curl_getFormData(struct FormData **finalform,
struct curl_httppost *post,
{
struct FormData *form = NULL;
struct FormData *firstform;
struct curl_httppost *file;
Daniel Stenberg
committed
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
size_t size =0;
struct curl_slist* curList;
Daniel Stenberg
committed
*finalform=NULL; /* default form is empty */
Daniel Stenberg
committed
return result; /* no input => no output! */
boundary = Curl_FormBoundary();
/* Make the first line of the output */
AddFormDataf(&form,
"Content-Type: multipart/form-data;"
" boundary=%s\r\n",
boundary);
/* we DO NOT count that line since that'll be part of the header! */
firstform = form;
do {
if(size)
size += AddFormDataf(&form, "\r\n");
size += AddFormDataf(&form, "--%s\r\n", boundary);
size += AddFormData(&form,
"Content-Disposition: form-data; name=\"", 0);
size += AddFormData(&form, post->name, post->namelength);
size += AddFormData(&form, "\"", 0);
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();
Daniel Stenberg
committed
"\r\nContent-Type: multipart/mixed,"
" boundary=%s\r\n",
fileboundary);
/* 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 */
size += AddFormDataf(&form,
"\r\n--%s\r\nContent-Disposition: "
"attachment; filename=\"%s\"",
Daniel Stenberg
committed
fileboundary,
(file->showfilename?file->showfilename:
file->contents));
Daniel Stenberg
committed
else if((post->flags & HTTPPOST_FILENAME) ||
(post->flags & HTTPPOST_BUFFER)) {
size += AddFormDataf(&form,
"; filename=\"%s\"",
(post->showfilename?post->showfilename:
post->contents));
Daniel Stenberg
committed
/* we have a specified type */
size += AddFormDataf(&form,
"\r\nContent-Type: %s",
file->contenttype);
curList = file->contentheader;
while( curList ) {
/* Process the additional headers specified for this form */
size += AddFormDataf( &form, "\r\n%s", curList->data );
curList = curList->next;
}
#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);
size += AddFormData(&form, "\r\n\r\n", 0);
if((post->flags & HTTPPOST_FILENAME) ||
Daniel Stenberg
committed
(post->flags & HTTPPOST_READFILE)) {
/* we should include the contents from the specified file */
FILE *fileread;
char buffer[1024];
fileread = strequal("-", 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
*/
Daniel Stenberg
committed
if(fileread) {
while((nread = fread(buffer, 1, 1024, fileread)))
size += AddFormData(&form, buffer, nread);
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) {
Daniel Stenberg
committed
/* include contents of buffer */
size += AddFormData(&form, post->buffer, post->bufferlength);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
/* include the contents we got */
size += AddFormData(&form, post->contents, post->contentslength);
}
} while((file = file->more)); /* for each specified file for this field */
if(post->more) {
/* this was a multiple-file inclusion, make a termination file
boundary: */
size += AddFormDataf(&form,
Daniel Stenberg
committed
"\r\n--%s--",
fileboundary);
free(fileboundary);
}
} while((post=post->next)); /* for each field */
/* end-boundary for everything */
size += AddFormDataf(&form,
Daniel Stenberg
committed
"\r\n--%s--\r\n",
boundary);
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
/*
* 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 */
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
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);
/* 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
#ifdef _OLD_FORM_DEBUG
int main(int argc, char **argv)
{
#if 0
char *testargs[]={
"name1 = data in number one",
"name2 = number two data",
"test = @upload"
};
#endif
int i;
char *nextarg;
struct curl_httppost *httppost=NULL;
struct curl_httppost *last_post=NULL;
struct curl_httppost *post;
int size;
int nread;
char buffer[4096];
struct FormData *form;
struct Form formread;
for(i=1; i<argc; i++) {
if( FormParse( argv[i],
&httppost,
&last_post)) {
fprintf(stderr, "Illegally formatted input field: '%s'!\n",
argv[i]);
return 1;
}
}
form=Curl_getFormData(httppost, &size);
Curl_FormInit(&formread, form);
do {
nread = Curl_FormReader(buffer, 1, sizeof(buffer),
(FILE *)&formread);
if(-1 == nread)
break;
} while(1);
fprintf(stderr, "size: %d\n", size);
return 0;
}
#endif
#endif /* CURL_DISABLE_HTTP */