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