1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #include <curl/curl.h>
26
27 #ifndef CURL_DISABLE_HTTP
28
29 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
30 #include <libgen.h>
31 #endif
32
33 #include "urldata.h" /* for struct SessionHandle */
34 #include "formdata.h"
35 #include "vtls/vtls.h"
36 #include "strequal.h"
37 #include "sendf.h"
38 #include "strdup.h"
39 #include "curl_printf.h"
40
41 /* The last #include files should be: */
42 #include "curl_memory.h"
43 #include "memdebug.h"
44
45 #ifndef HAVE_BASENAME
46 static char *Curl_basename(char *path);
47 #define basename(x) Curl_basename((x))
48 #endif
49
50 static size_t readfromfile(struct Form *form, char *buffer, size_t size);
51 static char *formboundary(struct SessionHandle *data);
52
53 /* What kind of Content-Type to use on un-specified files with unrecognized
54 extensions. */
55 #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
56
57 #define FORM_FILE_SEPARATOR ','
58 #define FORM_TYPE_SEPARATOR ';'
59
60 /***************************************************************************
61 *
62 * AddHttpPost()
63 *
64 * Adds a HttpPost structure to the list, if parent_post is given becomes
65 * a subpost of parent_post instead of a direct list element.
66 *
67 * Returns newly allocated HttpPost on success and NULL if malloc failed.
68 *
69 ***************************************************************************/
70 static struct curl_httppost *
AddHttpPost(char * name,size_t namelength,char * value,size_t contentslength,char * buffer,size_t bufferlength,char * contenttype,long flags,struct curl_slist * contentHeader,char * showfilename,char * userp,struct curl_httppost * parent_post,struct curl_httppost ** httppost,struct curl_httppost ** last_post)71 AddHttpPost(char *name, size_t namelength,
72 char *value, size_t contentslength,
73 char *buffer, size_t bufferlength,
74 char *contenttype,
75 long flags,
76 struct curl_slist* contentHeader,
77 char *showfilename, char *userp,
78 struct curl_httppost *parent_post,
79 struct curl_httppost **httppost,
80 struct curl_httppost **last_post)
81 {
82 struct curl_httppost *post;
83 post = calloc(1, sizeof(struct curl_httppost));
84 if(post) {
85 post->name = name;
86 post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
87 post->contents = value;
88 post->contentslength = (long)contentslength;
89 post->buffer = buffer;
90 post->bufferlength = (long)bufferlength;
91 post->contenttype = contenttype;
92 post->contentheader = contentHeader;
93 post->showfilename = showfilename;
94 post->userp = userp,
95 post->flags = flags;
96 }
97 else
98 return NULL;
99
100 if(parent_post) {
101 /* now, point our 'more' to the original 'more' */
102 post->more = parent_post->more;
103
104 /* then move the original 'more' to point to ourselves */
105 parent_post->more = post;
106 }
107 else {
108 /* make the previous point to this */
109 if(*last_post)
110 (*last_post)->next = post;
111 else
112 (*httppost) = post;
113
114 (*last_post) = post;
115 }
116 return post;
117 }
118
119 /***************************************************************************
120 *
121 * AddFormInfo()
122 *
123 * Adds a FormInfo structure to the list presented by parent_form_info.
124 *
125 * Returns newly allocated FormInfo on success and NULL if malloc failed/
126 * parent_form_info is NULL.
127 *
128 ***************************************************************************/
AddFormInfo(char * value,char * contenttype,FormInfo * parent_form_info)129 static FormInfo * AddFormInfo(char *value,
130 char *contenttype,
131 FormInfo *parent_form_info)
132 {
133 FormInfo *form_info;
134 form_info = calloc(1, sizeof(struct FormInfo));
135 if(form_info) {
136 if(value)
137 form_info->value = value;
138 if(contenttype)
139 form_info->contenttype = contenttype;
140 form_info->flags = HTTPPOST_FILENAME;
141 }
142 else
143 return NULL;
144
145 if(parent_form_info) {
146 /* now, point our 'more' to the original 'more' */
147 form_info->more = parent_form_info->more;
148
149 /* then move the original 'more' to point to ourselves */
150 parent_form_info->more = form_info;
151 }
152
153 return form_info;
154 }
155
156 /***************************************************************************
157 *
158 * ContentTypeForFilename()
159 *
160 * Provides content type for filename if one of the known types (else
161 * (either the prevtype or the default is returned).
162 *
163 * Returns some valid contenttype for filename.
164 *
165 ***************************************************************************/
ContentTypeForFilename(const char * filename,const char * prevtype)166 static const char *ContentTypeForFilename(const char *filename,
167 const char *prevtype)
168 {
169 const char *contenttype = NULL;
170 unsigned int i;
171 /*
172 * No type was specified, we scan through a few well-known
173 * extensions and pick the first we match!
174 */
175 struct ContentType {
176 const char *extension;
177 const char *type;
178 };
179 static const struct ContentType ctts[]={
180 {".gif", "image/gif"},
181 {".jpg", "image/jpeg"},
182 {".jpeg", "image/jpeg"},
183 {".txt", "text/plain"},
184 {".html", "text/html"},
185 {".xml", "application/xml"}
186 };
187
188 if(prevtype)
189 /* default to the previously set/used! */
190 contenttype = prevtype;
191 else
192 contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
193
194 if(filename) { /* in case a NULL was passed in */
195 for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
196 if(strlen(filename) >= strlen(ctts[i].extension)) {
197 if(strequal(filename +
198 strlen(filename) - strlen(ctts[i].extension),
199 ctts[i].extension)) {
200 contenttype = ctts[i].type;
201 break;
202 }
203 }
204 }
205 }
206 /* we have a contenttype by now */
207 return contenttype;
208 }
209
210 /***************************************************************************
211 *
212 * FormAdd()
213 *
214 * Stores a formpost parameter and builds the appropriate linked list.
215 *
216 * Has two principal functionalities: using files and byte arrays as
217 * post parts. Byte arrays are either copied or just the pointer is stored
218 * (as the user requests) while for files only the filename and not the
219 * content is stored.
220 *
221 * While you may have only one byte array for each name, multiple filenames
222 * are allowed (and because of this feature CURLFORM_END is needed after
223 * using CURLFORM_FILE).
224 *
225 * Examples:
226 *
227 * Simple name/value pair with copied contents:
228 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
229 * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
230 *
231 * name/value pair where only the content pointer is remembered:
232 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
233 * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
234 * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
235 *
236 * storing a filename (CONTENTTYPE is optional!):
237 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
238 * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
239 * CURLFORM_END);
240 *
241 * storing multiple filenames:
242 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
243 * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
244 *
245 * Returns:
246 * CURL_FORMADD_OK on success
247 * CURL_FORMADD_MEMORY if the FormInfo allocation fails
248 * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form
249 * CURL_FORMADD_NULL if a null pointer was given for a char
250 * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed
251 * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
252 * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error)
253 * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated
254 * CURL_FORMADD_MEMORY if some allocation for string copying failed.
255 * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array
256 *
257 ***************************************************************************/
258
259 static
FormAdd(struct curl_httppost ** httppost,struct curl_httppost ** last_post,va_list params)260 CURLFORMcode FormAdd(struct curl_httppost **httppost,
261 struct curl_httppost **last_post,
262 va_list params)
263 {
264 FormInfo *first_form, *current_form, *form = NULL;
265 CURLFORMcode return_value = CURL_FORMADD_OK;
266 const char *prevtype = NULL;
267 struct curl_httppost *post = NULL;
268 CURLformoption option;
269 struct curl_forms *forms = NULL;
270 char *array_value=NULL; /* value read from an array */
271
272 /* This is a state variable, that if TRUE means that we're parsing an
273 array that we got passed to us. If FALSE we're parsing the input
274 va_list arguments. */
275 bool array_state = FALSE;
276
277 /*
278 * We need to allocate the first struct to fill in.
279 */
280 first_form = calloc(1, sizeof(struct FormInfo));
281 if(!first_form)
282 return CURL_FORMADD_MEMORY;
283
284 current_form = first_form;
285
286 /*
287 * Loop through all the options set. Break if we have an error to report.
288 */
289 while(return_value == CURL_FORMADD_OK) {
290
291 /* first see if we have more parts of the array param */
292 if(array_state && forms) {
293 /* get the upcoming option from the given array */
294 option = forms->option;
295 array_value = (char *)forms->value;
296
297 forms++; /* advance this to next entry */
298 if(CURLFORM_END == option) {
299 /* end of array state */
300 array_state = FALSE;
301 continue;
302 }
303 }
304 else {
305 /* This is not array-state, get next option */
306 option = va_arg(params, CURLformoption);
307 if(CURLFORM_END == option)
308 break;
309 }
310
311 switch (option) {
312 case CURLFORM_ARRAY:
313 if(array_state)
314 /* we don't support an array from within an array */
315 return_value = CURL_FORMADD_ILLEGAL_ARRAY;
316 else {
317 forms = va_arg(params, struct curl_forms *);
318 if(forms)
319 array_state = TRUE;
320 else
321 return_value = CURL_FORMADD_NULL;
322 }
323 break;
324
325 /*
326 * Set the Name property.
327 */
328 case CURLFORM_PTRNAME:
329 #ifdef CURL_DOES_CONVERSIONS
330 /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy
331 * the data in all cases so that we'll have safe memory for the eventual
332 * conversion.
333 */
334 #else
335 current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
336 #endif
337 case CURLFORM_COPYNAME:
338 if(current_form->name)
339 return_value = CURL_FORMADD_OPTION_TWICE;
340 else {
341 char *name = array_state?
342 array_value:va_arg(params, char *);
343 if(name)
344 current_form->name = name; /* store for the moment */
345 else
346 return_value = CURL_FORMADD_NULL;
347 }
348 break;
349 case CURLFORM_NAMELENGTH:
350 if(current_form->namelength)
351 return_value = CURL_FORMADD_OPTION_TWICE;
352 else
353 current_form->namelength =
354 array_state?(size_t)array_value:(size_t)va_arg(params, long);
355 break;
356
357 /*
358 * Set the contents property.
359 */
360 case CURLFORM_PTRCONTENTS:
361 current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
362 case CURLFORM_COPYCONTENTS:
363 if(current_form->value)
364 return_value = CURL_FORMADD_OPTION_TWICE;
365 else {
366 char *value =
367 array_state?array_value:va_arg(params, char *);
368 if(value)
369 current_form->value = value; /* store for the moment */
370 else
371 return_value = CURL_FORMADD_NULL;
372 }
373 break;
374 case CURLFORM_CONTENTSLENGTH:
375 if(current_form->contentslength)
376 return_value = CURL_FORMADD_OPTION_TWICE;
377 else
378 current_form->contentslength =
379 array_state?(size_t)array_value:(size_t)va_arg(params, long);
380 break;
381
382 /* Get contents from a given file name */
383 case CURLFORM_FILECONTENT:
384 if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
385 return_value = CURL_FORMADD_OPTION_TWICE;
386 else {
387 const char *filename = array_state?
388 array_value:va_arg(params, char *);
389 if(filename) {
390 current_form->value = strdup(filename);
391 if(!current_form->value)
392 return_value = CURL_FORMADD_MEMORY;
393 else {
394 current_form->flags |= HTTPPOST_READFILE;
395 current_form->value_alloc = TRUE;
396 }
397 }
398 else
399 return_value = CURL_FORMADD_NULL;
400 }
401 break;
402
403 /* We upload a file */
404 case CURLFORM_FILE:
405 {
406 const char *filename = array_state?array_value:
407 va_arg(params, char *);
408
409 if(current_form->value) {
410 if(current_form->flags & HTTPPOST_FILENAME) {
411 if(filename) {
412 char *fname = strdup(filename);
413 if(!fname)
414 return_value = CURL_FORMADD_MEMORY;
415 else {
416 form = AddFormInfo(fname, NULL, current_form);
417 if(!form) {
418 free(fname);
419 return_value = CURL_FORMADD_MEMORY;
420 }
421 else {
422 form->value_alloc = TRUE;
423 current_form = form;
424 form = NULL;
425 }
426 }
427 }
428 else
429 return_value = CURL_FORMADD_NULL;
430 }
431 else
432 return_value = CURL_FORMADD_OPTION_TWICE;
433 }
434 else {
435 if(filename) {
436 current_form->value = strdup(filename);
437 if(!current_form->value)
438 return_value = CURL_FORMADD_MEMORY;
439 else {
440 current_form->flags |= HTTPPOST_FILENAME;
441 current_form->value_alloc = TRUE;
442 }
443 }
444 else
445 return_value = CURL_FORMADD_NULL;
446 }
447 break;
448 }
449
450 case CURLFORM_BUFFERPTR:
451 current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
452 if(current_form->buffer)
453 return_value = CURL_FORMADD_OPTION_TWICE;
454 else {
455 char *buffer =
456 array_state?array_value:va_arg(params, char *);
457 if(buffer) {
458 current_form->buffer = buffer; /* store for the moment */
459 current_form->value = buffer; /* make it non-NULL to be accepted
460 as fine */
461 }
462 else
463 return_value = CURL_FORMADD_NULL;
464 }
465 break;
466
467 case CURLFORM_BUFFERLENGTH:
468 if(current_form->bufferlength)
469 return_value = CURL_FORMADD_OPTION_TWICE;
470 else
471 current_form->bufferlength =
472 array_state?(size_t)array_value:(size_t)va_arg(params, long);
473 break;
474
475 case CURLFORM_STREAM:
476 current_form->flags |= HTTPPOST_CALLBACK;
477 if(current_form->userp)
478 return_value = CURL_FORMADD_OPTION_TWICE;
479 else {
480 char *userp =
481 array_state?array_value:va_arg(params, char *);
482 if(userp) {
483 current_form->userp = userp;
484 current_form->value = userp; /* this isn't strictly true but we
485 derive a value from this later on
486 and we need this non-NULL to be
487 accepted as a fine form part */
488 }
489 else
490 return_value = CURL_FORMADD_NULL;
491 }
492 break;
493
494 case CURLFORM_CONTENTTYPE:
495 {
496 const char *contenttype =
497 array_state?array_value:va_arg(params, char *);
498 if(current_form->contenttype) {
499 if(current_form->flags & HTTPPOST_FILENAME) {
500 if(contenttype) {
501 char *type = strdup(contenttype);
502 if(!type)
503 return_value = CURL_FORMADD_MEMORY;
504 else {
505 form = AddFormInfo(NULL, type, current_form);
506 if(!form) {
507 free(type);
508 return_value = CURL_FORMADD_MEMORY;
509 }
510 else {
511 form->contenttype_alloc = TRUE;
512 current_form = form;
513 form = NULL;
514 }
515 }
516 }
517 else
518 return_value = CURL_FORMADD_NULL;
519 }
520 else
521 return_value = CURL_FORMADD_OPTION_TWICE;
522 }
523 else {
524 if(contenttype) {
525 current_form->contenttype = strdup(contenttype);
526 if(!current_form->contenttype)
527 return_value = CURL_FORMADD_MEMORY;
528 else
529 current_form->contenttype_alloc = TRUE;
530 }
531 else
532 return_value = CURL_FORMADD_NULL;
533 }
534 break;
535 }
536 case CURLFORM_CONTENTHEADER:
537 {
538 /* this "cast increases required alignment of target type" but
539 we consider it OK anyway */
540 struct curl_slist* list = array_state?
541 (struct curl_slist*)array_value:
542 va_arg(params, struct curl_slist*);
543
544 if(current_form->contentheader)
545 return_value = CURL_FORMADD_OPTION_TWICE;
546 else
547 current_form->contentheader = list;
548
549 break;
550 }
551 case CURLFORM_FILENAME:
552 case CURLFORM_BUFFER:
553 {
554 const char *filename = array_state?array_value:
555 va_arg(params, char *);
556 if(current_form->showfilename)
557 return_value = CURL_FORMADD_OPTION_TWICE;
558 else {
559 current_form->showfilename = strdup(filename);
560 if(!current_form->showfilename)
561 return_value = CURL_FORMADD_MEMORY;
562 else
563 current_form->showfilename_alloc = TRUE;
564 }
565 break;
566 }
567 default:
568 return_value = CURL_FORMADD_UNKNOWN_OPTION;
569 break;
570 }
571 }
572
573 if(CURL_FORMADD_OK != return_value) {
574 /* On error, free allocated fields for all nodes of the FormInfo linked
575 list without deallocating nodes. List nodes are deallocated later on */
576 FormInfo *ptr;
577 for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
578 if(ptr->name_alloc) {
579 Curl_safefree(ptr->name);
580 ptr->name_alloc = FALSE;
581 }
582 if(ptr->value_alloc) {
583 Curl_safefree(ptr->value);
584 ptr->value_alloc = FALSE;
585 }
586 if(ptr->contenttype_alloc) {
587 Curl_safefree(ptr->contenttype);
588 ptr->contenttype_alloc = FALSE;
589 }
590 if(ptr->showfilename_alloc) {
591 Curl_safefree(ptr->showfilename);
592 ptr->showfilename_alloc = FALSE;
593 }
594 }
595 }
596
597 if(CURL_FORMADD_OK == return_value) {
598 /* go through the list, check for completeness and if everything is
599 * alright add the HttpPost item otherwise set return_value accordingly */
600
601 post = NULL;
602 for(form = first_form;
603 form != NULL;
604 form = form->more) {
605 if(((!form->name || !form->value) && !post) ||
606 ( (form->contentslength) &&
607 (form->flags & HTTPPOST_FILENAME) ) ||
608 ( (form->flags & HTTPPOST_FILENAME) &&
609 (form->flags & HTTPPOST_PTRCONTENTS) ) ||
610
611 ( (!form->buffer) &&
612 (form->flags & HTTPPOST_BUFFER) &&
613 (form->flags & HTTPPOST_PTRBUFFER) ) ||
614
615 ( (form->flags & HTTPPOST_READFILE) &&
616 (form->flags & HTTPPOST_PTRCONTENTS) )
617 ) {
618 return_value = CURL_FORMADD_INCOMPLETE;
619 break;
620 }
621 else {
622 if(((form->flags & HTTPPOST_FILENAME) ||
623 (form->flags & HTTPPOST_BUFFER)) &&
624 !form->contenttype ) {
625 char *f = form->flags & HTTPPOST_BUFFER?
626 form->showfilename : form->value;
627
628 /* our contenttype is missing */
629 form->contenttype = strdup(ContentTypeForFilename(f, prevtype));
630 if(!form->contenttype) {
631 return_value = CURL_FORMADD_MEMORY;
632 break;
633 }
634 form->contenttype_alloc = TRUE;
635 }
636 if(!(form->flags & HTTPPOST_PTRNAME) &&
637 (form == first_form) ) {
638 /* Note that there's small risk that form->name is NULL here if the
639 app passed in a bad combo, so we better check for that first. */
640 if(form->name) {
641 /* copy name (without strdup; possibly contains null characters) */
642 form->name = Curl_memdup(form->name, form->namelength?
643 form->namelength:
644 strlen(form->name)+1);
645 }
646 if(!form->name) {
647 return_value = CURL_FORMADD_MEMORY;
648 break;
649 }
650 form->name_alloc = TRUE;
651 }
652 if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
653 HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
654 HTTPPOST_CALLBACK)) && form->value) {
655 /* copy value (without strdup; possibly contains null characters) */
656 form->value = Curl_memdup(form->value, form->contentslength?
657 form->contentslength:
658 strlen(form->value)+1);
659 if(!form->value) {
660 return_value = CURL_FORMADD_MEMORY;
661 break;
662 }
663 form->value_alloc = TRUE;
664 }
665 post = AddHttpPost(form->name, form->namelength,
666 form->value, form->contentslength,
667 form->buffer, form->bufferlength,
668 form->contenttype, form->flags,
669 form->contentheader, form->showfilename,
670 form->userp,
671 post, httppost,
672 last_post);
673
674 if(!post) {
675 return_value = CURL_FORMADD_MEMORY;
676 break;
677 }
678
679 if(form->contenttype)
680 prevtype = form->contenttype;
681 }
682 }
683 if(CURL_FORMADD_OK != return_value) {
684 /* On error, free allocated fields for nodes of the FormInfo linked
685 list which are not already owned by the httppost linked list
686 without deallocating nodes. List nodes are deallocated later on */
687 FormInfo *ptr;
688 for(ptr = form; ptr != NULL; ptr = ptr->more) {
689 if(ptr->name_alloc) {
690 Curl_safefree(ptr->name);
691 ptr->name_alloc = FALSE;
692 }
693 if(ptr->value_alloc) {
694 Curl_safefree(ptr->value);
695 ptr->value_alloc = FALSE;
696 }
697 if(ptr->contenttype_alloc) {
698 Curl_safefree(ptr->contenttype);
699 ptr->contenttype_alloc = FALSE;
700 }
701 if(ptr->showfilename_alloc) {
702 Curl_safefree(ptr->showfilename);
703 ptr->showfilename_alloc = FALSE;
704 }
705 }
706 }
707 }
708
709 /* Always deallocate FormInfo linked list nodes without touching node
710 fields given that these have either been deallocated or are owned
711 now by the httppost linked list */
712 while(first_form) {
713 FormInfo *ptr = first_form->more;
714 free(first_form);
715 first_form = ptr;
716 }
717
718 return return_value;
719 }
720
721 /*
722 * curl_formadd() is a public API to add a section to the multipart formpost.
723 *
724 * @unittest: 1308
725 */
726
curl_formadd(struct curl_httppost ** httppost,struct curl_httppost ** last_post,...)727 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
728 struct curl_httppost **last_post,
729 ...)
730 {
731 va_list arg;
732 CURLFORMcode result;
733 va_start(arg, last_post);
734 result = FormAdd(httppost, last_post, arg);
735 va_end(arg);
736 return result;
737 }
738
739 #ifdef __VMS
740 #include <fabdef.h>
741 /*
742 * get_vms_file_size does what it takes to get the real size of the file
743 *
744 * For fixed files, find out the size of the EOF block and adjust.
745 *
746 * For all others, have to read the entire file in, discarding the contents.
747 * Most posted text files will be small, and binary files like zlib archives
748 * and CD/DVD images should be either a STREAM_LF format or a fixed format.
749 *
750 */
VmsRealFileSize(const char * name,const struct_stat * stat_buf)751 curl_off_t VmsRealFileSize(const char * name,
752 const struct_stat * stat_buf)
753 {
754 char buffer[8192];
755 curl_off_t count;
756 int ret_stat;
757 FILE * file;
758
759 file = fopen(name, "r"); /* VMS */
760 if(file == NULL)
761 return 0;
762
763 count = 0;
764 ret_stat = 1;
765 while(ret_stat > 0) {
766 ret_stat = fread(buffer, 1, sizeof(buffer), file);
767 if(ret_stat != 0)
768 count += ret_stat;
769 }
770 fclose(file);
771
772 return count;
773 }
774
775 /*
776 *
777 * VmsSpecialSize checks to see if the stat st_size can be trusted and
778 * if not to call a routine to get the correct size.
779 *
780 */
VmsSpecialSize(const char * name,const struct_stat * stat_buf)781 static curl_off_t VmsSpecialSize(const char * name,
782 const struct_stat * stat_buf)
783 {
784 switch(stat_buf->st_fab_rfm) {
785 case FAB$C_VAR:
786 case FAB$C_VFC:
787 return VmsRealFileSize(name, stat_buf);
788 break;
789 default:
790 return stat_buf->st_size;
791 }
792 }
793
794 #endif
795
796 #ifndef __VMS
797 #define filesize(name, stat_data) (stat_data.st_size)
798 #else
799 /* Getting the expected file size needs help on VMS */
800 #define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
801 #endif
802
803 /*
804 * AddFormData() adds a chunk of data to the FormData linked list.
805 *
806 * size is incremented by the chunk length, unless it is NULL
807 */
AddFormData(struct FormData ** formp,enum formtype type,const void * line,size_t length,curl_off_t * size)808 static CURLcode AddFormData(struct FormData **formp,
809 enum formtype type,
810 const void *line,
811 size_t length,
812 curl_off_t *size)
813 {
814 struct FormData *newform = malloc(sizeof(struct FormData));
815 if(!newform)
816 return CURLE_OUT_OF_MEMORY;
817 newform->next = NULL;
818
819 if(type <= FORM_CONTENT) {
820 /* we make it easier for plain strings: */
821 if(!length)
822 length = strlen((char *)line);
823
824 newform->line = malloc(length+1);
825 if(!newform->line) {
826 free(newform);
827 return CURLE_OUT_OF_MEMORY;
828 }
829 memcpy(newform->line, line, length);
830 newform->length = length;
831 newform->line[length]=0; /* zero terminate for easier debugging */
832 }
833 else
834 /* For callbacks and files we don't have any actual data so we just keep a
835 pointer to whatever this points to */
836 newform->line = (char *)line;
837
838 newform->type = type;
839
840 if(*formp) {
841 (*formp)->next = newform;
842 *formp = newform;
843 }
844 else
845 *formp = newform;
846
847 if(size) {
848 if(type != FORM_FILE)
849 /* for static content as well as callback data we add the size given
850 as input argument */
851 *size += length;
852 else {
853 /* Since this is a file to be uploaded here, add the size of the actual
854 file */
855 if(!strequal("-", newform->line)) {
856 struct_stat file;
857 if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode))
858 *size += filesize(newform->line, file);
859 else
860 return CURLE_BAD_FUNCTION_ARGUMENT;
861 }
862 }
863 }
864 return CURLE_OK;
865 }
866
867 /*
868 * AddFormDataf() adds printf()-style formatted data to the formdata chain.
869 */
870
AddFormDataf(struct FormData ** formp,curl_off_t * size,const char * fmt,...)871 static CURLcode AddFormDataf(struct FormData **formp,
872 curl_off_t *size,
873 const char *fmt, ...)
874 {
875 char s[4096];
876 va_list ap;
877 va_start(ap, fmt);
878 vsnprintf(s, sizeof(s), fmt, ap);
879 va_end(ap);
880
881 return AddFormData(formp, FORM_DATA, s, 0, size);
882 }
883
884 /*
885 * Curl_formclean() is used from http.c, this cleans a built FormData linked
886 * list
887 */
Curl_formclean(struct FormData ** form_ptr)888 void Curl_formclean(struct FormData **form_ptr)
889 {
890 struct FormData *next, *form;
891
892 form = *form_ptr;
893 if(!form)
894 return;
895
896 do {
897 next=form->next; /* the following form line */
898 if(form->type <= FORM_CONTENT)
899 free(form->line); /* free the line */
900 free(form); /* free the struct */
901
902 } while((form = next) != NULL); /* continue */
903
904 *form_ptr = NULL;
905 }
906
907 /*
908 * curl_formget()
909 * Serialize a curl_httppost struct.
910 * Returns 0 on success.
911 *
912 * @unittest: 1308
913 */
curl_formget(struct curl_httppost * form,void * arg,curl_formget_callback append)914 int curl_formget(struct curl_httppost *form, void *arg,
915 curl_formget_callback append)
916 {
917 CURLcode result;
918 curl_off_t size;
919 struct FormData *data, *ptr;
920
921 result = Curl_getformdata(NULL, &data, form, NULL, &size);
922 if(result)
923 return (int)result;
924
925 for(ptr = data; ptr; ptr = ptr->next) {
926 if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
927 char buffer[8192];
928 size_t nread;
929 struct Form temp;
930
931 Curl_FormInit(&temp, ptr);
932
933 do {
934 nread = readfromfile(&temp, buffer, sizeof(buffer));
935 if((nread == (size_t) -1) ||
936 (nread > sizeof(buffer)) ||
937 (nread != append(arg, buffer, nread))) {
938 if(temp.fp)
939 fclose(temp.fp);
940 Curl_formclean(&data);
941 return -1;
942 }
943 } while(nread);
944 }
945 else {
946 if(ptr->length != append(arg, ptr->line, ptr->length)) {
947 Curl_formclean(&data);
948 return -1;
949 }
950 }
951 }
952 Curl_formclean(&data);
953 return 0;
954 }
955
956 /*
957 * curl_formfree() is an external function to free up a whole form post
958 * chain
959 */
curl_formfree(struct curl_httppost * form)960 void curl_formfree(struct curl_httppost *form)
961 {
962 struct curl_httppost *next;
963
964 if(!form)
965 /* no form to free, just get out of this */
966 return;
967
968 do {
969 next=form->next; /* the following form line */
970
971 /* recurse to sub-contents */
972 curl_formfree(form->more);
973
974 if(!(form->flags & HTTPPOST_PTRNAME))
975 free(form->name); /* free the name */
976 if(!(form->flags &
977 (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK))
978 )
979 free(form->contents); /* free the contents */
980 free(form->contenttype); /* free the content type */
981 free(form->showfilename); /* free the faked file name */
982 free(form); /* free the struct */
983
984 } while((form = next) != NULL); /* continue */
985 }
986
987 #ifndef HAVE_BASENAME
988 /*
989 (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
990 Edition)
991
992 The basename() function shall take the pathname pointed to by path and
993 return a pointer to the final component of the pathname, deleting any
994 trailing '/' characters.
995
996 If the string pointed to by path consists entirely of the '/' character,
997 basename() shall return a pointer to the string "/". If the string pointed
998 to by path is exactly "//", it is implementation-defined whether '/' or "//"
999 is returned.
1000
1001 If path is a null pointer or points to an empty string, basename() shall
1002 return a pointer to the string ".".
1003
1004 The basename() function may modify the string pointed to by path, and may
1005 return a pointer to static storage that may then be overwritten by a
1006 subsequent call to basename().
1007
1008 The basename() function need not be reentrant. A function that is not
1009 required to be reentrant is not required to be thread-safe.
1010
1011 */
Curl_basename(char * path)1012 static char *Curl_basename(char *path)
1013 {
1014 /* Ignore all the details above for now and make a quick and simple
1015 implementaion here */
1016 char *s1;
1017 char *s2;
1018
1019 s1=strrchr(path, '/');
1020 s2=strrchr(path, '\\');
1021
1022 if(s1 && s2) {
1023 path = (s1 > s2? s1 : s2)+1;
1024 }
1025 else if(s1)
1026 path = s1 + 1;
1027 else if(s2)
1028 path = s2 + 1;
1029
1030 return path;
1031 }
1032 #endif
1033
strippath(const char * fullfile)1034 static char *strippath(const char *fullfile)
1035 {
1036 char *filename;
1037 char *base;
1038 filename = strdup(fullfile); /* duplicate since basename() may ruin the
1039 buffer it works on */
1040 if(!filename)
1041 return NULL;
1042 base = strdup(basename(filename));
1043
1044 free(filename); /* free temporary buffer */
1045
1046 return base; /* returns an allocated string or NULL ! */
1047 }
1048
formdata_add_filename(const struct curl_httppost * file,struct FormData ** form,curl_off_t * size)1049 static CURLcode formdata_add_filename(const struct curl_httppost *file,
1050 struct FormData **form,
1051 curl_off_t *size)
1052 {
1053 CURLcode result = CURLE_OK;
1054 char *filename = file->showfilename;
1055 char *filebasename = NULL;
1056 char *filename_escaped = NULL;
1057
1058 if(!filename) {
1059 filebasename = strippath(file->contents);
1060 if(!filebasename)
1061 return CURLE_OUT_OF_MEMORY;
1062 filename = filebasename;
1063 }
1064
1065 if(strchr(filename, '\\') || strchr(filename, '"')) {
1066 char *p0, *p1;
1067
1068 /* filename need be escaped */
1069 filename_escaped = malloc(strlen(filename)*2+1);
1070 if(!filename_escaped) {
1071 free(filebasename);
1072 return CURLE_OUT_OF_MEMORY;
1073 }
1074 p0 = filename_escaped;
1075 p1 = filename;
1076 while(*p1) {
1077 if(*p1 == '\\' || *p1 == '"')
1078 *p0++ = '\\';
1079 *p0++ = *p1++;
1080 }
1081 *p0 = '\0';
1082 filename = filename_escaped;
1083 }
1084 result = AddFormDataf(form, size,
1085 "; filename=\"%s\"",
1086 filename);
1087 free(filename_escaped);
1088 free(filebasename);
1089 return result;
1090 }
1091
1092 /*
1093 * Curl_getformdata() converts a linked list of "meta data" into a complete
1094 * (possibly huge) multipart formdata. The input list is in 'post', while the
1095 * output resulting linked lists gets stored in '*finalform'. *sizep will get
1096 * the total size of the whole POST.
1097 * A multipart/form_data content-type is built, unless a custom content-type
1098 * is passed in 'custom_content_type'.
1099 *
1100 * This function will not do a failf() for the potential memory failures but
1101 * should for all other errors it spots. Just note that this function MAY get
1102 * a NULL pointer in the 'data' argument.
1103 */
1104
Curl_getformdata(struct SessionHandle * data,struct FormData ** finalform,struct curl_httppost * post,const char * custom_content_type,curl_off_t * sizep)1105 CURLcode Curl_getformdata(struct SessionHandle *data,
1106 struct FormData **finalform,
1107 struct curl_httppost *post,
1108 const char *custom_content_type,
1109 curl_off_t *sizep)
1110 {
1111 struct FormData *form = NULL;
1112 struct FormData *firstform;
1113 struct curl_httppost *file;
1114 CURLcode result = CURLE_OK;
1115
1116 curl_off_t size = 0; /* support potentially ENORMOUS formposts */
1117 char *boundary;
1118 char *fileboundary = NULL;
1119 struct curl_slist* curList;
1120
1121 *finalform = NULL; /* default form is empty */
1122
1123 if(!post)
1124 return result; /* no input => no output! */
1125
1126 boundary = formboundary(data);
1127 if(!boundary)
1128 return CURLE_OUT_OF_MEMORY;
1129
1130 /* Make the first line of the output */
1131 result = AddFormDataf(&form, NULL,
1132 "%s; boundary=%s\r\n",
1133 custom_content_type?custom_content_type:
1134 "Content-Type: multipart/form-data",
1135 boundary);
1136
1137 if(result) {
1138 free(boundary);
1139 return result;
1140 }
1141 /* we DO NOT include that line in the total size of the POST, since it'll be
1142 part of the header! */
1143
1144 firstform = form;
1145
1146 do {
1147
1148 if(size) {
1149 result = AddFormDataf(&form, &size, "\r\n");
1150 if(result)
1151 break;
1152 }
1153
1154 /* boundary */
1155 result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
1156 if(result)
1157 break;
1158
1159 /* Maybe later this should be disabled when a custom_content_type is
1160 passed, since Content-Disposition is not meaningful for all multipart
1161 types.
1162 */
1163 result = AddFormDataf(&form, &size,
1164 "Content-Disposition: form-data; name=\"");
1165 if(result)
1166 break;
1167
1168 result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
1169 &size);
1170 if(result)
1171 break;
1172
1173 result = AddFormDataf(&form, &size, "\"");
1174 if(result)
1175 break;
1176
1177 if(post->more) {
1178 /* If used, this is a link to more file names, we must then do
1179 the magic to include several files with the same field name */
1180
1181 free(fileboundary);
1182 fileboundary = formboundary(data);
1183 if(!fileboundary) {
1184 result = CURLE_OUT_OF_MEMORY;
1185 break;
1186 }
1187
1188 result = AddFormDataf(&form, &size,
1189 "\r\nContent-Type: multipart/mixed;"
1190 " boundary=%s\r\n",
1191 fileboundary);
1192 if(result)
1193 break;
1194 }
1195
1196 file = post;
1197
1198 do {
1199
1200 /* If 'showfilename' is set, that is a faked name passed on to us
1201 to use to in the formpost. If that is not set, the actually used
1202 local file name should be added. */
1203
1204 if(post->more) {
1205 /* if multiple-file */
1206 result = AddFormDataf(&form, &size,
1207 "\r\n--%s\r\nContent-Disposition: "
1208 "attachment",
1209 fileboundary);
1210 if(result)
1211 break;
1212 result = formdata_add_filename(file, &form, &size);
1213 if(result)
1214 break;
1215 }
1216 else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
1217 HTTPPOST_CALLBACK)) {
1218 /* it should be noted that for the HTTPPOST_FILENAME and
1219 HTTPPOST_CALLBACK cases the ->showfilename struct member is always
1220 assigned at this point */
1221 if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
1222 result = formdata_add_filename(post, &form, &size);
1223 }
1224
1225 if(result)
1226 break;
1227 }
1228
1229 if(file->contenttype) {
1230 /* we have a specified type */
1231 result = AddFormDataf(&form, &size,
1232 "\r\nContent-Type: %s",
1233 file->contenttype);
1234 if(result)
1235 break;
1236 }
1237
1238 curList = file->contentheader;
1239 while(curList) {
1240 /* Process the additional headers specified for this form */
1241 result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
1242 if(result)
1243 break;
1244 curList = curList->next;
1245 }
1246 if(result)
1247 break;
1248
1249 result = AddFormDataf(&form, &size, "\r\n\r\n");
1250 if(result)
1251 break;
1252
1253 if((post->flags & HTTPPOST_FILENAME) ||
1254 (post->flags & HTTPPOST_READFILE)) {
1255 /* we should include the contents from the specified file */
1256 FILE *fileread;
1257
1258 fileread = strequal("-", file->contents)?
1259 stdin:fopen(file->contents, "rb"); /* binary read for win32 */
1260
1261 /*
1262 * VMS: This only allows for stream files on VMS. Stream files are
1263 * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
1264 * every record needs to have a \n appended & 1 added to SIZE
1265 */
1266
1267 if(fileread) {
1268 if(fileread != stdin) {
1269 /* close the file */
1270 fclose(fileread);
1271 /* add the file name only - for later reading from this */
1272 result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
1273 }
1274 else {
1275 /* When uploading from stdin, we can't know the size of the file,
1276 * thus must read the full file as before. We *could* use chunked
1277 * transfer-encoding, but that only works for HTTP 1.1 and we
1278 * can't be sure we work with such a server.
1279 */
1280 size_t nread;
1281 char buffer[512];
1282 while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
1283 result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
1284 if(result)
1285 break;
1286 }
1287 }
1288 }
1289 else {
1290 if(data)
1291 failf(data, "couldn't open file \"%s\"", file->contents);
1292 *finalform = NULL;
1293 result = CURLE_READ_ERROR;
1294 }
1295 }
1296 else if(post->flags & HTTPPOST_BUFFER)
1297 /* include contents of buffer */
1298 result = AddFormData(&form, FORM_CONTENT, post->buffer,
1299 post->bufferlength, &size);
1300 else if(post->flags & HTTPPOST_CALLBACK)
1301 /* the contents should be read with the callback and the size
1302 is set with the contentslength */
1303 result = AddFormData(&form, FORM_CALLBACK, post->userp,
1304 post->contentslength, &size);
1305 else
1306 /* include the contents we got */
1307 result = AddFormData(&form, FORM_CONTENT, post->contents,
1308 post->contentslength, &size);
1309
1310 file = file->more;
1311 } while(file && !result); /* for each specified file for this field */
1312
1313 if(result)
1314 break;
1315
1316 if(post->more) {
1317 /* this was a multiple-file inclusion, make a termination file
1318 boundary: */
1319 result = AddFormDataf(&form, &size,
1320 "\r\n--%s--",
1321 fileboundary);
1322 if(result)
1323 break;
1324 }
1325
1326 } while((post = post->next) != NULL); /* for each field */
1327
1328 /* end-boundary for everything */
1329 if(!result)
1330 result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary);
1331
1332 if(result) {
1333 Curl_formclean(&firstform);
1334 free(fileboundary);
1335 free(boundary);
1336 return result;
1337 }
1338
1339 *sizep = size;
1340
1341 free(fileboundary);
1342 free(boundary);
1343
1344 *finalform = firstform;
1345
1346 return result;
1347 }
1348
1349 /*
1350 * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
1351 * and resets the 'sent' counter.
1352 */
Curl_FormInit(struct Form * form,struct FormData * formdata)1353 int Curl_FormInit(struct Form *form, struct FormData *formdata )
1354 {
1355 if(!formdata)
1356 return 1; /* error */
1357
1358 form->data = formdata;
1359 form->sent = 0;
1360 form->fp = NULL;
1361 form->fread_func = ZERO_NULL;
1362
1363 return 0;
1364 }
1365
1366 #ifndef __VMS
1367 # define fopen_read fopen
1368 #else
1369 /*
1370 * vmsfopenread
1371 *
1372 * For upload to work as expected on VMS, different optional
1373 * parameters must be added to the fopen command based on
1374 * record format of the file.
1375 *
1376 */
1377 # define fopen_read vmsfopenread
vmsfopenread(const char * file,const char * mode)1378 static FILE * vmsfopenread(const char *file, const char *mode) {
1379 struct_stat statbuf;
1380 int result;
1381
1382 result = stat(file, &statbuf);
1383
1384 switch (statbuf.st_fab_rfm) {
1385 case FAB$C_VAR:
1386 case FAB$C_VFC:
1387 case FAB$C_STMCR:
1388 return fopen(file, "r"); /* VMS */
1389 break;
1390 default:
1391 return fopen(file, "r", "rfm=stmlf", "ctx=stm");
1392 }
1393 }
1394 #endif
1395
1396 /*
1397 * readfromfile()
1398 *
1399 * The read callback that this function may use can return a value larger than
1400 * 'size' (which then this function returns) that indicates a problem and it
1401 * must be properly dealt with
1402 */
readfromfile(struct Form * form,char * buffer,size_t size)1403 static size_t readfromfile(struct Form *form, char *buffer,
1404 size_t size)
1405 {
1406 size_t nread;
1407 bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
1408
1409 if(callback) {
1410 if(form->fread_func == ZERO_NULL)
1411 return 0;
1412 else
1413 nread = form->fread_func(buffer, 1, size, form->data->line);
1414 }
1415 else {
1416 if(!form->fp) {
1417 /* this file hasn't yet been opened */
1418 form->fp = fopen_read(form->data->line, "rb"); /* b is for binary */
1419 if(!form->fp)
1420 return (size_t)-1; /* failure */
1421 }
1422 nread = fread(buffer, 1, size, form->fp);
1423 }
1424 if(!nread) {
1425 /* this is the last chunk from the file, move on */
1426 if(form->fp) {
1427 fclose(form->fp);
1428 form->fp = NULL;
1429 }
1430 form->data = form->data->next;
1431 }
1432
1433 return nread;
1434 }
1435
1436 /*
1437 * Curl_FormReader() is the fread() emulation function that will be used to
1438 * deliver the formdata to the transfer loop and then sent away to the peer.
1439 */
Curl_FormReader(char * buffer,size_t size,size_t nitems,FILE * mydata)1440 size_t Curl_FormReader(char *buffer,
1441 size_t size,
1442 size_t nitems,
1443 FILE *mydata)
1444 {
1445 struct Form *form;
1446 size_t wantedsize;
1447 size_t gotsize = 0;
1448
1449 form=(struct Form *)mydata;
1450
1451 wantedsize = size * nitems;
1452
1453 if(!form->data)
1454 return 0; /* nothing, error, empty */
1455
1456 if((form->data->type == FORM_FILE) ||
1457 (form->data->type == FORM_CALLBACK)) {
1458 gotsize = readfromfile(form, buffer, wantedsize);
1459
1460 if(gotsize)
1461 /* If positive or -1, return. If zero, continue! */
1462 return gotsize;
1463 }
1464 do {
1465
1466 if((form->data->length - form->sent ) > wantedsize - gotsize) {
1467
1468 memcpy(buffer + gotsize , form->data->line + form->sent,
1469 wantedsize - gotsize);
1470
1471 form->sent += wantedsize-gotsize;
1472
1473 return wantedsize;
1474 }
1475
1476 memcpy(buffer+gotsize,
1477 form->data->line + form->sent,
1478 (form->data->length - form->sent) );
1479 gotsize += form->data->length - form->sent;
1480
1481 form->sent = 0;
1482
1483 form->data = form->data->next; /* advance */
1484
1485 } while(form->data && (form->data->type < FORM_CALLBACK));
1486 /* If we got an empty line and we have more data, we proceed to the next
1487 line immediately to avoid returning zero before we've reached the end. */
1488
1489 return gotsize;
1490 }
1491
1492 /*
1493 * Curl_formpostheader() returns the first line of the formpost, the
1494 * request-header part (which is not part of the request-body like the rest of
1495 * the post).
1496 */
Curl_formpostheader(void * formp,size_t * len)1497 char *Curl_formpostheader(void *formp, size_t *len)
1498 {
1499 char *header;
1500 struct Form *form=(struct Form *)formp;
1501
1502 if(!form->data)
1503 return 0; /* nothing, ERROR! */
1504
1505 header = form->data->line;
1506 *len = form->data->length;
1507
1508 form->data = form->data->next; /* advance */
1509
1510 return header;
1511 }
1512
1513 /*
1514 * formboundary() creates a suitable boundary string and returns an allocated
1515 * one.
1516 */
formboundary(struct SessionHandle * data)1517 static char *formboundary(struct SessionHandle *data)
1518 {
1519 /* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615)
1520 combinations */
1521 return aprintf("------------------------%08x%08x",
1522 Curl_rand(data), Curl_rand(data));
1523 }
1524
1525 #else /* CURL_DISABLE_HTTP */
curl_formadd(struct curl_httppost ** httppost,struct curl_httppost ** last_post,...)1526 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
1527 struct curl_httppost **last_post,
1528 ...)
1529 {
1530 (void)httppost;
1531 (void)last_post;
1532 return CURL_FORMADD_DISABLED;
1533 }
1534
curl_formget(struct curl_httppost * form,void * arg,curl_formget_callback append)1535 int curl_formget(struct curl_httppost *form, void *arg,
1536 curl_formget_callback append)
1537 {
1538 (void) form;
1539 (void) arg;
1540 (void) append;
1541 return CURL_FORMADD_DISABLED;
1542 }
1543
curl_formfree(struct curl_httppost * form)1544 void curl_formfree(struct curl_httppost *form)
1545 {
1546 (void)form;
1547 /* does nothing HTTP is disabled */
1548 }
1549
1550
1551 #endif /* !defined(CURL_DISABLE_HTTP) */
1552