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