1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) 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 #include "tool_setup.h" 23 24 #include "strcase.h" 25 26 #define ENABLE_CURLX_PRINTF 27 /* use our own printf() functions */ 28 #include "curlx.h" 29 30 #include "tool_cfgable.h" 31 #include "tool_convert.h" 32 #include "tool_msgs.h" 33 #include "tool_binmode.h" 34 #include "tool_getparam.h" 35 #include "tool_paramhlp.h" 36 #include "tool_formparse.h" 37 38 #include "memdebug.h" /* keep this as LAST include */ 39 40 /* Macros to free const pointers. */ 41 #define CONST_FREE(x) free((void *) (x)) 42 #define CONST_SAFEFREE(x) Curl_safefree(*((void **) &(x))) 43 44 /* tool_mime functions. */ 45 static tool_mime *tool_mime_new(tool_mime *parent, toolmimekind kind) 46 { 47 tool_mime *m = (tool_mime *) calloc(1, sizeof(*m)); 48 49 if(m) { 50 m->kind = kind; 51 m->parent = parent; 52 if(parent) { 53 m->prev = parent->subparts; 54 parent->subparts = m; 55 } 56 } 57 return m; 58 } 59 60 static tool_mime *tool_mime_new_parts(tool_mime *parent) 61 { 62 return tool_mime_new(parent, TOOLMIME_PARTS); 63 } 64 65 static tool_mime *tool_mime_new_data(tool_mime *parent, const char *data) 66 { 67 tool_mime *m = NULL; 68 69 data = strdup(data); 70 if(data) { 71 m = tool_mime_new(parent, TOOLMIME_DATA); 72 if(!m) 73 CONST_FREE(data); 74 else 75 m->data = data; 76 } 77 return m; 78 } 79 80 static tool_mime *tool_mime_new_filedata(tool_mime *parent, 81 const char *filename, 82 bool isremotefile, 83 CURLcode *errcode) 84 { 85 CURLcode result = CURLE_OK; 86 tool_mime *m = NULL; 87 88 *errcode = CURLE_OUT_OF_MEMORY; 89 if(strcmp(filename, "-")) { 90 /* This is a normal file. */ 91 filename = strdup(filename); 92 if(filename) { 93 m = tool_mime_new(parent, TOOLMIME_FILE); 94 if(!m) 95 CONST_FREE(filename); 96 else { 97 m->data = filename; 98 if(!isremotefile) 99 m->kind = TOOLMIME_FILEDATA; 100 *errcode = CURLE_OK; 101 } 102 } 103 } 104 else { /* Standard input. */ 105 int fd = fileno(stdin); 106 char *data = NULL; 107 curl_off_t size; 108 curl_off_t origin; 109 struct_stat sbuf; 110 111 set_binmode(stdin); 112 origin = ftell(stdin); 113 /* If stdin is a regular file, do not buffer data but read it 114 when needed. */ 115 if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) && 116 #ifdef __VMS 117 sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && 118 #endif 119 S_ISREG(sbuf.st_mode)) { 120 size = sbuf.st_size - origin; 121 if(size < 0) 122 size = 0; 123 } 124 else { /* Not suitable for direct use, buffer stdin data. */ 125 size_t stdinsize = 0; 126 127 if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) { 128 /* Out of memory. */ 129 return m; 130 } 131 132 if(ferror(stdin)) { 133 result = CURLE_READ_ERROR; 134 Curl_safefree(data); 135 data = NULL; 136 } 137 else if(!stdinsize) { 138 /* Zero-length data has been freed. Re-create it. */ 139 data = strdup(""); 140 if(!data) 141 return m; 142 } 143 size = curlx_uztoso(stdinsize); 144 origin = 0; 145 } 146 m = tool_mime_new(parent, TOOLMIME_STDIN); 147 if(!m) 148 Curl_safefree(data); 149 else { 150 m->data = data; 151 m->origin = origin; 152 m->size = size; 153 m->curpos = 0; 154 if(!isremotefile) 155 m->kind = TOOLMIME_STDINDATA; 156 *errcode = result; 157 } 158 } 159 return m; 160 } 161 162 void tool_mime_free(tool_mime *mime) 163 { 164 if(mime) { 165 if(mime->subparts) 166 tool_mime_free(mime->subparts); 167 if(mime->prev) 168 tool_mime_free(mime->prev); 169 CONST_SAFEFREE(mime->name); 170 CONST_SAFEFREE(mime->filename); 171 CONST_SAFEFREE(mime->type); 172 CONST_SAFEFREE(mime->encoder); 173 CONST_SAFEFREE(mime->data); 174 curl_slist_free_all(mime->headers); 175 free(mime); 176 } 177 } 178 179 180 /* Mime part callbacks for stdin. */ 181 size_t tool_mime_stdin_read(char *buffer, 182 size_t size, size_t nitems, void *arg) 183 { 184 tool_mime *sip = (tool_mime *) arg; 185 curl_off_t bytesleft; 186 (void) size; /* Always 1: ignored. */ 187 188 if(sip->size >= 0) { 189 if(sip->curpos >= sip->size) 190 return 0; /* At eof. */ 191 bytesleft = sip->size - sip->curpos; 192 if(curlx_uztoso(nitems) > bytesleft) 193 nitems = curlx_sotouz(bytesleft); 194 } 195 if(nitems) { 196 if(sip->data) { 197 /* Return data from memory. */ 198 memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems); 199 } 200 else { 201 /* Read from stdin. */ 202 nitems = fread(buffer, 1, nitems, stdin); 203 if(ferror(stdin)) { 204 /* Show error only once. */ 205 if(sip->config) { 206 warnf(sip->config, "stdin: %s\n", strerror(errno)); 207 sip->config = NULL; 208 } 209 return CURL_READFUNC_ABORT; 210 } 211 } 212 sip->curpos += curlx_uztoso(nitems); 213 } 214 return nitems; 215 } 216 217 int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) 218 { 219 tool_mime *sip = (tool_mime *) instream; 220 221 switch(whence) { 222 case SEEK_CUR: 223 offset += sip->curpos; 224 break; 225 case SEEK_END: 226 offset += sip->size; 227 break; 228 } 229 if(offset < 0) 230 return CURL_SEEKFUNC_CANTSEEK; 231 if(!sip->data) { 232 if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) 233 return CURL_SEEKFUNC_CANTSEEK; 234 } 235 sip->curpos = offset; 236 return CURL_SEEKFUNC_OK; 237 } 238 239 /* Translate an internal mime tree into a libcurl mime tree. */ 240 241 static CURLcode tool2curlparts(CURL *curl, tool_mime *m, curl_mime *mime) 242 { 243 CURLcode ret = CURLE_OK; 244 curl_mimepart *part = NULL; 245 curl_mime *submime = NULL; 246 const char *filename = NULL; 247 248 if(m) { 249 ret = tool2curlparts(curl, m->prev, mime); 250 if(!ret) { 251 part = curl_mime_addpart(mime); 252 if(!part) 253 ret = CURLE_OUT_OF_MEMORY; 254 } 255 if(!ret) { 256 filename = m->filename; 257 switch(m->kind) { 258 case TOOLMIME_PARTS: 259 ret = tool2curlmime(curl, m, &submime); 260 if(!ret) { 261 ret = curl_mime_subparts(part, submime); 262 if(ret) 263 curl_mime_free(submime); 264 } 265 break; 266 267 case TOOLMIME_DATA: 268 #ifdef CURL_DOES_CONVERSIONS 269 /* Our data is always textual: convert it to ASCII. */ 270 { 271 size_t size = strlen(m->data); 272 char *cp = malloc(size + 1); 273 274 if(!cp) 275 ret = CURLE_OUT_OF_MEMORY; 276 else { 277 memcpy(cp, m->data, size + 1); 278 ret = convert_to_network(cp, size); 279 if(!ret) 280 ret = curl_mime_data(part, cp, CURL_ZERO_TERMINATED); 281 free(cp); 282 } 283 } 284 #else 285 ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); 286 #endif 287 break; 288 289 case TOOLMIME_FILE: 290 case TOOLMIME_FILEDATA: 291 ret = curl_mime_filedata(part, m->data); 292 if(!ret && m->kind == TOOLMIME_FILEDATA && !filename) 293 ret = curl_mime_filename(part, NULL); 294 break; 295 296 case TOOLMIME_STDIN: 297 if(!filename) 298 filename = "-"; 299 /* FALLTHROUGH */ 300 case TOOLMIME_STDINDATA: 301 ret = curl_mime_data_cb(part, m->size, 302 (curl_read_callback) tool_mime_stdin_read, 303 (curl_seek_callback) tool_mime_stdin_seek, 304 NULL, m); 305 break; 306 307 default: 308 /* Other cases not possible in this context. */ 309 break; 310 } 311 } 312 if(!ret && filename) 313 ret = curl_mime_filename(part, filename); 314 if(!ret) 315 ret = curl_mime_type(part, m->type); 316 if(!ret) 317 ret = curl_mime_headers(part, m->headers, 0); 318 if(!ret) 319 ret = curl_mime_encoder(part, m->encoder); 320 if(!ret) 321 ret = curl_mime_name(part, m->name); 322 } 323 return ret; 324 } 325 326 CURLcode tool2curlmime(CURL *curl, tool_mime *m, curl_mime **mime) 327 { 328 CURLcode ret = CURLE_OK; 329 330 *mime = curl_mime_init(curl); 331 if(!*mime) 332 ret = CURLE_OUT_OF_MEMORY; 333 else 334 ret = tool2curlparts(curl, m->subparts, *mime); 335 if(ret) { 336 curl_mime_free(*mime); 337 *mime = NULL; 338 } 339 return ret; 340 } 341 342 /* 343 * helper function to get a word from form param 344 * after call get_parm_word, str either point to string end 345 * or point to any of end chars. 346 */ 347 static char *get_param_word(char **str, char **end_pos, char endchar) 348 { 349 char *ptr = *str; 350 char *word_begin = NULL; 351 char *ptr2; 352 char *escape = NULL; 353 354 /* the first non-space char is here */ 355 word_begin = ptr; 356 if(*ptr == '"') { 357 ++ptr; 358 while(*ptr) { 359 if(*ptr == '\\') { 360 if(ptr[1] == '\\' || ptr[1] == '"') { 361 /* remember the first escape position */ 362 if(!escape) 363 escape = ptr; 364 /* skip escape of back-slash or double-quote */ 365 ptr += 2; 366 continue; 367 } 368 } 369 if(*ptr == '"') { 370 *end_pos = ptr; 371 if(escape) { 372 /* has escape, we restore the unescaped string here */ 373 ptr = ptr2 = escape; 374 do { 375 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"')) 376 ++ptr; 377 *ptr2++ = *ptr++; 378 } 379 while(ptr < *end_pos); 380 *end_pos = ptr2; 381 } 382 while(*ptr && *ptr != ';' && *ptr != endchar) 383 ++ptr; 384 *str = ptr; 385 return word_begin + 1; 386 } 387 ++ptr; 388 } 389 /* end quote is missing, treat it as non-quoted. */ 390 ptr = word_begin; 391 } 392 393 while(*ptr && *ptr != ';' && *ptr != endchar) 394 ++ptr; 395 *str = *end_pos = ptr; 396 return word_begin; 397 } 398 399 /* Append slist item and return -1 if failed. */ 400 static int slist_append(struct curl_slist **plist, const char *data) 401 { 402 struct curl_slist *s = curl_slist_append(*plist, data); 403 404 if(!s) 405 return -1; 406 407 *plist = s; 408 return 0; 409 } 410 411 /* Read headers from a file and append to list. */ 412 static int read_field_headers(struct OperationConfig *config, 413 const char *filename, FILE *fp, 414 struct curl_slist **pheaders) 415 { 416 size_t hdrlen = 0; 417 size_t pos = 0; 418 bool incomment = FALSE; 419 int lineno = 1; 420 char hdrbuf[999]; /* Max. header length + 1. */ 421 422 for(;;) { 423 int c = getc(fp); 424 if(c == EOF || (!pos && !ISSPACE(c))) { 425 /* Strip and flush the current header. */ 426 while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1])) 427 hdrlen--; 428 if(hdrlen) { 429 hdrbuf[hdrlen] = '\0'; 430 if(slist_append(pheaders, hdrbuf)) { 431 fprintf(config->global->errors, 432 "Out of memory for field headers!\n"); 433 return -1; 434 } 435 hdrlen = 0; 436 } 437 } 438 439 switch(c) { 440 case EOF: 441 if(ferror(fp)) { 442 fprintf(config->global->errors, 443 "Header file %s read error: %s\n", filename, strerror(errno)); 444 return -1; 445 } 446 return 0; /* Done. */ 447 case '\r': 448 continue; /* Ignore. */ 449 case '\n': 450 pos = 0; 451 incomment = FALSE; 452 lineno++; 453 continue; 454 case '#': 455 if(!pos) 456 incomment = TRUE; 457 break; 458 } 459 460 pos++; 461 if(!incomment) { 462 if(hdrlen == sizeof(hdrbuf) - 1) { 463 warnf(config->global, "File %s line %d: header too long (truncated)\n", 464 filename, lineno); 465 c = ' '; 466 } 467 if(hdrlen <= sizeof(hdrbuf) - 1) 468 hdrbuf[hdrlen++] = (char) c; 469 } 470 } 471 /* NOTREACHED */ 472 } 473 474 static int get_param_part(struct OperationConfig *config, char endchar, 475 char **str, char **pdata, char **ptype, 476 char **pfilename, char **pencoder, 477 struct curl_slist **pheaders) 478 { 479 char *p = *str; 480 char *type = NULL; 481 char *filename = NULL; 482 char *encoder = NULL; 483 char *endpos; 484 char *tp; 485 char sep; 486 char type_major[128] = ""; 487 char type_minor[128] = ""; 488 char *endct = NULL; 489 struct curl_slist *headers = NULL; 490 491 if(ptype) 492 *ptype = NULL; 493 if(pfilename) 494 *pfilename = NULL; 495 if(pheaders) 496 *pheaders = NULL; 497 if(pencoder) 498 *pencoder = NULL; 499 while(ISSPACE(*p)) 500 p++; 501 tp = p; 502 *pdata = get_param_word(&p, &endpos, endchar); 503 /* If not quoted, strip trailing spaces. */ 504 if(*pdata == tp) 505 while(endpos > *pdata && ISSPACE(endpos[-1])) 506 endpos--; 507 sep = *p; 508 *endpos = '\0'; 509 while(sep == ';') { 510 while(ISSPACE(*++p)) 511 ; 512 513 if(!endct && checkprefix("type=", p)) { 514 for(p += 5; ISSPACE(*p); p++) 515 ; 516 /* set type pointer */ 517 type = p; 518 519 /* verify that this is a fine type specifier */ 520 if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) { 521 warnf(config->global, "Illegally formatted content-type field!\n"); 522 curl_slist_free_all(headers); 523 return -1; /* illegal content-type syntax! */ 524 } 525 526 /* now point beyond the content-type specifier */ 527 p = type + strlen(type_major) + strlen(type_minor) + 1; 528 for(endct = p; *p && *p != ';' && *p != endchar; p++) 529 if(!ISSPACE(*p)) 530 endct = p + 1; 531 sep = *p; 532 } 533 else if(checkprefix("filename=", p)) { 534 if(endct) { 535 *endct = '\0'; 536 endct = NULL; 537 } 538 for(p += 9; ISSPACE(*p); p++) 539 ; 540 tp = p; 541 filename = get_param_word(&p, &endpos, endchar); 542 /* If not quoted, strip trailing spaces. */ 543 if(filename == tp) 544 while(endpos > filename && ISSPACE(endpos[-1])) 545 endpos--; 546 sep = *p; 547 *endpos = '\0'; 548 } 549 else if(checkprefix("headers=", p)) { 550 if(endct) { 551 *endct = '\0'; 552 endct = NULL; 553 } 554 p += 8; 555 if(*p == '@' || *p == '<') { 556 char *hdrfile; 557 FILE *fp; 558 /* Read headers from a file. */ 559 560 do { 561 p++; 562 } while(ISSPACE(*p)); 563 tp = p; 564 hdrfile = get_param_word(&p, &endpos, endchar); 565 /* If not quoted, strip trailing spaces. */ 566 if(hdrfile == tp) 567 while(endpos > hdrfile && ISSPACE(endpos[-1])) 568 endpos--; 569 sep = *p; 570 *endpos = '\0'; 571 /* TODO: maybe special fopen for VMS? */ 572 fp = fopen(hdrfile, FOPEN_READTEXT); 573 if(!fp) 574 warnf(config->global, "Cannot read from %s: %s\n", hdrfile, 575 strerror(errno)); 576 else { 577 int i = read_field_headers(config, hdrfile, fp, &headers); 578 579 fclose(fp); 580 if(i) { 581 curl_slist_free_all(headers); 582 return -1; 583 } 584 } 585 } 586 else { 587 char *hdr; 588 589 while(ISSPACE(*p)) 590 p++; 591 tp = p; 592 hdr = get_param_word(&p, &endpos, endchar); 593 /* If not quoted, strip trailing spaces. */ 594 if(hdr == tp) 595 while(endpos > hdr && ISSPACE(endpos[-1])) 596 endpos--; 597 sep = *p; 598 *endpos = '\0'; 599 if(slist_append(&headers, hdr)) { 600 fprintf(config->global->errors, "Out of memory for field header!\n"); 601 curl_slist_free_all(headers); 602 return -1; 603 } 604 } 605 } 606 else if(checkprefix("encoder=", p)) { 607 if(endct) { 608 *endct = '\0'; 609 endct = NULL; 610 } 611 for(p += 8; ISSPACE(*p); p++) 612 ; 613 tp = p; 614 encoder = get_param_word(&p, &endpos, endchar); 615 /* If not quoted, strip trailing spaces. */ 616 if(encoder == tp) 617 while(endpos > encoder && ISSPACE(endpos[-1])) 618 endpos--; 619 sep = *p; 620 *endpos = '\0'; 621 } 622 else if(endct) { 623 /* This is part of content type. */ 624 for(endct = p; *p && *p != ';' && *p != endchar; p++) 625 if(!ISSPACE(*p)) 626 endct = p + 1; 627 sep = *p; 628 } 629 else { 630 /* unknown prefix, skip to next block */ 631 char *unknown = get_param_word(&p, &endpos, endchar); 632 633 sep = *p; 634 *endpos = '\0'; 635 if(*unknown) 636 warnf(config->global, "skip unknown form field: %s\n", unknown); 637 } 638 } 639 640 /* Terminate content type. */ 641 if(endct) 642 *endct = '\0'; 643 644 if(ptype) 645 *ptype = type; 646 else if(type) 647 warnf(config->global, "Field content type not allowed here: %s\n", type); 648 649 if(pfilename) 650 *pfilename = filename; 651 else if(filename) 652 warnf(config->global, 653 "Field file name not allowed here: %s\n", filename); 654 655 if(pencoder) 656 *pencoder = encoder; 657 else if(encoder) 658 warnf(config->global, 659 "Field encoder not allowed here: %s\n", encoder); 660 661 if(pheaders) 662 *pheaders = headers; 663 else if(headers) { 664 warnf(config->global, 665 "Field headers not allowed here: %s\n", headers->data); 666 curl_slist_free_all(headers); 667 } 668 669 *str = p; 670 return sep & 0xFF; 671 } 672 673 674 /*************************************************************************** 675 * 676 * formparse() 677 * 678 * Reads a 'name=value' parameter and builds the appropriate linked list. 679 * 680 * If the value is of the form '<filename', field data is read from the 681 * given file. 682 683 * Specify files to upload with 'name=@filename', or 'name=@"filename"' 684 * in case the filename contain ',' or ';'. Supports specified 685 * given Content-Type of the files. Such as ';type=<content-type>'. 686 * 687 * If literal_value is set, any initial '@' or '<' in the value string 688 * loses its special meaning, as does any embedded ';type='. 689 * 690 * You may specify more than one file for a single name (field). Specify 691 * multiple files by writing it like: 692 * 693 * 'name=@filename,filename2,filename3' 694 * 695 * or use double-quotes quote the filename: 696 * 697 * 'name=@"filename","filename2","filename3"' 698 * 699 * If you want content-types specified for each too, write them like: 700 * 701 * 'name=@filename;type=image/gif,filename2,filename3' 702 * 703 * If you want custom headers added for a single part, write them in a separate 704 * file and do like this: 705 * 706 * 'name=foo;headers=@headerfile' or why not 707 * 'name=@filemame;headers=@headerfile' 708 * 709 * To upload a file, but to fake the file name that will be included in the 710 * formpost, do like this: 711 * 712 * 'name=@filename;filename=/dev/null' or quote the faked filename like: 713 * 'name=@filename;filename="play, play, and play.txt"' 714 * 715 * If filename/path contains ',' or ';', it must be quoted by double-quotes, 716 * else curl will fail to figure out the correct filename. if the filename 717 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. 718 * 719 ***************************************************************************/ 720 721 /* Convenience macros for null pointer check. */ 722 #define NULL_CHECK(ptr, init, retcode) { \ 723 (ptr) = (init); \ 724 if(!(ptr)) { \ 725 warnf(config->global, "out of memory!\n"); \ 726 curl_slist_free_all(headers); \ 727 Curl_safefree(contents); \ 728 return retcode; \ 729 } \ 730 } 731 #define SET_TOOL_MIME_PTR(m, field, retcode) { \ 732 if(field) \ 733 NULL_CHECK((m)->field, strdup(field), retcode); \ 734 } 735 736 int formparse(struct OperationConfig *config, 737 const char *input, 738 tool_mime **mimeroot, 739 tool_mime **mimecurrent, 740 bool literal_value) 741 { 742 /* input MUST be a string in the format 'name=contents' and we'll 743 build a linked list with the info */ 744 char *name = NULL; 745 char *contents = NULL; 746 char *contp; 747 char *data; 748 char *type = NULL; 749 char *filename = NULL; 750 char *encoder = NULL; 751 struct curl_slist *headers = NULL; 752 tool_mime *part = NULL; 753 CURLcode res; 754 755 /* Allocate the main mime structure if needed. */ 756 if(!*mimecurrent) { 757 NULL_CHECK(*mimeroot, tool_mime_new_parts(NULL), 1); 758 *mimecurrent = *mimeroot; 759 } 760 761 /* Make a copy we can overwrite. */ 762 NULL_CHECK(contents, strdup(input), 2); 763 764 /* Scan for the end of the name. */ 765 contp = strchr(contents, '='); 766 if(contp) { 767 int sep = '\0'; 768 if(contp > contents) 769 name = contents; 770 *contp++ = '\0'; 771 772 if(*contp == '(' && !literal_value) { 773 /* Starting a multipart. */ 774 sep = get_param_part(config, '\0', 775 &contp, &data, &type, NULL, NULL, &headers); 776 if(sep < 0) { 777 Curl_safefree(contents); 778 return 3; 779 } 780 NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4); 781 *mimecurrent = part; 782 part->headers = headers; 783 headers = NULL; 784 SET_TOOL_MIME_PTR(part, type, 5); 785 } 786 else if(!name && !strcmp(contp, ")") && !literal_value) { 787 /* Ending a multipart. */ 788 if(*mimecurrent == *mimeroot) { 789 warnf(config->global, "no multipart to terminate!\n"); 790 Curl_safefree(contents); 791 return 6; 792 } 793 *mimecurrent = (*mimecurrent)->parent; 794 } 795 else if('@' == contp[0] && !literal_value) { 796 797 /* we use the @-letter to indicate file name(s) */ 798 799 tool_mime *subparts = NULL; 800 801 do { 802 /* since this was a file, it may have a content-type specifier 803 at the end too, or a filename. Or both. */ 804 ++contp; 805 sep = get_param_part(config, ',', &contp, 806 &data, &type, &filename, &encoder, &headers); 807 if(sep < 0) { 808 Curl_safefree(contents); 809 return 7; 810 } 811 812 /* now contp point to comma or string end. 813 If more files to come, make sure we have multiparts. */ 814 if(!subparts) { 815 if(sep != ',') /* If there is a single file. */ 816 subparts = *mimecurrent; 817 else 818 NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8); 819 } 820 821 /* Store that file in a part. */ 822 NULL_CHECK(part, 823 tool_mime_new_filedata(subparts, data, TRUE, &res), 9); 824 part->headers = headers; 825 headers = NULL; 826 part->config = config->global; 827 if(res == CURLE_READ_ERROR) { 828 /* An error occurred while reading stdin: if read has started, 829 issue the error now. Else, delay it until processed by 830 libcurl. */ 831 if(part->size > 0) { 832 warnf(config->global, 833 "error while reading standard input\n"); 834 Curl_safefree(contents); 835 return 10; 836 } 837 CONST_SAFEFREE(part->data); 838 part->data = NULL; 839 part->size = -1; 840 res = CURLE_OK; 841 } 842 SET_TOOL_MIME_PTR(part, filename, 11); 843 SET_TOOL_MIME_PTR(part, type, 12); 844 SET_TOOL_MIME_PTR(part, encoder, 13); 845 846 /* *contp could be '\0', so we just check with the delimiter */ 847 } while(sep); /* loop if there's another file name */ 848 part = (*mimecurrent)->subparts; /* Set name on group. */ 849 } 850 else { 851 if(*contp == '<' && !literal_value) { 852 ++contp; 853 sep = get_param_part(config, '\0', &contp, 854 &data, &type, NULL, &encoder, &headers); 855 if(sep < 0) { 856 Curl_safefree(contents); 857 return 14; 858 } 859 860 NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE, 861 &res), 15); 862 part->headers = headers; 863 headers = NULL; 864 part->config = config->global; 865 if(res == CURLE_READ_ERROR) { 866 /* An error occurred while reading stdin: if read has started, 867 issue the error now. Else, delay it until processed by 868 libcurl. */ 869 if(part->size > 0) { 870 warnf(config->global, 871 "error while reading standard input\n"); 872 Curl_safefree(contents); 873 return 16; 874 } 875 CONST_SAFEFREE(part->data); 876 part->data = NULL; 877 part->size = -1; 878 res = CURLE_OK; 879 } 880 } 881 else { 882 if(literal_value) 883 data = contp; 884 else { 885 sep = get_param_part(config, '\0', &contp, 886 &data, &type, &filename, &encoder, &headers); 887 if(sep < 0) { 888 Curl_safefree(contents); 889 return 17; 890 } 891 } 892 893 NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18); 894 part->headers = headers; 895 headers = NULL; 896 } 897 898 SET_TOOL_MIME_PTR(part, filename, 19); 899 SET_TOOL_MIME_PTR(part, type, 20); 900 SET_TOOL_MIME_PTR(part, encoder, 21); 901 902 if(sep) { 903 *contp = (char) sep; 904 warnf(config->global, 905 "garbage at end of field specification: %s\n", contp); 906 } 907 } 908 909 /* Set part name. */ 910 SET_TOOL_MIME_PTR(part, name, 22); 911 } 912 else { 913 warnf(config->global, "Illegally formatted input field!\n"); 914 Curl_safefree(contents); 915 return 23; 916 } 917 Curl_safefree(contents); 918 return 0; 919 } 920