#include "orconfig.h"
#include "lib/container/smartlist.h"
#include "lib/encoding/confline.h"
#include "lib/encoding/cstring.h"
#include "lib/encoding/kvline.h"
#include "lib/encoding/qstring.h"
#include "lib/malloc/malloc.h"
#include "lib/string/compat_ctype.h"
#include "lib/string/printf.h"
#include "lib/string/util_string.h"
#include "lib/log/escape.h"
#include "lib/log/util_bug.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
static bool
needs_escape(const char *s, bool as_keyless_val)
{
if (as_keyless_val && *s == 0)
return true;
if (as_keyless_val && strchr(s, '='))
return true;
for (; *s; ++s) {
if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) ||
*s == '\'' || *s == '\"') {
return true;
}
}
return false;
}
static bool
line_has_no_key(const config_line_t *line)
{
return line->key == NULL || strlen(line->key) == 0;
}
static bool
line_has_no_val(const config_line_t *line)
{
return line->value == NULL || strlen(line->value) == 0;
}
static bool
kvline_can_encode_lines(const config_line_t *line, unsigned flags)
{
for ( ; line; line = line->next) {
const bool keyless = line_has_no_key(line);
if (keyless && ! (flags & KV_OMIT_KEYS)) {
return false;
}
if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) {
return false;
}
if (!keyless && needs_escape(line->key, true)) {
return false;
}
}
return true;
}
char *
kvline_encode(const config_line_t *line,
unsigned flags)
{
tor_assert(! (flags & KV_QUOTED_QSTRING));
tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
(KV_OMIT_KEYS|KV_OMIT_VALS));
tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW));
if (!kvline_can_encode_lines(line, flags))
return NULL;
smartlist_t *elements = smartlist_new();
for (; line; line = line->next) {
const char *k = "";
const char *eq = "=";
const char *v = "";
const bool keyless = line_has_no_key(line);
bool esc = needs_escape(line->value, keyless);
char *tmp = NULL;
if (! keyless) {
k = line->key;
} else {
eq = "";
}
if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
eq = "";
v = "";
} else if (!(flags & KV_RAW) && esc) {
tmp = esc_for_log(line->value);
v = tmp;
} else {
v = line->value;
}
smartlist_add_asprintf(elements, "%s%s%s", k, eq, v);
tor_free(tmp);
}
char *result = smartlist_join_strings(elements, " ", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
return result;
}
config_line_t *
kvline_parse(const char *line, unsigned flags)
{
tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
(KV_OMIT_KEYS|KV_OMIT_VALS));
tor_assert(!(flags & KV_RAW));
const char *cp = line, *cplast = NULL;
const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
const bool c_quoted = (flags & (KV_QUOTED)) != 0;
config_line_t *result = NULL;
config_line_t **next_line = &result;
char *key = NULL;
char *val = NULL;
while (*cp) {
key = val = NULL;
{
size_t idx = strspn(cp, " \t\r\v\n");
cp += idx;
}
if (BUG(cp == cplast)) {
goto err; }
cplast = cp;
if (! *cp)
break;
if (*cp != '\"') {
size_t idx = strcspn(cp, " \t\r\v\n=");
if (cp[idx] == '=') {
key = tor_memdup_nulterm(cp, idx);
cp += idx + 1;
} else if (omit_vals) {
key = tor_memdup_nulterm(cp, idx);
cp += idx;
goto commit;
} else {
if (!omit_keys)
goto err;
}
}
if (*cp == '\"') {
if (!quoted)
goto err;
size_t len=0;
if (c_quoted) {
cp = unescape_string(cp, &val, &len);
} else {
cp = decode_qstring(cp, strlen(cp), &val, &len);
}
if (cp == NULL || len != strlen(val)) {
goto err;
}
} else {
size_t idx = strcspn(cp, " \t\r\v\n");
val = tor_memdup_nulterm(cp, idx);
cp += idx;
}
commit:
if (key && strlen(key) == 0) {
goto err;
}
*next_line = tor_malloc_zero(sizeof(config_line_t));
(*next_line)->key = key ? key : tor_strdup("");
(*next_line)->value = val ? val : tor_strdup("");
next_line = &(*next_line)->next;
key = val = NULL;
}
if (! (flags & KV_QUOTED_QSTRING)) {
if (!kvline_can_encode_lines(result, flags)) {
goto err;
}
}
return result;
err:
tor_free(key);
tor_free(val);
config_free_lines(result);
return NULL;
}