1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, 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.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. */
tool_mime_new(tool_mime * parent,toolmimekind kind)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
tool_mime_new_parts(tool_mime * parent)60 static tool_mime *tool_mime_new_parts(tool_mime *parent)
61 {
62 return tool_mime_new(parent, TOOLMIME_PARTS);
63 }
64
tool_mime_new_data(tool_mime * parent,const char * data)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
tool_mime_new_filedata(tool_mime * parent,const char * filename,bool isremotefile,CURLcode * errcode)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
tool_mime_free(tool_mime * mime)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. */
tool_mime_stdin_read(char * buffer,size_t size,size_t nitems,void * arg)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
tool_mime_stdin_seek(void * instream,curl_off_t offset,int whence)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
tool2curlparts(CURL * curl,tool_mime * m,curl_mime * mime)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
tool2curlmime(CURL * curl,tool_mime * m,curl_mime ** mime)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 */
get_param_word(char ** str,char ** end_pos,char endchar)347 static char *get_param_word(char **str, char **end_pos, char endchar)
348 {
349 char *ptr = *str;
350 /* the first non-space char is here */
351 char *word_begin = ptr;
352 char *ptr2;
353 char *escape = NULL;
354
355 if(*ptr == '"') {
356 ++ptr;
357 while(*ptr) {
358 if(*ptr == '\\') {
359 if(ptr[1] == '\\' || ptr[1] == '"') {
360 /* remember the first escape position */
361 if(!escape)
362 escape = ptr;
363 /* skip escape of back-slash or double-quote */
364 ptr += 2;
365 continue;
366 }
367 }
368 if(*ptr == '"') {
369 *end_pos = ptr;
370 if(escape) {
371 /* has escape, we restore the unescaped string here */
372 ptr = ptr2 = escape;
373 do {
374 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
375 ++ptr;
376 *ptr2++ = *ptr++;
377 }
378 while(ptr < *end_pos);
379 *end_pos = ptr2;
380 }
381 while(*ptr && *ptr != ';' && *ptr != endchar)
382 ++ptr;
383 *str = ptr;
384 return word_begin + 1;
385 }
386 ++ptr;
387 }
388 /* end quote is missing, treat it as non-quoted. */
389 ptr = word_begin;
390 }
391
392 while(*ptr && *ptr != ';' && *ptr != endchar)
393 ++ptr;
394 *str = *end_pos = ptr;
395 return word_begin;
396 }
397
398 /* Append slist item and return -1 if failed. */
slist_append(struct curl_slist ** plist,const char * data)399 static int slist_append(struct curl_slist **plist, const char *data)
400 {
401 struct curl_slist *s = curl_slist_append(*plist, data);
402
403 if(!s)
404 return -1;
405
406 *plist = s;
407 return 0;
408 }
409
410 /* Read headers from a file and append to list. */
read_field_headers(struct OperationConfig * config,const char * filename,FILE * fp,struct curl_slist ** pheaders)411 static int read_field_headers(struct OperationConfig *config,
412 const char *filename, FILE *fp,
413 struct curl_slist **pheaders)
414 {
415 size_t hdrlen = 0;
416 size_t pos = 0;
417 bool incomment = FALSE;
418 int lineno = 1;
419 char hdrbuf[999]; /* Max. header length + 1. */
420
421 for(;;) {
422 int c = getc(fp);
423 if(c == EOF || (!pos && !ISSPACE(c))) {
424 /* Strip and flush the current header. */
425 while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
426 hdrlen--;
427 if(hdrlen) {
428 hdrbuf[hdrlen] = '\0';
429 if(slist_append(pheaders, hdrbuf)) {
430 fprintf(config->global->errors,
431 "Out of memory for field headers!\n");
432 return -1;
433 }
434 hdrlen = 0;
435 }
436 }
437
438 switch(c) {
439 case EOF:
440 if(ferror(fp)) {
441 fprintf(config->global->errors,
442 "Header file %s read error: %s\n", filename, strerror(errno));
443 return -1;
444 }
445 return 0; /* Done. */
446 case '\r':
447 continue; /* Ignore. */
448 case '\n':
449 pos = 0;
450 incomment = FALSE;
451 lineno++;
452 continue;
453 case '#':
454 if(!pos)
455 incomment = TRUE;
456 break;
457 }
458
459 pos++;
460 if(!incomment) {
461 if(hdrlen == sizeof(hdrbuf) - 1) {
462 warnf(config->global, "File %s line %d: header too long (truncated)\n",
463 filename, lineno);
464 c = ' ';
465 }
466 if(hdrlen <= sizeof(hdrbuf) - 1)
467 hdrbuf[hdrlen++] = (char) c;
468 }
469 }
470 /* NOTREACHED */
471 }
472
get_param_part(struct OperationConfig * config,char endchar,char ** str,char ** pdata,char ** ptype,char ** pfilename,char ** pencoder,struct curl_slist ** pheaders)473 static int get_param_part(struct OperationConfig *config, char endchar,
474 char **str, char **pdata, char **ptype,
475 char **pfilename, char **pencoder,
476 struct curl_slist **pheaders)
477 {
478 char *p = *str;
479 char *type = NULL;
480 char *filename = NULL;
481 char *encoder = NULL;
482 char *endpos;
483 char *tp;
484 char sep;
485 char type_major[128] = "";
486 char type_minor[128] = "";
487 char *endct = NULL;
488 struct curl_slist *headers = NULL;
489
490 if(ptype)
491 *ptype = NULL;
492 if(pfilename)
493 *pfilename = NULL;
494 if(pheaders)
495 *pheaders = NULL;
496 if(pencoder)
497 *pencoder = NULL;
498 while(ISSPACE(*p))
499 p++;
500 tp = p;
501 *pdata = get_param_word(&p, &endpos, endchar);
502 /* If not quoted, strip trailing spaces. */
503 if(*pdata == tp)
504 while(endpos > *pdata && ISSPACE(endpos[-1]))
505 endpos--;
506 sep = *p;
507 *endpos = '\0';
508 while(sep == ';') {
509 while(ISSPACE(*++p))
510 ;
511
512 if(!endct && checkprefix("type=", p)) {
513 for(p += 5; ISSPACE(*p); p++)
514 ;
515 /* set type pointer */
516 type = p;
517
518 /* verify that this is a fine type specifier */
519 if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
520 warnf(config->global, "Illegally formatted content-type field!\n");
521 curl_slist_free_all(headers);
522 return -1; /* illegal content-type syntax! */
523 }
524
525 /* now point beyond the content-type specifier */
526 p = type + strlen(type_major) + strlen(type_minor) + 1;
527 for(endct = p; *p && *p != ';' && *p != endchar; p++)
528 if(!ISSPACE(*p))
529 endct = p + 1;
530 sep = *p;
531 }
532 else if(checkprefix("filename=", p)) {
533 if(endct) {
534 *endct = '\0';
535 endct = NULL;
536 }
537 for(p += 9; ISSPACE(*p); p++)
538 ;
539 tp = p;
540 filename = get_param_word(&p, &endpos, endchar);
541 /* If not quoted, strip trailing spaces. */
542 if(filename == tp)
543 while(endpos > filename && ISSPACE(endpos[-1]))
544 endpos--;
545 sep = *p;
546 *endpos = '\0';
547 }
548 else if(checkprefix("headers=", p)) {
549 if(endct) {
550 *endct = '\0';
551 endct = NULL;
552 }
553 p += 8;
554 if(*p == '@' || *p == '<') {
555 char *hdrfile;
556 FILE *fp;
557 /* Read headers from a file. */
558
559 do {
560 p++;
561 } while(ISSPACE(*p));
562 tp = p;
563 hdrfile = get_param_word(&p, &endpos, endchar);
564 /* If not quoted, strip trailing spaces. */
565 if(hdrfile == tp)
566 while(endpos > hdrfile && ISSPACE(endpos[-1]))
567 endpos--;
568 sep = *p;
569 *endpos = '\0';
570 fp = fopen(hdrfile, FOPEN_READTEXT);
571 if(!fp)
572 warnf(config->global, "Cannot read from %s: %s\n", hdrfile,
573 strerror(errno));
574 else {
575 int i = read_field_headers(config, hdrfile, fp, &headers);
576
577 fclose(fp);
578 if(i) {
579 curl_slist_free_all(headers);
580 return -1;
581 }
582 }
583 }
584 else {
585 char *hdr;
586
587 while(ISSPACE(*p))
588 p++;
589 tp = p;
590 hdr = get_param_word(&p, &endpos, endchar);
591 /* If not quoted, strip trailing spaces. */
592 if(hdr == tp)
593 while(endpos > hdr && ISSPACE(endpos[-1]))
594 endpos--;
595 sep = *p;
596 *endpos = '\0';
597 if(slist_append(&headers, hdr)) {
598 fprintf(config->global->errors, "Out of memory for field header!\n");
599 curl_slist_free_all(headers);
600 return -1;
601 }
602 }
603 }
604 else if(checkprefix("encoder=", p)) {
605 if(endct) {
606 *endct = '\0';
607 endct = NULL;
608 }
609 for(p += 8; ISSPACE(*p); p++)
610 ;
611 tp = p;
612 encoder = get_param_word(&p, &endpos, endchar);
613 /* If not quoted, strip trailing spaces. */
614 if(encoder == tp)
615 while(endpos > encoder && ISSPACE(endpos[-1]))
616 endpos--;
617 sep = *p;
618 *endpos = '\0';
619 }
620 else if(endct) {
621 /* This is part of content type. */
622 for(endct = p; *p && *p != ';' && *p != endchar; p++)
623 if(!ISSPACE(*p))
624 endct = p + 1;
625 sep = *p;
626 }
627 else {
628 /* unknown prefix, skip to next block */
629 char *unknown = get_param_word(&p, &endpos, endchar);
630
631 sep = *p;
632 *endpos = '\0';
633 if(*unknown)
634 warnf(config->global, "skip unknown form field: %s\n", unknown);
635 }
636 }
637
638 /* Terminate content type. */
639 if(endct)
640 *endct = '\0';
641
642 if(ptype)
643 *ptype = type;
644 else if(type)
645 warnf(config->global, "Field content type not allowed here: %s\n", type);
646
647 if(pfilename)
648 *pfilename = filename;
649 else if(filename)
650 warnf(config->global,
651 "Field file name not allowed here: %s\n", filename);
652
653 if(pencoder)
654 *pencoder = encoder;
655 else if(encoder)
656 warnf(config->global,
657 "Field encoder not allowed here: %s\n", encoder);
658
659 if(pheaders)
660 *pheaders = headers;
661 else if(headers) {
662 warnf(config->global,
663 "Field headers not allowed here: %s\n", headers->data);
664 curl_slist_free_all(headers);
665 }
666
667 *str = p;
668 return sep & 0xFF;
669 }
670
671
672 /***************************************************************************
673 *
674 * formparse()
675 *
676 * Reads a 'name=value' parameter and builds the appropriate linked list.
677 *
678 * If the value is of the form '<filename', field data is read from the
679 * given file.
680
681 * Specify files to upload with 'name=@filename', or 'name=@"filename"'
682 * in case the filename contain ',' or ';'. Supports specified
683 * given Content-Type of the files. Such as ';type=<content-type>'.
684 *
685 * If literal_value is set, any initial '@' or '<' in the value string
686 * loses its special meaning, as does any embedded ';type='.
687 *
688 * You may specify more than one file for a single name (field). Specify
689 * multiple files by writing it like:
690 *
691 * 'name=@filename,filename2,filename3'
692 *
693 * or use double-quotes quote the filename:
694 *
695 * 'name=@"filename","filename2","filename3"'
696 *
697 * If you want content-types specified for each too, write them like:
698 *
699 * 'name=@filename;type=image/gif,filename2,filename3'
700 *
701 * If you want custom headers added for a single part, write them in a separate
702 * file and do like this:
703 *
704 * 'name=foo;headers=@headerfile' or why not
705 * 'name=@filemame;headers=@headerfile'
706 *
707 * To upload a file, but to fake the file name that will be included in the
708 * formpost, do like this:
709 *
710 * 'name=@filename;filename=/dev/null' or quote the faked filename like:
711 * 'name=@filename;filename="play, play, and play.txt"'
712 *
713 * If filename/path contains ',' or ';', it must be quoted by double-quotes,
714 * else curl will fail to figure out the correct filename. if the filename
715 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
716 *
717 ***************************************************************************/
718
719 /* Convenience macros for null pointer check. */
720 #define NULL_CHECK(ptr, init, retcode) { \
721 (ptr) = (init); \
722 if(!(ptr)) { \
723 warnf(config->global, "out of memory!\n"); \
724 curl_slist_free_all(headers); \
725 Curl_safefree(contents); \
726 return retcode; \
727 } \
728 }
729 #define SET_TOOL_MIME_PTR(m, field, retcode) { \
730 if(field) \
731 NULL_CHECK((m)->field, strdup(field), retcode); \
732 }
733
formparse(struct OperationConfig * config,const char * input,tool_mime ** mimeroot,tool_mime ** mimecurrent,bool literal_value)734 int formparse(struct OperationConfig *config,
735 const char *input,
736 tool_mime **mimeroot,
737 tool_mime **mimecurrent,
738 bool literal_value)
739 {
740 /* input MUST be a string in the format 'name=contents' and we'll
741 build a linked list with the info */
742 char *name = NULL;
743 char *contents = NULL;
744 char *contp;
745 char *data;
746 char *type = NULL;
747 char *filename = NULL;
748 char *encoder = NULL;
749 struct curl_slist *headers = NULL;
750 tool_mime *part = NULL;
751 CURLcode res;
752
753 /* Allocate the main mime structure if needed. */
754 if(!*mimecurrent) {
755 NULL_CHECK(*mimeroot, tool_mime_new_parts(NULL), 1);
756 *mimecurrent = *mimeroot;
757 }
758
759 /* Make a copy we can overwrite. */
760 NULL_CHECK(contents, strdup(input), 2);
761
762 /* Scan for the end of the name. */
763 contp = strchr(contents, '=');
764 if(contp) {
765 int sep = '\0';
766 if(contp > contents)
767 name = contents;
768 *contp++ = '\0';
769
770 if(*contp == '(' && !literal_value) {
771 /* Starting a multipart. */
772 sep = get_param_part(config, '\0',
773 &contp, &data, &type, NULL, NULL, &headers);
774 if(sep < 0) {
775 Curl_safefree(contents);
776 return 3;
777 }
778 NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4);
779 *mimecurrent = part;
780 part->headers = headers;
781 headers = NULL;
782 SET_TOOL_MIME_PTR(part, type, 5);
783 }
784 else if(!name && !strcmp(contp, ")") && !literal_value) {
785 /* Ending a multipart. */
786 if(*mimecurrent == *mimeroot) {
787 warnf(config->global, "no multipart to terminate!\n");
788 Curl_safefree(contents);
789 return 6;
790 }
791 *mimecurrent = (*mimecurrent)->parent;
792 }
793 else if('@' == contp[0] && !literal_value) {
794
795 /* we use the @-letter to indicate file name(s) */
796
797 tool_mime *subparts = NULL;
798
799 do {
800 /* since this was a file, it may have a content-type specifier
801 at the end too, or a filename. Or both. */
802 ++contp;
803 sep = get_param_part(config, ',', &contp,
804 &data, &type, &filename, &encoder, &headers);
805 if(sep < 0) {
806 Curl_safefree(contents);
807 return 7;
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 NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8);
817 }
818
819 /* Store that file in a part. */
820 NULL_CHECK(part,
821 tool_mime_new_filedata(subparts, data, TRUE, &res), 9);
822 part->headers = headers;
823 headers = NULL;
824 part->config = config->global;
825 if(res == CURLE_READ_ERROR) {
826 /* An error occurred while reading stdin: if read has started,
827 issue the error now. Else, delay it until processed by
828 libcurl. */
829 if(part->size > 0) {
830 warnf(config->global,
831 "error while reading standard input\n");
832 Curl_safefree(contents);
833 return 10;
834 }
835 CONST_SAFEFREE(part->data);
836 part->data = NULL;
837 part->size = -1;
838 res = CURLE_OK;
839 }
840 SET_TOOL_MIME_PTR(part, filename, 11);
841 SET_TOOL_MIME_PTR(part, type, 12);
842 SET_TOOL_MIME_PTR(part, encoder, 13);
843
844 /* *contp could be '\0', so we just check with the delimiter */
845 } while(sep); /* loop if there's another file name */
846 part = (*mimecurrent)->subparts; /* Set name on group. */
847 }
848 else {
849 if(*contp == '<' && !literal_value) {
850 ++contp;
851 sep = get_param_part(config, '\0', &contp,
852 &data, &type, NULL, &encoder, &headers);
853 if(sep < 0) {
854 Curl_safefree(contents);
855 return 14;
856 }
857
858 NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE,
859 &res), 15);
860 part->headers = headers;
861 headers = NULL;
862 part->config = config->global;
863 if(res == CURLE_READ_ERROR) {
864 /* An error occurred while reading stdin: if read has started,
865 issue the error now. Else, delay it until processed by
866 libcurl. */
867 if(part->size > 0) {
868 warnf(config->global,
869 "error while reading standard input\n");
870 Curl_safefree(contents);
871 return 16;
872 }
873 CONST_SAFEFREE(part->data);
874 part->data = NULL;
875 part->size = -1;
876 res = CURLE_OK;
877 }
878 }
879 else {
880 if(literal_value)
881 data = contp;
882 else {
883 sep = get_param_part(config, '\0', &contp,
884 &data, &type, &filename, &encoder, &headers);
885 if(sep < 0) {
886 Curl_safefree(contents);
887 return 17;
888 }
889 }
890
891 NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18);
892 part->headers = headers;
893 headers = NULL;
894 }
895
896 SET_TOOL_MIME_PTR(part, filename, 19);
897 SET_TOOL_MIME_PTR(part, type, 20);
898 SET_TOOL_MIME_PTR(part, encoder, 21);
899
900 if(sep) {
901 *contp = (char) sep;
902 warnf(config->global,
903 "garbage at end of field specification: %s\n", contp);
904 }
905 }
906
907 /* Set part name. */
908 SET_TOOL_MIME_PTR(part, name, 22);
909 }
910 else {
911 warnf(config->global, "Illegally formatted input field!\n");
912 Curl_safefree(contents);
913 return 23;
914 }
915 Curl_safefree(contents);
916 return 0;
917 }
918