Files correlati : omento : Aggiornata cUrl alla ultima versione su Github: 7.56.1 git-svn-id: svn://10.65.10.50/branches/R_10_00@24202 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			865 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			865 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/***************************************************************************
 | 
						|
 *                                  _   _ ____  _
 | 
						|
 *  Project                     ___| | | |  _ \| |
 | 
						|
 *                             / __| | | | |_) | |
 | 
						|
 *                            | (__| |_| |  _ <| |___
 | 
						|
 *                             \___|\___/|_| \_\_____|
 | 
						|
 *
 | 
						|
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 | 
						|
 *
 | 
						|
 * This software is licensed as described in the file COPYING, which
 | 
						|
 * you should have received as part of this distribution. The terms
 | 
						|
 * are also available at https://curl.haxx.se/docs/copyright.html.
 | 
						|
 *
 | 
						|
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 | 
						|
 * copies of the Software, and permit persons to whom the Software is
 | 
						|
 * furnished to do so, under the terms of the COPYING file.
 | 
						|
 *
 | 
						|
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | 
						|
 * KIND, either express or implied.
 | 
						|
 *
 | 
						|
 ***************************************************************************/
 | 
						|
#include "tool_setup.h"
 | 
						|
 | 
						|
#include "mime.h"
 | 
						|
#include "strcase.h"
 | 
						|
 | 
						|
#define ENABLE_CURLX_PRINTF
 | 
						|
/* use our own printf() functions */
 | 
						|
#include "curlx.h"
 | 
						|
 | 
						|
#include "tool_cfgable.h"
 | 
						|
#include "tool_convert.h"
 | 
						|
#include "tool_msgs.h"
 | 
						|
#include "tool_binmode.h"
 | 
						|
#include "tool_getparam.h"
 | 
						|
#include "tool_paramhlp.h"
 | 
						|
#include "tool_formparse.h"
 | 
						|
 | 
						|
#include "memdebug.h" /* keep this as LAST include */
 | 
						|
 | 
						|
/* Stdin parameters. */
 | 
						|
typedef struct {
 | 
						|
  char *data;  /* Memory data. */
 | 
						|
  curl_off_t origin;  /* File read origin offset. */
 | 
						|
  curl_off_t size; /* Data size. */
 | 
						|
  curl_off_t curpos; /* Current read position. */
 | 
						|
}  standard_input;
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * helper function to get a word from form param
 | 
						|
 * after call get_parm_word, str either point to string end
 | 
						|
 * or point to any of end chars.
 | 
						|
 */
 | 
						|
static char *get_param_word(char **str, char **end_pos, char endchar)
 | 
						|
{
 | 
						|
  char *ptr = *str;
 | 
						|
  char *word_begin = NULL;
 | 
						|
  char *ptr2;
 | 
						|
  char *escape = NULL;
 | 
						|
 | 
						|
  /* the first non-space char is here */
 | 
						|
  word_begin = ptr;
 | 
						|
  if(*ptr == '"') {
 | 
						|
    ++ptr;
 | 
						|
    while(*ptr) {
 | 
						|
      if(*ptr == '\\') {
 | 
						|
        if(ptr[1] == '\\' || ptr[1] == '"') {
 | 
						|
          /* remember the first escape position */
 | 
						|
          if(!escape)
 | 
						|
            escape = ptr;
 | 
						|
          /* skip escape of back-slash or double-quote */
 | 
						|
          ptr += 2;
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if(*ptr == '"') {
 | 
						|
        *end_pos = ptr;
 | 
						|
        if(escape) {
 | 
						|
          /* has escape, we restore the unescaped string here */
 | 
						|
          ptr = ptr2 = escape;
 | 
						|
          do {
 | 
						|
            if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
 | 
						|
              ++ptr;
 | 
						|
            *ptr2++ = *ptr++;
 | 
						|
          }
 | 
						|
          while(ptr < *end_pos);
 | 
						|
          *end_pos = ptr2;
 | 
						|
        }
 | 
						|
        while(*ptr && *ptr != ';' && *ptr != endchar)
 | 
						|
          ++ptr;
 | 
						|
        *str = ptr;
 | 
						|
        return word_begin + 1;
 | 
						|
      }
 | 
						|
      ++ptr;
 | 
						|
    }
 | 
						|
    /* end quote is missing, treat it as non-quoted. */
 | 
						|
    ptr = word_begin;
 | 
						|
  }
 | 
						|
 | 
						|
  while(*ptr && *ptr != ';' && *ptr != endchar)
 | 
						|
    ++ptr;
 | 
						|
  *str = *end_pos = ptr;
 | 
						|
  return word_begin;
 | 
						|
}
 | 
						|
 | 
						|
/* Append slist item and return -1 if failed. */
 | 
						|
static int slist_append(struct curl_slist **plist, const char *data)
 | 
						|
{
 | 
						|
  struct curl_slist *s = curl_slist_append(*plist, data);
 | 
						|
 | 
						|
  if(!s)
 | 
						|
    return -1;
 | 
						|
 | 
						|
  *plist = s;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Read headers from a file and append to list. */
 | 
						|
static int read_field_headers(struct OperationConfig *config,
 | 
						|
                              const char *filename, FILE *fp,
 | 
						|
                              struct curl_slist **pheaders)
 | 
						|
{
 | 
						|
  size_t hdrlen = 0;
 | 
						|
  size_t pos = 0;
 | 
						|
  int c;
 | 
						|
  bool incomment = FALSE;
 | 
						|
  int lineno = 1;
 | 
						|
  char hdrbuf[999]; /* Max. header length + 1. */
 | 
						|
 | 
						|
  for(;;) {
 | 
						|
    c = getc(fp);
 | 
						|
    if(c == EOF || (!pos && !ISSPACE(c))) {
 | 
						|
      /* Strip and flush the current header. */
 | 
						|
      while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
 | 
						|
        hdrlen--;
 | 
						|
      if(hdrlen) {
 | 
						|
        hdrbuf[hdrlen] = '\0';
 | 
						|
        if(slist_append(pheaders, hdrbuf)) {
 | 
						|
          fprintf(config->global->errors,
 | 
						|
                  "Out of memory for field headers!\n");
 | 
						|
          return -1;
 | 
						|
        }
 | 
						|
        hdrlen = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    switch(c) {
 | 
						|
    case EOF:
 | 
						|
      if(ferror(fp)) {
 | 
						|
        fprintf(config->global->errors,
 | 
						|
                "Header file %s read error: %s\n", filename, strerror(errno));
 | 
						|
        return -1;
 | 
						|
      }
 | 
						|
      return 0;    /* Done. */
 | 
						|
    case '\r':
 | 
						|
      continue;    /* Ignore. */
 | 
						|
    case '\n':
 | 
						|
      pos = 0;
 | 
						|
      incomment = FALSE;
 | 
						|
      lineno++;
 | 
						|
      continue;
 | 
						|
    case '#':
 | 
						|
      if(!pos)
 | 
						|
        incomment = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    pos++;
 | 
						|
    if(!incomment) {
 | 
						|
      if(hdrlen == sizeof hdrbuf - 1) {
 | 
						|
        warnf(config->global, "File %s line %d: header too long (truncated)\n",
 | 
						|
              filename, lineno);
 | 
						|
        c = ' ';
 | 
						|
      }
 | 
						|
      if(hdrlen <= sizeof hdrbuf - 1)
 | 
						|
        hdrbuf[hdrlen++] = (char) c;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /* NOTREACHED */
 | 
						|
}
 | 
						|
 | 
						|
static int get_param_part(struct OperationConfig *config, char endchar,
 | 
						|
                          char **str, char **pdata, char **ptype,
 | 
						|
                          char **pfilename, char **pencoder,
 | 
						|
                          struct curl_slist **pheaders)
 | 
						|
{
 | 
						|
  char *p = *str;
 | 
						|
  char *type = NULL;
 | 
						|
  char *filename = NULL;
 | 
						|
  char *encoder = NULL;
 | 
						|
  char *endpos;
 | 
						|
  char *tp;
 | 
						|
  char sep;
 | 
						|
  char type_major[128] = "";
 | 
						|
  char type_minor[128] = "";
 | 
						|
  char *endct = NULL;
 | 
						|
  struct curl_slist *headers = NULL;
 | 
						|
 | 
						|
  if(ptype)
 | 
						|
    *ptype = NULL;
 | 
						|
  if(pfilename)
 | 
						|
    *pfilename = NULL;
 | 
						|
  if(pheaders)
 | 
						|
    *pheaders = NULL;
 | 
						|
  if(pencoder)
 | 
						|
    *pencoder = NULL;
 | 
						|
  while(ISSPACE(*p))
 | 
						|
    p++;
 | 
						|
  tp = p;
 | 
						|
  *pdata = get_param_word(&p, &endpos, endchar);
 | 
						|
  /* If not quoted, strip trailing spaces. */
 | 
						|
  if(*pdata == tp)
 | 
						|
    while(endpos > *pdata && ISSPACE(endpos[-1]))
 | 
						|
      endpos--;
 | 
						|
  sep = *p;
 | 
						|
  *endpos = '\0';
 | 
						|
  while(sep == ';') {
 | 
						|
    while(ISSPACE(*++p))
 | 
						|
      ;
 | 
						|
 | 
						|
    if(!endct && checkprefix("type=", p)) {
 | 
						|
      for(p += 5; ISSPACE(*p); p++)
 | 
						|
        ;
 | 
						|
      /* set type pointer */
 | 
						|
      type = p;
 | 
						|
 | 
						|
      /* verify that this is a fine type specifier */
 | 
						|
      if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
 | 
						|
        warnf(config->global, "Illegally formatted content-type field!\n");
 | 
						|
        curl_slist_free_all(headers);
 | 
						|
        return -1; /* illegal content-type syntax! */
 | 
						|
      }
 | 
						|
 | 
						|
      /* now point beyond the content-type specifier */
 | 
						|
      p = type + strlen(type_major) + strlen(type_minor) + 1;
 | 
						|
      for(endct = p; *p && *p != ';' && *p != endchar; p++)
 | 
						|
        if(!ISSPACE(*p))
 | 
						|
          endct = p + 1;
 | 
						|
      sep = *p;
 | 
						|
    }
 | 
						|
    else if(checkprefix("filename=", p)) {
 | 
						|
      if(endct) {
 | 
						|
        *endct = '\0';
 | 
						|
        endct = NULL;
 | 
						|
      }
 | 
						|
      for(p += 9; ISSPACE(*p); p++)
 | 
						|
        ;
 | 
						|
      tp = p;
 | 
						|
      filename = get_param_word(&p, &endpos, endchar);
 | 
						|
      /* If not quoted, strip trailing spaces. */
 | 
						|
      if(filename == tp)
 | 
						|
        while(endpos > filename && ISSPACE(endpos[-1]))
 | 
						|
          endpos--;
 | 
						|
      sep = *p;
 | 
						|
      *endpos = '\0';
 | 
						|
    }
 | 
						|
    else if(checkprefix("headers=", p)) {
 | 
						|
      if(endct) {
 | 
						|
        *endct = '\0';
 | 
						|
        endct = NULL;
 | 
						|
      }
 | 
						|
      p += 8;
 | 
						|
      if(*p == '@' || *p == '<') {
 | 
						|
        char *hdrfile;
 | 
						|
        FILE *fp;
 | 
						|
        /* Read headers from a file. */
 | 
						|
 | 
						|
        do {
 | 
						|
          p++;
 | 
						|
        } while(ISSPACE(*p));
 | 
						|
        tp = p;
 | 
						|
        hdrfile = get_param_word(&p, &endpos, endchar);
 | 
						|
        /* If not quoted, strip trailing spaces. */
 | 
						|
        if(hdrfile == tp)
 | 
						|
          while(endpos > hdrfile && ISSPACE(endpos[-1]))
 | 
						|
            endpos--;
 | 
						|
        sep = *p;
 | 
						|
        *endpos = '\0';
 | 
						|
        /* TODO: maybe special fopen for VMS? */
 | 
						|
        fp = fopen(hdrfile, FOPEN_READTEXT);
 | 
						|
        if(!fp)
 | 
						|
          warnf(config->global, "Cannot read from %s: %s\n", hdrfile,
 | 
						|
                strerror(errno));
 | 
						|
        else {
 | 
						|
          int i = read_field_headers(config, hdrfile, fp, &headers);
 | 
						|
 | 
						|
          fclose(fp);
 | 
						|
          if(i) {
 | 
						|
            curl_slist_free_all(headers);
 | 
						|
            return -1;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        char *hdr;
 | 
						|
 | 
						|
        while(ISSPACE(*p))
 | 
						|
          p++;
 | 
						|
        tp = p;
 | 
						|
        hdr = get_param_word(&p, &endpos, endchar);
 | 
						|
        /* If not quoted, strip trailing spaces. */
 | 
						|
        if(hdr == tp)
 | 
						|
          while(endpos > hdr && ISSPACE(endpos[-1]))
 | 
						|
            endpos--;
 | 
						|
        sep = *p;
 | 
						|
        *endpos = '\0';
 | 
						|
        if(slist_append(&headers, hdr)) {
 | 
						|
          fprintf(config->global->errors, "Out of memory for field header!\n");
 | 
						|
          curl_slist_free_all(headers);
 | 
						|
          return -1;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if(checkprefix("encoder=", p)) {
 | 
						|
      if(endct) {
 | 
						|
        *endct = '\0';
 | 
						|
        endct = NULL;
 | 
						|
      }
 | 
						|
      for(p += 8; ISSPACE(*p); p++)
 | 
						|
        ;
 | 
						|
      tp = p;
 | 
						|
      encoder = get_param_word(&p, &endpos, endchar);
 | 
						|
      /* If not quoted, strip trailing spaces. */
 | 
						|
      if(encoder == tp)
 | 
						|
        while(endpos > encoder && ISSPACE(endpos[-1]))
 | 
						|
          endpos--;
 | 
						|
      sep = *p;
 | 
						|
      *endpos = '\0';
 | 
						|
    }
 | 
						|
    else if(endct) {
 | 
						|
      /* This is part of content type. */
 | 
						|
      for(endct = p; *p && *p != ';' && *p != endchar; p++)
 | 
						|
        if(!ISSPACE(*p))
 | 
						|
          endct = p + 1;
 | 
						|
      sep = *p;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      /* unknown prefix, skip to next block */
 | 
						|
      char *unknown = get_param_word(&p, &endpos, endchar);
 | 
						|
 | 
						|
      sep = *p;
 | 
						|
      *endpos = '\0';
 | 
						|
      if(*unknown)
 | 
						|
        warnf(config->global, "skip unknown form field: %s\n", unknown);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Terminate content type. */
 | 
						|
  if(endct)
 | 
						|
    *endct = '\0';
 | 
						|
 | 
						|
  if(ptype)
 | 
						|
    *ptype = type;
 | 
						|
  else if(type)
 | 
						|
    warnf(config->global, "Field content type not allowed here: %s\n", type);
 | 
						|
 | 
						|
  if(pfilename)
 | 
						|
    *pfilename = filename;
 | 
						|
  else if(filename)
 | 
						|
    warnf(config->global,
 | 
						|
          "Field file name not allowed here: %s\n", filename);
 | 
						|
 | 
						|
  if(pencoder)
 | 
						|
    *pencoder = encoder;
 | 
						|
  else if(encoder)
 | 
						|
    warnf(config->global,
 | 
						|
          "Field encoder not allowed here: %s\n", encoder);
 | 
						|
 | 
						|
  if(pheaders)
 | 
						|
    *pheaders = headers;
 | 
						|
  else if(headers) {
 | 
						|
    warnf(config->global,
 | 
						|
          "Field headers not allowed here: %s\n", headers->data);
 | 
						|
    curl_slist_free_all(headers);
 | 
						|
  }
 | 
						|
 | 
						|
  *str = p;
 | 
						|
  return sep & 0xFF;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Mime part callbacks for stdin. */
 | 
						|
static size_t stdin_read(char *buffer, size_t size, size_t nitems, void *arg)
 | 
						|
{
 | 
						|
  standard_input *sip = (standard_input *) arg;
 | 
						|
  curl_off_t bytesleft;
 | 
						|
  (void) size;  /* Always 1: ignored. */
 | 
						|
 | 
						|
  if(sip->curpos >= sip->size)
 | 
						|
    return 0;  /* At eof. */
 | 
						|
  bytesleft = sip->size - sip->curpos;
 | 
						|
  if((curl_off_t) nitems > bytesleft)
 | 
						|
    nitems = (size_t) bytesleft;
 | 
						|
  if(sip->data) {
 | 
						|
    /* Return data from memory. */
 | 
						|
    memcpy(buffer, sip->data + (size_t) sip->curpos, nitems);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* Read from stdin. */
 | 
						|
    nitems = fread(buffer, 1, nitems, stdin);
 | 
						|
  }
 | 
						|
  sip->curpos += nitems;
 | 
						|
  return nitems;
 | 
						|
}
 | 
						|
 | 
						|
static int stdin_seek(void *instream, curl_off_t offset, int whence)
 | 
						|
{
 | 
						|
  standard_input *sip = (standard_input *) instream;
 | 
						|
 | 
						|
  switch(whence) {
 | 
						|
  case SEEK_CUR:
 | 
						|
    offset += sip->curpos;
 | 
						|
    break;
 | 
						|
  case SEEK_END:
 | 
						|
    offset += sip->size;
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  if(offset < 0)
 | 
						|
    return CURL_SEEKFUNC_CANTSEEK;
 | 
						|
  if(!sip->data) {
 | 
						|
    if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
 | 
						|
      return CURL_SEEKFUNC_CANTSEEK;
 | 
						|
  }
 | 
						|
  sip->curpos = offset;
 | 
						|
  return CURL_SEEKFUNC_OK;
 | 
						|
}
 | 
						|
 | 
						|
static void stdin_free(void *ptr)
 | 
						|
{
 | 
						|
  standard_input *sip = (standard_input *) ptr;
 | 
						|
 | 
						|
  Curl_safefree(sip->data);
 | 
						|
  free(sip);
 | 
						|
}
 | 
						|
 | 
						|
/* Set a part's data from a file, taking care about the pseudo filename "-" as
 | 
						|
 * a shortcut to read stdin: if so, use a callback to read OUR stdin (to
 | 
						|
 * workaround Windows DLL file handle caveat).
 | 
						|
 * If stdin is a regular file opened in binary mode, save current offset as
 | 
						|
 * origin for rewind and do not buffer data. Else read to EOF and keep in
 | 
						|
 * memory. In all cases, compute the stdin data size.
 | 
						|
 */
 | 
						|
static CURLcode file_or_stdin(curl_mimepart *part, const char *file)
 | 
						|
{
 | 
						|
  standard_input *sip = NULL;
 | 
						|
  int fd = -1;
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct_stat sbuf;
 | 
						|
 | 
						|
  if(strcmp(file, "-"))
 | 
						|
    return curl_mime_filedata(part, file);
 | 
						|
 | 
						|
  sip = (standard_input *) malloc(sizeof *sip);
 | 
						|
  if(!sip)
 | 
						|
    return CURLE_OUT_OF_MEMORY;
 | 
						|
 | 
						|
  memset((char *) sip, 0, sizeof *sip);
 | 
						|
  set_binmode(stdin);
 | 
						|
 | 
						|
  /* If stdin is a regular file, do not buffer data but read it when needed. */
 | 
						|
  fd = fileno(stdin);
 | 
						|
  sip->origin = ftell(stdin);
 | 
						|
  if(fd >= 0 && sip->origin >= 0 && !fstat(fd, &sbuf) &&
 | 
						|
#ifdef __VMS
 | 
						|
     sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
 | 
						|
#endif
 | 
						|
     S_ISREG(sbuf.st_mode)) {
 | 
						|
    sip->size = sbuf.st_size - sip->origin;
 | 
						|
    if(sip->size < 0)
 | 
						|
      sip->size = 0;
 | 
						|
  }
 | 
						|
  else {  /* Not suitable for direct use, buffer stdin data. */
 | 
						|
    size_t stdinsize = 0;
 | 
						|
 | 
						|
    sip->origin = 0;
 | 
						|
    if(file2memory(&sip->data, &stdinsize, stdin) != PARAM_OK)
 | 
						|
      result = CURLE_OUT_OF_MEMORY;
 | 
						|
    else {
 | 
						|
      if(!stdinsize)
 | 
						|
        sip->data = NULL;  /* Has been freed if no data. */
 | 
						|
      sip->size = stdinsize;
 | 
						|
      if(ferror(stdin))
 | 
						|
        result = CURLE_READ_ERROR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Set remote file name. */
 | 
						|
  if(!result)
 | 
						|
    result = curl_mime_filename(part, file);
 | 
						|
 | 
						|
  /* Set part's data from callback. */
 | 
						|
  if(!result)
 | 
						|
    result = curl_mime_data_cb(part, sip->size,
 | 
						|
                               stdin_read, stdin_seek, stdin_free, sip);
 | 
						|
  if(result)
 | 
						|
    stdin_free(sip);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/***************************************************************************
 | 
						|
 *
 | 
						|
 * formparse()
 | 
						|
 *
 | 
						|
 * Reads a 'name=value' parameter and builds the appropriate linked list.
 | 
						|
 *
 | 
						|
 * Specify files to upload with 'name=@filename', or 'name=@"filename"'
 | 
						|
 * in case the filename contain ',' or ';'. Supports specified
 | 
						|
 * given Content-Type of the files. Such as ';type=<content-type>'.
 | 
						|
 *
 | 
						|
 * If literal_value is set, any initial '@' or '<' in the value string
 | 
						|
 * loses its special meaning, as does any embedded ';type='.
 | 
						|
 *
 | 
						|
 * You may specify more than one file for a single name (field). Specify
 | 
						|
 * multiple files by writing it like:
 | 
						|
 *
 | 
						|
 * 'name=@filename,filename2,filename3'
 | 
						|
 *
 | 
						|
 * or use double-quotes quote the filename:
 | 
						|
 *
 | 
						|
 * 'name=@"filename","filename2","filename3"'
 | 
						|
 *
 | 
						|
 * If you want content-types specified for each too, write them like:
 | 
						|
 *
 | 
						|
 * 'name=@filename;type=image/gif,filename2,filename3'
 | 
						|
 *
 | 
						|
 * If you want custom headers added for a single part, write them in a separate
 | 
						|
 * file and do like this:
 | 
						|
 *
 | 
						|
 * 'name=foo;headers=@headerfile' or why not
 | 
						|
 * 'name=@filemame;headers=@headerfile'
 | 
						|
 *
 | 
						|
 * To upload a file, but to fake the file name that will be included in the
 | 
						|
 * formpost, do like this:
 | 
						|
 *
 | 
						|
 * 'name=@filename;filename=/dev/null' or quote the faked filename like:
 | 
						|
 * 'name=@filename;filename="play, play, and play.txt"'
 | 
						|
 *
 | 
						|
 * If filename/path contains ',' or ';', it must be quoted by double-quotes,
 | 
						|
 * else curl will fail to figure out the correct filename. if the filename
 | 
						|
 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
 | 
						|
 *
 | 
						|
 * This function uses curl_formadd to fulfill it's job. Is heavily based on
 | 
						|
 * the old curl_formparse code.
 | 
						|
 *
 | 
						|
 ***************************************************************************/
 | 
						|
 | 
						|
int formparse(struct OperationConfig *config,
 | 
						|
              const char *input,
 | 
						|
              curl_mime **mimepost,
 | 
						|
              curl_mime **mimecurrent,
 | 
						|
              bool literal_value)
 | 
						|
{
 | 
						|
  /* input MUST be a string in the format 'name=contents' and we'll
 | 
						|
     build a linked list with the info */
 | 
						|
  char *name = NULL;
 | 
						|
  char *contents = NULL;
 | 
						|
  char *contp;
 | 
						|
  char *data;
 | 
						|
  char *type = NULL;
 | 
						|
  char *filename = NULL;
 | 
						|
  char *encoder = NULL;
 | 
						|
  struct curl_slist *headers = NULL;
 | 
						|
  curl_mimepart *part = NULL;
 | 
						|
  CURLcode res;
 | 
						|
  int sep = '\0';
 | 
						|
 | 
						|
  /* Allocate the main mime structure if needed. */
 | 
						|
  if(!*mimepost) {
 | 
						|
    *mimepost = curl_mime_init(config->easy);
 | 
						|
    if(!*mimepost) {
 | 
						|
      warnf(config->global, "curl_mime_init failed!\n");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
    *mimecurrent = *mimepost;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Make a copy we can overwrite. */
 | 
						|
  contents = strdup(input);
 | 
						|
  if(!contents) {
 | 
						|
    fprintf(config->global->errors, "out of memory\n");
 | 
						|
    return 2;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Scan for the end of the name. */
 | 
						|
  contp = strchr(contents, '=');
 | 
						|
  if(contp) {
 | 
						|
    if(contp > contents)
 | 
						|
      name = contents;
 | 
						|
    *contp++ = '\0';
 | 
						|
 | 
						|
    if(*contp == '(' && !literal_value) {
 | 
						|
      curl_mime *subparts;
 | 
						|
 | 
						|
      /* Starting a multipart. */
 | 
						|
      sep = get_param_part(config, '\0',
 | 
						|
                           &contp, &data, &type, NULL, NULL, &headers);
 | 
						|
      if(sep < 0) {
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 3;
 | 
						|
      }
 | 
						|
      subparts = curl_mime_init(config->easy);
 | 
						|
      if(!subparts) {
 | 
						|
        warnf(config->global, "curl_mime_init failed!\n");
 | 
						|
        curl_slist_free_all(headers);
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 4;
 | 
						|
      }
 | 
						|
      part = curl_mime_addpart(*mimecurrent);
 | 
						|
      if(!part) {
 | 
						|
        warnf(config->global, "curl_mime_addpart failed!\n");
 | 
						|
        curl_mime_free(subparts);
 | 
						|
        curl_slist_free_all(headers);
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 5;
 | 
						|
      }
 | 
						|
      if(curl_mime_subparts(part, subparts)) {
 | 
						|
        warnf(config->global, "curl_mime_subparts failed!\n");
 | 
						|
        curl_mime_free(subparts);
 | 
						|
        curl_slist_free_all(headers);
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 6;
 | 
						|
      }
 | 
						|
      *mimecurrent = subparts;
 | 
						|
      if(curl_mime_headers(part, headers, 1)) {
 | 
						|
        warnf(config->global, "curl_mime_headers failed!\n");
 | 
						|
        curl_slist_free_all(headers);
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 7;
 | 
						|
      }
 | 
						|
      if(curl_mime_type(part, type)) {
 | 
						|
        warnf(config->global, "curl_mime_type failed!\n");
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 8;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if(!name && !strcmp(contp, ")") && !literal_value) {
 | 
						|
      /* Ending a mutipart. */
 | 
						|
      if(*mimecurrent == *mimepost) {
 | 
						|
        warnf(config->global, "no multipart to terminate!\n");
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 9;
 | 
						|
        }
 | 
						|
      *mimecurrent = (*mimecurrent)->parent->parent;
 | 
						|
    }
 | 
						|
    else if('@' == contp[0] && !literal_value) {
 | 
						|
 | 
						|
      /* we use the @-letter to indicate file name(s) */
 | 
						|
 | 
						|
      curl_mime *subparts = NULL;
 | 
						|
 | 
						|
      do {
 | 
						|
        /* since this was a file, it may have a content-type specifier
 | 
						|
           at the end too, or a filename. Or both. */
 | 
						|
        ++contp;
 | 
						|
        sep = get_param_part(config, ',', &contp,
 | 
						|
                             &data, &type, &filename, &encoder, &headers);
 | 
						|
        if(sep < 0) {
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 10;
 | 
						|
        }
 | 
						|
 | 
						|
        /* now contp point to comma or string end.
 | 
						|
           If more files to come, make sure we have multiparts. */
 | 
						|
        if(!subparts) {
 | 
						|
          if(sep != ',')    /* If there is a single file. */
 | 
						|
            subparts = *mimecurrent;
 | 
						|
          else {
 | 
						|
            subparts = curl_mime_init(config->easy);
 | 
						|
            if(!subparts) {
 | 
						|
              warnf(config->global, "curl_mime_init failed!\n");
 | 
						|
              curl_slist_free_all(headers);
 | 
						|
              Curl_safefree(contents);
 | 
						|
              return 11;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        /* Allocate a part for that file. */
 | 
						|
        part = curl_mime_addpart(subparts);
 | 
						|
        if(!part) {
 | 
						|
          warnf(config->global, "curl_mime_addpart failed!\n");
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          curl_slist_free_all(headers);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 12;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set part headers. */
 | 
						|
        if(curl_mime_headers(part, headers, 1)) {
 | 
						|
          warnf(config->global, "curl_mime_headers failed!\n");
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          curl_slist_free_all(headers);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 13;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Setup file in part. */
 | 
						|
        res = file_or_stdin(part, data);
 | 
						|
        if(res) {
 | 
						|
          warnf(config->global, "setting file %s  failed!\n", data);
 | 
						|
          if(res != CURLE_READ_ERROR) {
 | 
						|
            if(subparts != *mimecurrent)
 | 
						|
              curl_mime_free(subparts);
 | 
						|
            Curl_safefree(contents);
 | 
						|
            return 14;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        if(filename && curl_mime_filename(part, filename)) {
 | 
						|
          warnf(config->global, "curl_mime_filename failed!\n");
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 15;
 | 
						|
        }
 | 
						|
        if(curl_mime_type(part, type)) {
 | 
						|
          warnf(config->global, "curl_mime_type failed!\n");
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 16;
 | 
						|
        }
 | 
						|
        if(curl_mime_encoder(part, encoder)) {
 | 
						|
          warnf(config->global, "curl_mime_encoder failed!\n");
 | 
						|
          if(subparts != *mimecurrent)
 | 
						|
            curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 17;
 | 
						|
        }
 | 
						|
 | 
						|
        /* *contp could be '\0', so we just check with the delimiter */
 | 
						|
      } while(sep); /* loop if there's another file name */
 | 
						|
 | 
						|
      /* now we add the multiple files section */
 | 
						|
      if(subparts != *mimecurrent) {
 | 
						|
        part = curl_mime_addpart(*mimecurrent);
 | 
						|
        if(!part) {
 | 
						|
          warnf(config->global, "curl_mime_addpart failed!\n");
 | 
						|
          curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 18;
 | 
						|
        }
 | 
						|
        if(curl_mime_subparts(part, subparts)) {
 | 
						|
          warnf(config->global, "curl_mime_subparts failed!\n");
 | 
						|
          curl_mime_free(subparts);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 19;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        /* Allocate a mime part. */
 | 
						|
        part = curl_mime_addpart(*mimecurrent);
 | 
						|
        if(!part) {
 | 
						|
          warnf(config->global, "curl_mime_addpart failed!\n");
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 20;
 | 
						|
        }
 | 
						|
 | 
						|
      if(*contp == '<' && !literal_value) {
 | 
						|
        ++contp;
 | 
						|
        sep = get_param_part(config, '\0', &contp,
 | 
						|
                             &data, &type, NULL, &encoder, &headers);
 | 
						|
        if(sep < 0) {
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 21;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set part headers. */
 | 
						|
        if(curl_mime_headers(part, headers, 1)) {
 | 
						|
          warnf(config->global, "curl_mime_headers failed!\n");
 | 
						|
          curl_slist_free_all(headers);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 22;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Setup file in part. */
 | 
						|
        res = file_or_stdin(part, data);
 | 
						|
        if(res) {
 | 
						|
          warnf(config->global, "setting file %s failed!\n", data);
 | 
						|
          if(res != CURLE_READ_ERROR) {
 | 
						|
            Curl_safefree(contents);
 | 
						|
            return 23;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        if(literal_value)
 | 
						|
          data = contp;
 | 
						|
        else {
 | 
						|
          sep = get_param_part(config, '\0', &contp,
 | 
						|
                               &data, &type, &filename, &encoder, &headers);
 | 
						|
          if(sep < 0) {
 | 
						|
            Curl_safefree(contents);
 | 
						|
            return 24;
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set part headers. */
 | 
						|
        if(curl_mime_headers(part, headers, 1)) {
 | 
						|
          warnf(config->global, "curl_mime_headers failed!\n");
 | 
						|
          curl_slist_free_all(headers);
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 25;
 | 
						|
        }
 | 
						|
 | 
						|
#ifdef CURL_DOES_CONVERSIONS
 | 
						|
        if(convert_to_network(data, strlen(data))) {
 | 
						|
          warnf(config->global, "curl_formadd failed!\n");
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 26;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
        if(curl_mime_data(part, data, CURL_ZERO_TERMINATED)) {
 | 
						|
          warnf(config->global, "curl_mime_data failed!\n");
 | 
						|
          Curl_safefree(contents);
 | 
						|
          return 27;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if(curl_mime_filename(part, filename)) {
 | 
						|
        warnf(config->global, "curl_mime_filename failed!\n");
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 28;
 | 
						|
      }
 | 
						|
      if(curl_mime_type(part, type)) {
 | 
						|
        warnf(config->global, "curl_mime_type failed!\n");
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 29;
 | 
						|
      }
 | 
						|
      if(curl_mime_encoder(part, encoder)) {
 | 
						|
        warnf(config->global, "curl_mime_encoder failed!\n");
 | 
						|
        Curl_safefree(contents);
 | 
						|
        return 30;
 | 
						|
      }
 | 
						|
 | 
						|
      if(sep) {
 | 
						|
        *contp = (char) sep;
 | 
						|
        warnf(config->global,
 | 
						|
              "garbage at end of field specification: %s\n", contp);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Set part name. */
 | 
						|
    if(name && curl_mime_name(part, name)) {
 | 
						|
      warnf(config->global, "curl_mime_name failed!\n");
 | 
						|
      Curl_safefree(contents);
 | 
						|
      return 31;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    warnf(config->global, "Illegally formatted input field!\n");
 | 
						|
    Curl_safefree(contents);
 | 
						|
    return 32;
 | 
						|
  }
 | 
						|
  Curl_safefree(contents);
 | 
						|
  return 0;
 | 
						|
}
 |