quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

var.c (14470B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@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.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  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 #include "tool_setup.h"
     25 
     26 #include "tool_cfgable.h"
     27 #include "tool_getparam.h"
     28 #include "tool_helpers.h"
     29 #include "tool_findfile.h"
     30 #include "tool_msgs.h"
     31 #include "tool_parsecfg.h"
     32 #include "tool_paramhlp.h"
     33 #include "tool_writeout_json.h"
     34 #include "var.h"
     35 #include "memdebug.h" /* keep this as LAST include */
     36 
     37 #define MAX_EXPAND_CONTENT 10000000
     38 #define MAX_VAR_LEN 128 /* max length of a name */
     39 
     40 static char *Memdup(const char *data, size_t len)
     41 {
     42   char *p = malloc(len + 1);
     43   if(!p)
     44     return NULL;
     45   if(len)
     46     memcpy(p, data, len);
     47   p[len] = 0;
     48   return p;
     49 }
     50 
     51 /* free everything */
     52 void varcleanup(struct GlobalConfig *global)
     53 {
     54   struct tool_var *list = global->variables;
     55   while(list) {
     56     struct tool_var *t = list;
     57     list = list->next;
     58     free(CURL_UNCONST(t->content));
     59     free(t);
     60   }
     61 }
     62 
     63 static const struct tool_var *varcontent(struct GlobalConfig *global,
     64                                          const char *name, size_t nlen)
     65 {
     66   struct tool_var *list = global->variables;
     67   while(list) {
     68     if((strlen(list->name) == nlen) &&
     69        !strncmp(name, list->name, nlen)) {
     70       return list;
     71     }
     72     list = list->next;
     73   }
     74   return NULL;
     75 }
     76 
     77 #define ENDOFFUNC(x) (((x) == '}') || ((x) == ':'))
     78 #define FUNCMATCH(ptr,name,len)                         \
     79   (!strncmp(ptr, name, len) && ENDOFFUNC(ptr[len]))
     80 
     81 #define FUNC_TRIM "trim"
     82 #define FUNC_TRIM_LEN (sizeof(FUNC_TRIM) - 1)
     83 #define FUNC_JSON "json"
     84 #define FUNC_JSON_LEN (sizeof(FUNC_JSON) - 1)
     85 #define FUNC_URL "url"
     86 #define FUNC_URL_LEN (sizeof(FUNC_URL) - 1)
     87 #define FUNC_B64 "b64"
     88 #define FUNC_B64_LEN (sizeof(FUNC_B64) - 1)
     89 #define FUNC_64DEC "64dec" /* base64 decode */
     90 #define FUNC_64DEC_LEN (sizeof(FUNC_64DEC) - 1)
     91 
     92 static ParameterError varfunc(struct GlobalConfig *global,
     93                               char *c, /* content */
     94                               size_t clen, /* content length */
     95                               char *f, /* functions */
     96                               size_t flen, /* function string length */
     97                               struct dynbuf *out)
     98 {
     99   bool alloc = FALSE;
    100   ParameterError err = PARAM_OK;
    101   const char *finput = f;
    102 
    103   /* The functions are independent and runs left to right */
    104   while(*f && !err) {
    105     if(*f == '}')
    106       /* end of functions */
    107       break;
    108     /* On entry, this is known to be a colon already. In subsequent laps, it
    109        is also known to be a colon since that is part of the FUNCMATCH()
    110        checks */
    111     f++;
    112     if(FUNCMATCH(f, FUNC_TRIM, FUNC_TRIM_LEN)) {
    113       size_t len = clen;
    114       f += FUNC_TRIM_LEN;
    115       if(clen) {
    116         /* skip leading white space, including CRLF */
    117         while(ISSPACE(*c)) {
    118           c++;
    119           len--;
    120         }
    121         while(len && ISSPACE(c[len-1]))
    122           len--;
    123       }
    124       /* put it in the output */
    125       curlx_dyn_reset(out);
    126       if(curlx_dyn_addn(out, c, len)) {
    127         err = PARAM_NO_MEM;
    128         break;
    129       }
    130     }
    131     else if(FUNCMATCH(f, FUNC_JSON, FUNC_JSON_LEN)) {
    132       f += FUNC_JSON_LEN;
    133       curlx_dyn_reset(out);
    134       if(clen) {
    135         if(jsonquoted(c, clen, out, FALSE)) {
    136           err = PARAM_NO_MEM;
    137           break;
    138         }
    139       }
    140     }
    141     else if(FUNCMATCH(f, FUNC_URL, FUNC_URL_LEN)) {
    142       f += FUNC_URL_LEN;
    143       curlx_dyn_reset(out);
    144       if(clen) {
    145         char *enc = curl_easy_escape(NULL, c, (int)clen);
    146         if(!enc) {
    147           err = PARAM_NO_MEM;
    148           break;
    149         }
    150 
    151         /* put it in the output */
    152         if(curlx_dyn_add(out, enc))
    153           err = PARAM_NO_MEM;
    154         curl_free(enc);
    155         if(err)
    156           break;
    157       }
    158     }
    159     else if(FUNCMATCH(f, FUNC_B64, FUNC_B64_LEN)) {
    160       f += FUNC_B64_LEN;
    161       curlx_dyn_reset(out);
    162       if(clen) {
    163         char *enc;
    164         size_t elen;
    165         CURLcode result = curlx_base64_encode(c, clen, &enc, &elen);
    166         if(result) {
    167           err = PARAM_NO_MEM;
    168           break;
    169         }
    170 
    171         /* put it in the output */
    172         if(curlx_dyn_addn(out, enc, elen))
    173           err = PARAM_NO_MEM;
    174         curl_free(enc);
    175         if(err)
    176           break;
    177       }
    178     }
    179     else if(FUNCMATCH(f, FUNC_64DEC, FUNC_64DEC_LEN)) {
    180       f += FUNC_64DEC_LEN;
    181       curlx_dyn_reset(out);
    182       if(clen) {
    183         unsigned char *enc;
    184         size_t elen;
    185         CURLcode result = curlx_base64_decode(c, &enc, &elen);
    186         /* put it in the output */
    187         if(result) {
    188           if(curlx_dyn_add(out, "[64dec-fail]"))
    189             err = PARAM_NO_MEM;
    190         }
    191         else {
    192           if(curlx_dyn_addn(out, enc, elen))
    193             err = PARAM_NO_MEM;
    194           curl_free(enc);
    195         }
    196         if(err)
    197           break;
    198       }
    199     }
    200     else {
    201       /* unsupported function */
    202       errorf(global, "unknown variable function in '%.*s'",
    203              (int)flen, finput);
    204       err = PARAM_EXPAND_ERROR;
    205       break;
    206     }
    207     if(alloc)
    208       free(c);
    209 
    210     clen = curlx_dyn_len(out);
    211     c = Memdup(curlx_dyn_ptr(out), clen);
    212     if(!c) {
    213       err = PARAM_NO_MEM;
    214       break;
    215     }
    216     alloc = TRUE;
    217   }
    218   if(alloc)
    219     free(c);
    220   if(err)
    221     curlx_dyn_free(out);
    222   return err;
    223 }
    224 
    225 ParameterError varexpand(struct GlobalConfig *global,
    226                          const char *line, struct dynbuf *out,
    227                          bool *replaced)
    228 {
    229   CURLcode result;
    230   char *envp;
    231   bool added = FALSE;
    232   const char *input = line;
    233   *replaced = FALSE;
    234   curlx_dyn_init(out, MAX_EXPAND_CONTENT);
    235   do {
    236     envp = strstr(line, "{{");
    237     if((envp > line) && envp[-1] == '\\') {
    238       /* preceding backslash, we want this verbatim */
    239 
    240       /* insert the text up to this point, minus the backslash */
    241       result = curlx_dyn_addn(out, line, envp - line - 1);
    242       if(result)
    243         return PARAM_NO_MEM;
    244 
    245       /* output '{{' then continue from here */
    246       result = curlx_dyn_addn(out, "{{", 2);
    247       if(result)
    248         return PARAM_NO_MEM;
    249       line = &envp[2];
    250     }
    251     else if(envp) {
    252       char name[MAX_VAR_LEN];
    253       size_t nlen;
    254       size_t i;
    255       char *funcp;
    256       char *clp = strstr(envp, "}}");
    257       size_t prefix;
    258 
    259       if(!clp) {
    260         /* uneven braces */
    261         warnf(global, "missing close '}}' in '%s'", input);
    262         break;
    263       }
    264 
    265       prefix = 2;
    266       envp += 2; /* move over the {{ */
    267 
    268       /* if there is a function, it ends the name with a colon */
    269       funcp = memchr(envp, ':', clp - envp);
    270       if(funcp)
    271         nlen = funcp - envp;
    272       else
    273         nlen = clp - envp;
    274       if(!nlen || (nlen >= sizeof(name))) {
    275         warnf(global, "bad variable name length '%s'", input);
    276         /* insert the text as-is since this is not an env variable */
    277         result = curlx_dyn_addn(out, line, clp - line + prefix);
    278         if(result)
    279           return PARAM_NO_MEM;
    280       }
    281       else {
    282         /* insert the text up to this point */
    283         result = curlx_dyn_addn(out, line, envp - prefix - line);
    284         if(result)
    285           return PARAM_NO_MEM;
    286 
    287         /* copy the name to separate buffer */
    288         memcpy(name, envp, nlen);
    289         name[nlen] = 0;
    290 
    291         /* verify that the name looks sensible */
    292         for(i = 0; (i < nlen) &&
    293               (ISALNUM(name[i]) || (name[i] == '_')); i++);
    294         if(i != nlen) {
    295           warnf(global, "bad variable name: %s", name);
    296           /* insert the text as-is since this is not an env variable */
    297           result = curlx_dyn_addn(out, envp - prefix,
    298                                   clp - envp + prefix + 2);
    299           if(result)
    300             return PARAM_NO_MEM;
    301         }
    302         else {
    303           char *value;
    304           size_t vlen = 0;
    305           struct dynbuf buf;
    306           const struct tool_var *v = varcontent(global, name, nlen);
    307           if(v) {
    308             value = (char *)CURL_UNCONST(v->content);
    309             vlen = v->clen;
    310           }
    311           else
    312             value = NULL;
    313 
    314           curlx_dyn_init(&buf, MAX_EXPAND_CONTENT);
    315           if(funcp) {
    316             /* apply the list of functions on the value */
    317             size_t flen = clp - funcp;
    318             ParameterError err = varfunc(global, value, vlen, funcp, flen,
    319                                          &buf);
    320             if(err)
    321               return err;
    322             value = curlx_dyn_ptr(&buf);
    323             vlen = curlx_dyn_len(&buf);
    324           }
    325 
    326           if(value && vlen > 0) {
    327             /* A variable might contain null bytes. Such bytes cannot be shown
    328                using normal means, this is an error. */
    329             char *nb = memchr(value, '\0', vlen);
    330             if(nb) {
    331               errorf(global, "variable contains null byte");
    332               return PARAM_EXPAND_ERROR;
    333             }
    334           }
    335           /* insert the value */
    336           result = curlx_dyn_addn(out, value, vlen);
    337           curlx_dyn_free(&buf);
    338           if(result)
    339             return PARAM_NO_MEM;
    340 
    341           added = true;
    342         }
    343       }
    344       line = &clp[2];
    345     }
    346 
    347   } while(envp);
    348   if(added && *line) {
    349     /* add the "suffix" as well */
    350     result = curlx_dyn_add(out, line);
    351     if(result)
    352       return PARAM_NO_MEM;
    353   }
    354   *replaced = added;
    355   if(!added)
    356     curlx_dyn_free(out);
    357   return PARAM_OK;
    358 }
    359 
    360 /*
    361  * Created in a way that is not revealing how variables are actually stored so
    362  * that we can improve this if we want better performance when managing many
    363  * at a later point.
    364  */
    365 static ParameterError addvariable(struct GlobalConfig *global,
    366                                   const char *name,
    367                                   size_t nlen,
    368                                   const char *content,
    369                                   size_t clen,
    370                                   bool contalloc)
    371 {
    372   struct tool_var *p;
    373   const struct tool_var *check = varcontent(global, name, nlen);
    374   DEBUGASSERT(nlen);
    375   if(check)
    376     notef(global, "Overwriting variable '%s'", check->name);
    377 
    378   p = calloc(1, sizeof(struct tool_var) + nlen);
    379   if(p) {
    380     memcpy(p->name, name, nlen);
    381 
    382     p->content = contalloc ? content : Memdup(content, clen);
    383     if(p->content) {
    384       p->clen = clen;
    385 
    386       p->next = global->variables;
    387       global->variables = p;
    388       return PARAM_OK;
    389     }
    390     free(p);
    391   }
    392   return PARAM_NO_MEM;
    393 }
    394 
    395 #define MAX_FILENAME 10000
    396 
    397 ParameterError setvariable(struct GlobalConfig *global,
    398                            const char *input)
    399 {
    400   const char *name;
    401   size_t nlen;
    402   char *content = NULL;
    403   size_t clen = 0;
    404   bool contalloc = FALSE;
    405   const char *line = input;
    406   ParameterError err = PARAM_OK;
    407   bool import = FALSE;
    408   char *ge = NULL;
    409   char buf[MAX_VAR_LEN];
    410   curl_off_t startoffset = 0;
    411   curl_off_t endoffset = CURL_OFF_T_MAX;
    412 
    413   if(*input == '%') {
    414     import = TRUE;
    415     line++;
    416   }
    417   name = line;
    418   while(*line && (ISALNUM(*line) || (*line == '_')))
    419     line++;
    420   nlen = line - name;
    421   if(!nlen || (nlen >= MAX_VAR_LEN)) {
    422     warnf(global, "Bad variable name length (%zd), skipping", nlen);
    423     return PARAM_OK;
    424   }
    425   if(import) {
    426     /* this does not use curl_getenv() because we want "" support for blank
    427        content */
    428     if(*line) {
    429       /* if there is a default action, we need to copy the name */
    430       memcpy(buf, name, nlen);
    431       buf[nlen] = 0;
    432       name = buf;
    433     }
    434     ge = getenv(name);
    435     if(!*line && !ge) {
    436       /* no assign, no variable, fail */
    437       errorf(global, "Variable '%s' import fail, not set", name);
    438       return PARAM_EXPAND_ERROR;
    439     }
    440     else if(ge) {
    441       /* there is a value to use */
    442       content = ge;
    443       clen = strlen(ge);
    444     }
    445   }
    446   if(*line == '[' && ISDIGIT(line[1])) {
    447     /* is there a byte range specified? [num-num] */
    448     line++;
    449     if(curlx_str_number(&line, &startoffset, CURL_OFF_T_MAX) ||
    450        curlx_str_single(&line, '-'))
    451       return PARAM_VAR_SYNTAX;
    452     if(curlx_str_single(&line, ']')) {
    453       if(curlx_str_number(&line, &endoffset, CURL_OFF_T_MAX) ||
    454          curlx_str_single(&line, ']'))
    455         return PARAM_VAR_SYNTAX;
    456     }
    457     if(startoffset > endoffset)
    458       return PARAM_VAR_SYNTAX;
    459   }
    460   if(content)
    461     ;
    462   else if(*line == '@') {
    463     /* read from file or stdin */
    464     FILE *file;
    465     bool use_stdin;
    466     struct dynbuf fname;
    467     line++;
    468 
    469     curlx_dyn_init(&fname, MAX_FILENAME);
    470 
    471     use_stdin = !strcmp(line, "-");
    472     if(use_stdin)
    473       file = stdin;
    474     else {
    475       file = fopen(line, "rb");
    476       if(!file) {
    477         errorf(global, "Failed to open %s: %s", line,
    478                strerror(errno));
    479         err = PARAM_READ_ERROR;
    480       }
    481     }
    482     if(!err) {
    483       err = file2memory_range(&content, &clen, file, startoffset, endoffset);
    484       /* in case of out of memory, this should fail the entire operation */
    485       if(clen)
    486         contalloc = TRUE;
    487     }
    488     curlx_dyn_free(&fname);
    489     if(!use_stdin && file)
    490       fclose(file);
    491     if(err)
    492       return err;
    493   }
    494   else if(*line == '=') {
    495     line++;
    496     clen = strlen(line);
    497     /* this is the exact content */
    498     content = (char *)CURL_UNCONST(line);
    499     if(startoffset || (endoffset != CURL_OFF_T_MAX)) {
    500       if(startoffset >= (curl_off_t)clen)
    501         clen = 0;
    502       else {
    503         /* make the end offset no larger than the last byte */
    504         if(endoffset >= (curl_off_t)clen)
    505           endoffset = clen - 1;
    506         clen = (size_t)(endoffset - startoffset) + 1;
    507         content += startoffset;
    508       }
    509     }
    510   }
    511   else {
    512     warnf(global, "Bad --variable syntax, skipping: %s", input);
    513     return PARAM_OK;
    514   }
    515   err = addvariable(global, name, nlen, content, clen, contalloc);
    516   if(err) {
    517     if(contalloc)
    518       free(content);
    519   }
    520   return err;
    521 }