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