source: UsbWattMeter/trunk/curl-7.47.1/lib/formdata.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 6 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

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