quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

tool_formparse.c (25106B)


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