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