diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/config.c b/config.c new file mode 100644 index 0000000..bd7f822 --- /dev/null +++ b/config.c @@ -0,0 +1,115 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include "config.h" +#include "global.h" + + +struct config_data { + size_t num_apikeys; + char **apikeys; + struct apikey_perm *apikey_perms; +}; + +static struct config_data config_data; + +static void add_apikey(char *key, struct apikey_perm perm) { + // This is slow, but optimisation is not worth it + config_data.num_apikeys++; + config_data.apikeys = realloc(config_data.apikeys, config_data.num_apikeys, char*); + config_data.apikey_perms = + realloc(config_data.apikey_perms, config_data.num_apikeys, struct apikey_perm); + + config_data.apikeys[config_data.num_apikeys - 1] = key; + config_data.apikey_perms[config_data.num_apikeys - 1] = perm; +} + +struct slice { + const char *s; + size_t len; +}; + +static struct slice next_word(struct slice *line) { + size_t cursor = 0; + while (cursor < line->len && isspace(line->s[cursor])) cursor++; + const size_t start = cursor; + while (cursor < line->len && !isspace(line->s[cursor])) cursor++; + const struct slice word = (struct slice){line->s + start, cursor - start}; + line->s += cursor; + line->len -= cursor; + return word; +} + +static void apply_config_line(const char *linebuf, size_t linelen) { + struct slice line = (struct slice){linebuf, linelen}; + struct slice cmd = next_word(&line); + if (cmd.len == 0) return; + + if (cmd.len == 6 && memcmp(cmd.s, "apikey", 6) == 0) { + struct slice key = next_word(&line); + struct slice pbits = next_word(&line); + if (line.len > 0) die("Too many fields on 'apikey' line"); + + for (size_t i = 0; i < pbits.len; i++) { + if (pbits.s[i] != '0' && pbits.s[i] != '1') { + die("Invalid permission bit '%c' on 'apikey' line", pbits.s[i]); + } + } + + if (pbits.len != 1) die("Incorrect permission bit vector length on 'apikey' line"); + struct apikey_perm perm; + perm.sendat = pbits.s[0] == '1'; + + for (size_t i = 0; i < key.len; i++) { + if (key.s[i] == '\0') die("Invalid null byte in key on 'apikey' line"); + } + + char *keystr = malloc(key.len + 1, char); + memcpy(keystr, key.s, key.len); + keystr[key.len] = '\0'; + + add_apikey(keystr, perm); + } else { + char *cmdstr = malloc(cmd.len + 1, char); + memcpy(cmdstr, cmd.s, cmd.len); + cmdstr[cmd.len] = '\0'; + die("Unknown command '%s' in config file", cmdstr); + } +} + +void config_init(const char *filename) { + FILE *f = fopen(filename, "r"); + if (!f) die("Cannot open config file '%s'", filename); + + char *line = NULL; + size_t linecap = 0; + ssize_t nr; + while ((nr = getline(&line, &linecap, f)) >= 0) { + if (line[0] == '#') continue; + if (nr > 0 && line[nr - 1] == '\n') nr--; + if (nr > 0 && line[nr - 1] == '\r') nr--; + apply_config_line(line, nr); + } + free(line); + + if (!feof(f) && ferror(f)) { + die("Error reading config file '%s': %s", filename, strerror(errno)); + } + + fprintf(stderr, "config: Registered %zu API key%s\n", + config_data.num_apikeys, config_data.num_apikeys == 1 ? "" : "s"); +} + +struct apikey_perm config_check_apikey(const char *apikey) { + for (size_t i = 0; i < config_data.num_apikeys; i++) { + if (strcmp(config_data.apikeys[i], apikey) == 0) { + return config_data.apikey_perms[i]; + } + } + + // Unknown key means no permissions + struct apikey_perm perm; + memset(&perm, 0, sizeof perm); + return perm; +} |