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