| /*************************************************************************** |
| * _ _ ____ _ |
| * 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; |
| } |