Home | History | Annotate | Download | only in src
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2018, 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 #define ENABLE_CURLX_PRINTF
     25 /* use our own printf() functions */
     26 #include "curlx.h"
     27 
     28 #include "tool_cfgable.h"
     29 #include "tool_getparam.h"
     30 #include "tool_helpers.h"
     31 #include "tool_homedir.h"
     32 #include "tool_msgs.h"
     33 #include "tool_parsecfg.h"
     34 
     35 #include "memdebug.h" /* keep this as LAST include */
     36 
     37 #define CURLRC DOT_CHAR "curlrc"
     38 
     39 /* only acknowledge colon or equals as separators if the option was not
     40    specified with an initial dash! */
     41 #define ISSEP(x,dash) (!dash && (((x) == '=') || ((x) == ':')))
     42 
     43 static const char *unslashquote(const char *line, char *param);
     44 static char *my_get_line(FILE *fp);
     45 
     46 /* return 0 on everything-is-fine, and non-zero otherwise */
     47 int parseconfig(const char *filename, struct GlobalConfig *global)
     48 {
     49   FILE *file;
     50   char filebuffer[512];
     51   bool usedarg = FALSE;
     52   int rc = 0;
     53   struct OperationConfig *operation = global->first;
     54 
     55   if(!filename || !*filename) {
     56     /* NULL or no file name attempts to load .curlrc from the homedir! */
     57 
     58 #ifndef __AMIGA__
     59     char *home = homedir();    /* portable homedir finder */
     60     filename = CURLRC;   /* sensible default */
     61     if(home) {
     62       if(strlen(home) < (sizeof(filebuffer) - strlen(CURLRC))) {
     63         msnprintf(filebuffer, sizeof(filebuffer),
     64                   "%s%s%s", home, DIR_CHAR, CURLRC);
     65 
     66 #ifdef WIN32
     67         /* Check if the file exists - if not, try CURLRC in the same
     68          * directory as our executable
     69          */
     70         file = fopen(filebuffer, FOPEN_READTEXT);
     71         if(file != NULL) {
     72           fclose(file);
     73           filename = filebuffer;
     74         }
     75         else {
     76           /* Get the filename of our executable. GetModuleFileName is
     77            * already declared via inclusions done in setup header file.
     78            * We assume that we are using the ASCII version here.
     79            */
     80           int n = GetModuleFileNameA(0, filebuffer, sizeof(filebuffer));
     81           if(n > 0 && n < (int)sizeof(filebuffer)) {
     82             /* We got a valid filename - get the directory part */
     83             char *lastdirchar = strrchr(filebuffer, '\\');
     84             if(lastdirchar) {
     85               size_t remaining;
     86               *lastdirchar = 0;
     87               /* If we have enough space, build the RC filename */
     88               remaining = sizeof(filebuffer) - strlen(filebuffer);
     89               if(strlen(CURLRC) < remaining - 1) {
     90                 msnprintf(lastdirchar, remaining,
     91                           "%s%s", DIR_CHAR, CURLRC);
     92                 /* Don't bother checking if it exists - we do that later */
     93                 filename = filebuffer;
     94               }
     95             }
     96           }
     97         }
     98 #else /* WIN32 */
     99         filename = filebuffer;
    100 #endif /* WIN32 */
    101       }
    102       Curl_safefree(home); /* we've used it, now free it */
    103     }
    104 
    105 # else /* __AMIGA__ */
    106     /* On AmigaOS all the config files are into env:
    107      */
    108     filename = "ENV:" CURLRC;
    109 
    110 #endif
    111   }
    112 
    113   if(strcmp(filename, "-"))
    114     file = fopen(filename, FOPEN_READTEXT);
    115   else
    116     file = stdin;
    117 
    118   if(file) {
    119     char *line;
    120     char *aline;
    121     char *option;
    122     char *param;
    123     int lineno = 0;
    124     bool dashed_option;
    125 
    126     while(NULL != (aline = my_get_line(file))) {
    127       int res;
    128       bool alloced_param = FALSE;
    129       lineno++;
    130       line = aline;
    131 
    132       /* line with # in the first non-blank column is a comment! */
    133       while(*line && ISSPACE(*line))
    134         line++;
    135 
    136       switch(*line) {
    137       case '#':
    138       case '/':
    139       case '\r':
    140       case '\n':
    141       case '*':
    142       case '\0':
    143         Curl_safefree(aline);
    144         continue;
    145       }
    146 
    147       /* the option keywords starts here */
    148       option = line;
    149 
    150       /* the option starts with a dash? */
    151       dashed_option = option[0]=='-'?TRUE:FALSE;
    152 
    153       while(*line && !ISSPACE(*line) && !ISSEP(*line, dashed_option))
    154         line++;
    155       /* ... and has ended here */
    156 
    157       if(*line)
    158         *line++ = '\0'; /* zero terminate, we have a local copy of the data */
    159 
    160 #ifdef DEBUG_CONFIG
    161       fprintf(stderr, "GOT: %s\n", option);
    162 #endif
    163 
    164       /* pass spaces and separator(s) */
    165       while(*line && (ISSPACE(*line) || ISSEP(*line, dashed_option)))
    166         line++;
    167 
    168       /* the parameter starts here (unless quoted) */
    169       if(*line == '\"') {
    170         /* quoted parameter, do the quote dance */
    171         line++;
    172         param = malloc(strlen(line) + 1); /* parameter */
    173         if(!param) {
    174           /* out of memory */
    175           Curl_safefree(aline);
    176           rc = 1;
    177           break;
    178         }
    179         alloced_param = TRUE;
    180         (void)unslashquote(line, param);
    181       }
    182       else {
    183         param = line; /* parameter starts here */
    184         while(*line && !ISSPACE(*line))
    185           line++;
    186 
    187         if(*line) {
    188           *line = '\0'; /* zero terminate */
    189 
    190           /* to detect mistakes better, see if there's data following */
    191           line++;
    192           /* pass all spaces */
    193           while(*line && ISSPACE(*line))
    194             line++;
    195 
    196           switch(*line) {
    197           case '\0':
    198           case '\r':
    199           case '\n':
    200           case '#': /* comment */
    201             break;
    202           default:
    203             warnf(operation->global, "%s:%d: warning: '%s' uses unquoted "
    204                   "white space in the line that may cause side-effects!\n",
    205                   filename, lineno, option);
    206           }
    207         }
    208         if(!*param)
    209           /* do this so getparameter can check for required parameters.
    210              Otherwise it always thinks there's a parameter. */
    211           param = NULL;
    212       }
    213 
    214 #ifdef DEBUG_CONFIG
    215       fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
    216 #endif
    217       res = getparameter(option, param, &usedarg, global, operation);
    218 
    219       if(!res && param && *param && !usedarg)
    220         /* we passed in a parameter that wasn't used! */
    221         res = PARAM_GOT_EXTRA_PARAMETER;
    222 
    223       if(res == PARAM_NEXT_OPERATION) {
    224         if(operation->url_list && operation->url_list->url) {
    225           /* Allocate the next config */
    226           operation->next = malloc(sizeof(struct OperationConfig));
    227           if(operation->next) {
    228             /* Initialise the newly created config */
    229             config_init(operation->next);
    230 
    231             /* Copy the easy handle */
    232             operation->next->easy = global->easy;
    233 
    234             /* Set the global config pointer */
    235             operation->next->global = global;
    236 
    237             /* Update the last operation pointer */
    238             global->last = operation->next;
    239 
    240             /* Move onto the new config */
    241             operation->next->prev = operation;
    242             operation = operation->next;
    243           }
    244           else
    245             res = PARAM_NO_MEM;
    246         }
    247       }
    248 
    249       if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) {
    250         /* the help request isn't really an error */
    251         if(!strcmp(filename, "-")) {
    252           filename = "<stdin>";
    253         }
    254         if(res != PARAM_HELP_REQUESTED &&
    255            res != PARAM_MANUAL_REQUESTED &&
    256            res != PARAM_VERSION_INFO_REQUESTED &&
    257            res != PARAM_ENGINES_REQUESTED) {
    258           const char *reason = param2text(res);
    259           warnf(operation->global, "%s:%d: warning: '%s' %s\n",
    260                 filename, lineno, option, reason);
    261         }
    262       }
    263 
    264       if(alloced_param)
    265         Curl_safefree(param);
    266 
    267       Curl_safefree(aline);
    268     }
    269     if(file != stdin)
    270       fclose(file);
    271   }
    272   else
    273     rc = 1; /* couldn't open the file */
    274 
    275   return rc;
    276 }
    277 
    278 /*
    279  * Copies the string from line to the buffer at param, unquoting
    280  * backslash-quoted characters and NUL-terminating the output string.
    281  * Stops at the first non-backslash-quoted double quote character or the
    282  * end of the input string. param must be at least as long as the input
    283  * string.  Returns the pointer after the last handled input character.
    284  */
    285 static const char *unslashquote(const char *line, char *param)
    286 {
    287   while(*line && (*line != '\"')) {
    288     if(*line == '\\') {
    289       char out;
    290       line++;
    291 
    292       /* default is to output the letter after the backslash */
    293       switch(out = *line) {
    294       case '\0':
    295         continue; /* this'll break out of the loop */
    296       case 't':
    297         out = '\t';
    298         break;
    299       case 'n':
    300         out = '\n';
    301         break;
    302       case 'r':
    303         out = '\r';
    304         break;
    305       case 'v':
    306         out = '\v';
    307         break;
    308       }
    309       *param++ = out;
    310       line++;
    311     }
    312     else
    313       *param++ = *line++;
    314   }
    315   *param = '\0'; /* always zero terminate */
    316   return line;
    317 }
    318 
    319 /*
    320  * Reads a line from the given file, ensuring is NUL terminated.
    321  * The pointer must be freed by the caller.
    322  * NULL is returned on an out of memory condition.
    323  */
    324 static char *my_get_line(FILE *fp)
    325 {
    326   char buf[4096];
    327   char *nl = NULL;
    328   char *line = NULL;
    329 
    330   do {
    331     if(NULL == fgets(buf, sizeof(buf), fp))
    332       break;
    333     if(!line) {
    334       line = strdup(buf);
    335       if(!line)
    336         return NULL;
    337     }
    338     else {
    339       char *ptr;
    340       size_t linelen = strlen(line);
    341       ptr = realloc(line, linelen + strlen(buf) + 1);
    342       if(!ptr) {
    343         Curl_safefree(line);
    344         return NULL;
    345       }
    346       line = ptr;
    347       strcpy(&line[linelen], buf);
    348     }
    349     nl = strchr(line, '\n');
    350   } while(!nl);
    351 
    352   if(nl)
    353     *nl = '\0';
    354 
    355   return line;
    356 }
    357