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