var.c 12 KB

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