tool_formparse.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 #include "tool_setup.h"
00023 
00024 #include "strcase.h"
00025 
00026 #define ENABLE_CURLX_PRINTF
00027 /* use our own printf() functions */
00028 #include "curlx.h"
00029 
00030 #include "tool_cfgable.h"
00031 #include "tool_mfiles.h"
00032 #include "tool_msgs.h"
00033 #include "tool_formparse.h"
00034 
00035 #include "memdebug.h" /* keep this as LAST include */
00036 
00037 
00038 /*
00039  * helper function to get a word from form param
00040  * after call get_parm_word, str either point to string end
00041  * or point to any of end chars.
00042  */
00043 static char *get_param_word(char **str, char **end_pos)
00044 {
00045   char *ptr = *str;
00046   char *word_begin = NULL;
00047   char *ptr2;
00048   char *escape = NULL;
00049   const char *end_chars = ";,";
00050 
00051   /* the first non-space char is here */
00052   word_begin = ptr;
00053   if(*ptr == '"') {
00054     ++ptr;
00055     while(*ptr) {
00056       if(*ptr == '\\') {
00057         if(ptr[1] == '\\' || ptr[1] == '"') {
00058           /* remember the first escape position */
00059           if(!escape)
00060             escape = ptr;
00061           /* skip escape of back-slash or double-quote */
00062           ptr += 2;
00063           continue;
00064         }
00065       }
00066       if(*ptr == '"') {
00067         *end_pos = ptr;
00068         if(escape) {
00069           /* has escape, we restore the unescaped string here */
00070           ptr = ptr2 = escape;
00071           do {
00072             if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
00073               ++ptr;
00074             *ptr2++ = *ptr++;
00075           }
00076           while(ptr < *end_pos);
00077           *end_pos = ptr2;
00078         }
00079         while(*ptr && NULL==strchr(end_chars, *ptr))
00080           ++ptr;
00081         *str = ptr;
00082         return word_begin+1;
00083       }
00084       ++ptr;
00085     }
00086     /* end quote is missing, treat it as non-quoted. */
00087     ptr = word_begin;
00088   }
00089 
00090   while(*ptr && NULL==strchr(end_chars, *ptr))
00091     ++ptr;
00092   *str = *end_pos = ptr;
00093   return word_begin;
00094 }
00095 
00096 /***************************************************************************
00097  *
00098  * formparse()
00099  *
00100  * Reads a 'name=value' parameter and builds the appropriate linked list.
00101  *
00102  * Specify files to upload with 'name=@filename', or 'name=@"filename"'
00103  * in case the filename contain ',' or ';'. Supports specified
00104  * given Content-Type of the files. Such as ';type=<content-type>'.
00105  *
00106  * If literal_value is set, any initial '@' or '<' in the value string
00107  * loses its special meaning, as does any embedded ';type='.
00108  *
00109  * You may specify more than one file for a single name (field). Specify
00110  * multiple files by writing it like:
00111  *
00112  * 'name=@filename,filename2,filename3'
00113  *
00114  * or use double-quotes quote the filename:
00115  *
00116  * 'name=@"filename","filename2","filename3"'
00117  *
00118  * If you want content-types specified for each too, write them like:
00119  *
00120  * 'name=@filename;type=image/gif,filename2,filename3'
00121  *
00122  * If you want custom headers added for a single part, write them in a separate
00123  * file and do like this:
00124  *
00125  * 'name=foo;headers=@headerfile' or why not
00126  * 'name=@filemame;headers=@headerfile'
00127  *
00128  * To upload a file, but to fake the file name that will be included in the
00129  * formpost, do like this:
00130  *
00131  * 'name=@filename;filename=/dev/null' or quote the faked filename like:
00132  * 'name=@filename;filename="play, play, and play.txt"'
00133  *
00134  * If filename/path contains ',' or ';', it must be quoted by double-quotes,
00135  * else curl will fail to figure out the correct filename. if the filename
00136  * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
00137  *
00138  * This function uses curl_formadd to fulfill it's job. Is heavily based on
00139  * the old curl_formparse code.
00140  *
00141  ***************************************************************************/
00142 
00143 int formparse(struct OperationConfig *config,
00144               const char *input,
00145               struct curl_httppost **httppost,
00146               struct curl_httppost **last_post,
00147               bool literal_value)
00148 {
00149   /* nextarg MUST be a string in the format 'name=contents' and we'll
00150      build a linked list with the info */
00151   char name[256];
00152   char *contents = NULL;
00153   char type_major[128] = "";
00154   char type_minor[128] = "";
00155   char *contp;
00156   const char *type = NULL;
00157   char *sep;
00158 
00159   if((1 == sscanf(input, "%255[^=]=", name)) &&
00160      ((contp = strchr(input, '=')) != NULL)) {
00161     /* the input was using the correct format */
00162 
00163     /* Allocate the contents */
00164     contents = strdup(contp+1);
00165     if(!contents) {
00166       fprintf(config->global->errors, "out of memory\n");
00167       return 1;
00168     }
00169     contp = contents;
00170 
00171     if('@' == contp[0] && !literal_value) {
00172 
00173       /* we use the @-letter to indicate file name(s) */
00174 
00175       struct multi_files *multi_start = NULL;
00176       struct multi_files *multi_current = NULL;
00177 
00178       char *ptr = contp;
00179       char *end = ptr + strlen(ptr);
00180 
00181       do {
00182         /* since this was a file, it may have a content-type specifier
00183            at the end too, or a filename. Or both. */
00184         char *filename = NULL;
00185         char *word_end;
00186         bool semicolon;
00187 
00188         type = NULL;
00189 
00190         ++ptr;
00191         contp = get_param_word(&ptr, &word_end);
00192         semicolon = (';' == *ptr) ? TRUE : FALSE;
00193         *word_end = '\0'; /* terminate the contp */
00194 
00195         /* have other content, continue parse */
00196         while(semicolon) {
00197           /* have type or filename field */
00198           ++ptr;
00199           while(*ptr && (ISSPACE(*ptr)))
00200             ++ptr;
00201 
00202           if(checkprefix("type=", ptr)) {
00203             /* set type pointer */
00204             type = &ptr[5];
00205 
00206             /* verify that this is a fine type specifier */
00207             if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
00208                            type_major, type_minor)) {
00209               warnf(config->global,
00210                     "Illegally formatted content-type field!\n");
00211               Curl_safefree(contents);
00212               FreeMultiInfo(&multi_start, &multi_current);
00213               return 2; /* illegal content-type syntax! */
00214             }
00215 
00216             /* now point beyond the content-type specifier */
00217             sep = (char *)type + strlen(type_major)+strlen(type_minor)+1;
00218 
00219             /* there's a semicolon following - we check if it is a filename
00220                specified and if not we simply assume that it is text that
00221                the user wants included in the type and include that too up
00222                to the next sep. */
00223             ptr = sep;
00224             if(*sep==';') {
00225               if(!checkprefix(";filename=", sep)) {
00226                 ptr = sep + 1;
00227                 (void)get_param_word(&ptr, &sep);
00228                 semicolon = (';' == *ptr) ? TRUE : FALSE;
00229               }
00230             }
00231             else
00232               semicolon = FALSE;
00233 
00234             if(*sep)
00235               *sep = '\0'; /* zero terminate type string */
00236           }
00237           else if(checkprefix("filename=", ptr)) {
00238             ptr += 9;
00239             filename = get_param_word(&ptr, &word_end);
00240             semicolon = (';' == *ptr) ? TRUE : FALSE;
00241             *word_end = '\0';
00242           }
00243           else {
00244             /* unknown prefix, skip to next block */
00245             char *unknown = NULL;
00246             unknown = get_param_word(&ptr, &word_end);
00247             semicolon = (';' == *ptr) ? TRUE : FALSE;
00248             if(*unknown) {
00249               *word_end = '\0';
00250               warnf(config->global, "skip unknown form field: %s\n", unknown);
00251             }
00252           }
00253         }
00254         /* now ptr point to comma or string end */
00255 
00256 
00257         /* if type == NULL curl_formadd takes care of the problem */
00258 
00259         if(*contp && !AddMultiFiles(contp, type, filename, &multi_start,
00260                           &multi_current)) {
00261           warnf(config->global, "Error building form post!\n");
00262           Curl_safefree(contents);
00263           FreeMultiInfo(&multi_start, &multi_current);
00264           return 3;
00265         }
00266 
00267         /* *ptr could be '\0', so we just check with the string end */
00268       } while(ptr < end); /* loop if there's another file name */
00269 
00270       /* now we add the multiple files section */
00271       if(multi_start) {
00272         struct curl_forms *forms = NULL;
00273         struct multi_files *start = multi_start;
00274         unsigned int i, count = 0;
00275         while(start) {
00276           start = start->next;
00277           ++count;
00278         }
00279         forms = malloc((count+1)*sizeof(struct curl_forms));
00280         if(!forms) {
00281           fprintf(config->global->errors, "Error building form post!\n");
00282           Curl_safefree(contents);
00283           FreeMultiInfo(&multi_start, &multi_current);
00284           return 4;
00285         }
00286         for(i = 0, start = multi_start; i < count; ++i, start = start->next) {
00287           forms[i].option = start->form.option;
00288           forms[i].value = start->form.value;
00289         }
00290         forms[count].option = CURLFORM_END;
00291         FreeMultiInfo(&multi_start, &multi_current);
00292         if(curl_formadd(httppost, last_post,
00293                         CURLFORM_COPYNAME, name,
00294                         CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
00295           warnf(config->global, "curl_formadd failed!\n");
00296           Curl_safefree(forms);
00297           Curl_safefree(contents);
00298           return 5;
00299         }
00300         Curl_safefree(forms);
00301       }
00302     }
00303     else {
00304       struct curl_forms info[4];
00305       int i = 0;
00306       char *ct = literal_value ? NULL : strstr(contp, ";type=");
00307 
00308       info[i].option = CURLFORM_COPYNAME;
00309       info[i].value = name;
00310       i++;
00311 
00312       if(ct) {
00313         info[i].option = CURLFORM_CONTENTTYPE;
00314         info[i].value = &ct[6];
00315         i++;
00316         ct[0] = '\0'; /* zero terminate here */
00317       }
00318 
00319       if(contp[0]=='<' && !literal_value) {
00320         info[i].option = CURLFORM_FILECONTENT;
00321         info[i].value = contp+1;
00322         i++;
00323         info[i].option = CURLFORM_END;
00324 
00325         if(curl_formadd(httppost, last_post,
00326                         CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
00327           warnf(config->global, "curl_formadd failed, possibly the file %s is "
00328                 "bad!\n", contp + 1);
00329           Curl_safefree(contents);
00330           return 6;
00331         }
00332       }
00333       else {
00334 #ifdef CURL_DOES_CONVERSIONS
00335         if(convert_to_network(contp, strlen(contp))) {
00336           warnf(config->global, "curl_formadd failed!\n");
00337           Curl_safefree(contents);
00338           return 7;
00339         }
00340 #endif
00341         info[i].option = CURLFORM_COPYCONTENTS;
00342         info[i].value = contp;
00343         i++;
00344         info[i].option = CURLFORM_END;
00345         if(curl_formadd(httppost, last_post,
00346                         CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
00347           warnf(config->global, "curl_formadd failed!\n");
00348           Curl_safefree(contents);
00349           return 8;
00350         }
00351       }
00352     }
00353 
00354   }
00355   else {
00356     warnf(config->global, "Illegally formatted input field!\n");
00357     return 1;
00358   }
00359   Curl_safefree(contents);
00360   return 0;
00361 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:07