#include "tool_setup.h"
#include "tool_cfgable.h"
#include "tool_getparam.h"
#include "tool_helpers.h"
#include "tool_findfile.h"
#include "tool_msgs.h"
#include "tool_parsecfg.h"
#include "tool_paramhlp.h"
#include "tool_writeout_json.h"
#include "tool_strdup.h"
#include "var.h"
#include "memdebug.h"
#define MAX_EXPAND_CONTENT 10000000
#define MAX_VAR_LEN 128
void varcleanup(void)
{
struct tool_var *list = global->variables;
while(list) {
struct tool_var *t = list;
list = list->next;
free(CURL_UNCONST(t->content));
free(t);
}
}
static const struct tool_var *varcontent(const char *name, size_t nlen)
{
struct tool_var *list = global->variables;
while(list) {
if((strlen(list->name) == nlen) &&
!strncmp(name, list->name, nlen)) {
return list;
}
list = list->next;
}
return NULL;
}
#define ENDOFFUNC(x) (((x) == '}') || ((x) == ':'))
#define FUNCMATCH(ptr,name,len) \
(!strncmp(ptr, name, len) && ENDOFFUNC(ptr[len]))
#define FUNC_TRIM "trim"
#define FUNC_TRIM_LEN (sizeof(FUNC_TRIM) - 1)
#define FUNC_JSON "json"
#define FUNC_JSON_LEN (sizeof(FUNC_JSON) - 1)
#define FUNC_URL "url"
#define FUNC_URL_LEN (sizeof(FUNC_URL) - 1)
#define FUNC_B64 "b64"
#define FUNC_B64_LEN (sizeof(FUNC_B64) - 1)
#define FUNC_64DEC "64dec"
#define FUNC_64DEC_LEN (sizeof(FUNC_64DEC) - 1)
static ParameterError varfunc(char *c,
size_t clen,
char *f,
size_t flen,
struct dynbuf *out)
{
bool alloc = FALSE;
ParameterError err = PARAM_OK;
const char *finput = f;
while(*f && !err) {
if(*f == '}')
break;
f++;
if(FUNCMATCH(f, FUNC_TRIM, FUNC_TRIM_LEN)) {
size_t len = clen;
f += FUNC_TRIM_LEN;
if(clen) {
while(ISSPACE(*c)) {
c++;
len--;
}
while(len && ISSPACE(c[len-1]))
len--;
}
curlx_dyn_reset(out);
if(curlx_dyn_addn(out, c, len)) {
err = PARAM_NO_MEM;
break;
}
}
else if(FUNCMATCH(f, FUNC_JSON, FUNC_JSON_LEN)) {
f += FUNC_JSON_LEN;
curlx_dyn_reset(out);
if(clen) {
if(jsonquoted(c, clen, out, FALSE)) {
err = PARAM_NO_MEM;
break;
}
}
}
else if(FUNCMATCH(f, FUNC_URL, FUNC_URL_LEN)) {
f += FUNC_URL_LEN;
curlx_dyn_reset(out);
if(clen) {
char *enc = curl_easy_escape(NULL, c, (int)clen);
if(!enc) {
err = PARAM_NO_MEM;
break;
}
if(curlx_dyn_add(out, enc))
err = PARAM_NO_MEM;
curl_free(enc);
if(err)
break;
}
}
else if(FUNCMATCH(f, FUNC_B64, FUNC_B64_LEN)) {
f += FUNC_B64_LEN;
curlx_dyn_reset(out);
if(clen) {
char *enc;
size_t elen;
CURLcode result = curlx_base64_encode(c, clen, &enc, &elen);
if(result) {
err = PARAM_NO_MEM;
break;
}
if(curlx_dyn_addn(out, enc, elen))
err = PARAM_NO_MEM;
curl_free(enc);
if(err)
break;
}
}
else if(FUNCMATCH(f, FUNC_64DEC, FUNC_64DEC_LEN)) {
f += FUNC_64DEC_LEN;
curlx_dyn_reset(out);
if(clen) {
unsigned char *enc;
size_t elen;
CURLcode result = curlx_base64_decode(c, &enc, &elen);
if(result) {
if(curlx_dyn_add(out, "[64dec-fail]"))
err = PARAM_NO_MEM;
}
else {
if(curlx_dyn_addn(out, enc, elen))
err = PARAM_NO_MEM;
curl_free(enc);
}
if(err)
break;
}
}
else {
errorf("unknown variable function in '%.*s'", (int)flen, finput);
err = PARAM_EXPAND_ERROR;
break;
}
if(alloc)
free(c);
clen = curlx_dyn_len(out);
c = memdup0(curlx_dyn_ptr(out), clen);
if(!c) {
err = PARAM_NO_MEM;
break;
}
alloc = TRUE;
}
if(alloc)
free(c);
if(err)
curlx_dyn_free(out);
return err;
}
ParameterError varexpand(const char *line, struct dynbuf *out,
bool *replaced)
{
CURLcode result;
char *envp;
bool added = FALSE;
const char *input = line;
*replaced = FALSE;
curlx_dyn_init(out, MAX_EXPAND_CONTENT);
do {
envp = strstr(line, "{{");
if((envp > line) && envp[-1] == '\\') {
result = curlx_dyn_addn(out, line, envp - line - 1);
if(result)
return PARAM_NO_MEM;
result = curlx_dyn_addn(out, "{{", 2);
if(result)
return PARAM_NO_MEM;
line = &envp[2];
}
else if(envp) {
char name[MAX_VAR_LEN];
size_t nlen;
size_t i;
char *funcp;
char *clp = strstr(envp, "}}");
size_t prefix;
if(!clp) {
warnf("missing close '}}' in '%s'", input);
break;
}
prefix = 2;
envp += 2;
funcp = memchr(envp, ':', clp - envp);
if(funcp)
nlen = funcp - envp;
else
nlen = clp - envp;
if(!nlen || (nlen >= sizeof(name))) {
warnf("bad variable name length '%s'", input);
result = curlx_dyn_addn(out, line, clp - line + prefix);
if(result)
return PARAM_NO_MEM;
}
else {
result = curlx_dyn_addn(out, line, envp - prefix - line);
if(result)
return PARAM_NO_MEM;
memcpy(name, envp, nlen);
name[nlen] = 0;
for(i = 0; (i < nlen) &&
(ISALNUM(name[i]) || (name[i] == '_')); i++);
if(i != nlen) {
warnf("bad variable name: %s", name);
result = curlx_dyn_addn(out, envp - prefix,
clp - envp + prefix + 2);
if(result)
return PARAM_NO_MEM;
}
else {
char *value;
size_t vlen = 0;
struct dynbuf buf;
const struct tool_var *v = varcontent(name, nlen);
if(v) {
value = (char *)CURL_UNCONST(v->content);
vlen = v->clen;
}
else
value = NULL;
curlx_dyn_init(&buf, MAX_EXPAND_CONTENT);
if(funcp) {
size_t flen = clp - funcp;
ParameterError err = varfunc(value, vlen, funcp, flen, &buf);
if(err)
return err;
value = curlx_dyn_ptr(&buf);
vlen = curlx_dyn_len(&buf);
}
if(value && vlen > 0) {
char *nb = memchr(value, '\0', vlen);
if(nb) {
errorf("variable contains null byte");
return PARAM_EXPAND_ERROR;
}
}
result = curlx_dyn_addn(out, value, vlen);
curlx_dyn_free(&buf);
if(result)
return PARAM_NO_MEM;
added = true;
}
}
line = &clp[2];
}
} while(envp);
if(added && *line) {
result = curlx_dyn_add(out, line);
if(result)
return PARAM_NO_MEM;
}
*replaced = added;
if(!added)
curlx_dyn_free(out);
return PARAM_OK;
}
static ParameterError addvariable(const char *name,
size_t nlen,
const char *content,
size_t clen,
bool contalloc)
{
struct tool_var *p;
const struct tool_var *check = varcontent(name, nlen);
DEBUGASSERT(nlen);
if(check)
notef("Overwriting variable '%s'", check->name);
p = calloc(1, sizeof(struct tool_var) + nlen);
if(p) {
memcpy(p->name, name, nlen);
p->content = contalloc ? content : memdup0(content, clen);
if(p->content) {
p->clen = clen;
p->next = global->variables;
global->variables = p;
return PARAM_OK;
}
free(p);
}
return PARAM_NO_MEM;
}
#define MAX_FILENAME 10000
ParameterError setvariable(const char *input)
{
const char *name;
size_t nlen;
char *content = NULL;
size_t clen = 0;
bool contalloc = FALSE;
const char *line = input;
ParameterError err = PARAM_OK;
bool import = FALSE;
char *ge = NULL;
char buf[MAX_VAR_LEN];
curl_off_t startoffset = 0;
curl_off_t endoffset = CURL_OFF_T_MAX;
if(*input == '%') {
import = TRUE;
line++;
}
name = line;
while(*line && (ISALNUM(*line) || (*line == '_')))
line++;
nlen = line - name;
if(!nlen || (nlen >= MAX_VAR_LEN)) {
warnf("Bad variable name length (%zd), skipping", nlen);
return PARAM_OK;
}
if(import) {
if(*line) {
memcpy(buf, name, nlen);
buf[nlen] = 0;
name = buf;
}
ge = getenv(name);
if(!*line && !ge) {
errorf("Variable '%s' import fail, not set", name);
return PARAM_EXPAND_ERROR;
}
else if(ge) {
content = ge;
clen = strlen(ge);
}
}
if(*line == '[' && ISDIGIT(line[1])) {
line++;
if(curlx_str_number(&line, &startoffset, CURL_OFF_T_MAX) ||
curlx_str_single(&line, '-'))
return PARAM_VAR_SYNTAX;
if(curlx_str_single(&line, ']')) {
if(curlx_str_number(&line, &endoffset, CURL_OFF_T_MAX) ||
curlx_str_single(&line, ']'))
return PARAM_VAR_SYNTAX;
}
if(startoffset > endoffset)
return PARAM_VAR_SYNTAX;
}
if(content)
;
else if(*line == '@') {
FILE *file;
bool use_stdin;
line++;
use_stdin = !strcmp(line, "-");
if(use_stdin)
file = stdin;
else {
file = curlx_fopen(line, "rb");
if(!file) {
char errbuf[STRERROR_LEN];
errorf("Failed to open %s: %s", line,
curlx_strerror(errno, errbuf, sizeof(errbuf)));
err = PARAM_READ_ERROR;
}
}
if(!err) {
err = file2memory_range(&content, &clen, file, startoffset, endoffset);
if(clen)
contalloc = TRUE;
}
if(!use_stdin && file)
curlx_fclose(file);
if(err)
return err;
}
else if(*line == '=') {
line++;
clen = strlen(line);
content = (char *)CURL_UNCONST(line);
if(startoffset || (endoffset != CURL_OFF_T_MAX)) {
if(startoffset >= (curl_off_t)clen)
clen = 0;
else {
if(endoffset >= (curl_off_t)clen)
endoffset = clen - 1;
clen = (size_t)(endoffset - startoffset) + 1;
content += startoffset;
}
}
}
else {
warnf("Bad --variable syntax, skipping: %s", input);
return PARAM_OK;
}
err = addvariable(name, nlen, content, clen, contalloc);
if(err) {
if(contalloc)
free(content);
}
return err;
}