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