application/config.c

changeset 6
09ac07345656
child 7
905ac52c910f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/application/config.c	Mon Jan 29 10:41:00 2024 +0100
@@ -0,0 +1,328 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <cx/hash_map.h>
+#include <cx/utils.h>
+#include <errno.h>
+#include <libxml/tree.h>
+
+#include "pwd.h"
+#include "config.h"
+#include "system.h"
+
+#include <libidav/utils.h>
+#include <libidav/config.h>
+
+#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
+#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)
+
+#define print_error(lineno, ...) \
+    do {\
+        fprintf(stderr, "Error (config.xml line %u): ", lineno); \
+        fprintf(stderr, __VA_ARGS__); \
+        fprintf(stderr, "Abort.\n"); \
+    } while(0);
+#define print_warning(lineno, ...) \
+    do {\
+        fprintf(stderr, "Warning (config.xml line %u): ", lineno); \
+        fprintf(stderr, __VA_ARGS__); \
+    } while(0);
+
+#ifdef _WIN32
+#define ENV_HOME getenv("USERPROFILE")
+#else
+#define ENV_HOME getenv("HOME")
+#endif /* _WIN32 */
+
+static CxMap* repos;
+static CxMap* keys;
+
+static DavConfig* davconfig;
+static PwdStore* pstore;
+
+static char* secretstore_unlock_cmd;
+static char* secretstore_lock_cmd;
+
+int check_config_dir(void) {
+    char* file = util_concat_path(ENV_HOME, ".dav");
+    int ret = 0;
+    if (util_mkdir(file, S_IRWXU)) {
+        if (errno != EEXIST) {
+            ret = 1;
+        }
+    }
+    free(file);
+    return ret;
+}
+
+static DavContext* context;
+
+void create_default_config(char* file) {
+    xmlDoc* doc = xmlNewDoc(BAD_CAST "1.0");
+    xmlNode* root = xmlNewNode(NULL, BAD_CAST "configuration");
+    xmlDocSetRootElement(doc, root);
+    xmlSaveFormatFileEnc(file, doc, "UTF-8", 1);
+    xmlFreeDoc(doc);
+}
+
+char* config_file_path(char* name) {
+    char* davd = util_concat_path(ENV_HOME, ".dav");
+    if (!davd) {
+        return NULL;
+    }
+    char* path = util_concat_path(davd, name);
+    free(davd);
+    return path;
+}
+
+cxmutstr config_load_file(const char* path) {
+    FILE* file = sys_fopen(path, "r");
+    if (!file) {
+        return (cxmutstr) { NULL, 0 };
+    }
+
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
+    cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
+    fclose(file);
+
+    return cx_mutstrn(buf.space, buf.size);
+}
+
+int load_config(DavContext* ctx) {
+    context = ctx;
+    // TODO: free the config somewhere
+    repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+
+    char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
+    pstore = pwdstore_open(pwfile);
+    free(pwfile);
+
+    char* file = util_concat_path(ENV_HOME, ".dav/config.xml");
+
+    struct stat s;
+    if (stat(file, &s)) {
+        switch (errno) {
+        case ENOENT: {
+            return 0;
+        }
+        default: {
+            perror("Cannot load config.xml");
+        }
+        }
+        return 1;
+    }
+
+    cxmutstr config_content = config_load_file(file);
+    int config_error;
+    davconfig = dav_config_load(config_content, &config_error);
+    free(config_content.ptr);
+    free(file);
+
+    if (!davconfig) {
+        fprintf(stderr, "Cannot load config.xml\n");
+        return 1;
+    }
+
+    return dav_config_register_keys(davconfig, ctx, load_key_file);
+}
+
+DavConfig* get_config(void) {
+    return davconfig;
+}
+
+int store_config(void) {
+    if (check_config_dir()) {
+        return 1;
+    }
+
+    CxBuffer* buf = dav_config2buf(davconfig);
+    if (!buf) {
+        return 1;
+    }
+
+    char* file = util_concat_path(ENV_HOME, ".dav/config.xml");
+    FILE* cout = sys_fopen(file, "w");
+    if (!cout) {
+        cxBufferFree(buf);
+        return 1;
+    }
+
+    // should only fail if we run out of disk space or something like that
+    // in that case, the config file is only destroyed
+    // could only be prevented, if we write to a temp file first and than
+    // rename it
+    fwrite(buf->space, buf->size, 1, cout);
+
+    cxBufferFree(buf);
+    fclose(cout);
+
+    return 0;
+}
+
+void free_config(void) {
+    if (davconfig) {
+        dav_config_free(davconfig);
+    }
+}
+
+cxmutstr load_key_file(const char* filename) {
+    cxmutstr k;
+    k.ptr = NULL;
+    k.length = 0;
+
+    FILE* file = NULL;
+    if (filename[0] == '/') {
+        file = sys_fopen(filename, "r");
+    }
+    else {
+        char* path = util_concat_path(ENV_HOME, ".dav/");
+        char* p2 = util_concat_path(path, filename);
+        file = sys_fopen(p2, "r");
+        free(path);
+        free(p2);
+    }
+
+    if (!file) {
+        fprintf(stderr, "Error: cannot load keyfile %s\n", filename);
+        return k;
+    }
+
+    char* data = malloc(256);
+    size_t r = fread(data, 1, 256, file);
+    k.ptr = data;
+    k.length = r;
+
+    fclose(file);
+    return k;
+}
+
+static char* get_attr_content(xmlNode* node) {
+    // TODO: remove code duplication (util_xml_get_text)
+    while (node) {
+        if (node->type == XML_TEXT_NODE) {
+            return (char*)node->content;
+        }
+        node = node->next;
+    }
+    return NULL;
+}
+
+int load_namespace(const xmlNode* node) {
+    const char* prefix = NULL;
+    const char* uri = NULL;
+
+    xmlAttr* attr = node->properties;
+    while (attr) {
+        if (attr->type == XML_ATTRIBUTE_NODE) {
+            char* value = get_attr_content(attr->children);
+            if (!value) {
+                print_error(
+                    node->line,
+                    "missing value for attribute %s\n", (char*)attr->name);
+                return 1;
+            }
+            if (xstreq(attr->name, "prefix")) {
+                prefix = value;
+            }
+            else if (xstreq(attr->name, "uri")) {
+                uri = value;
+            }
+            else {
+                print_error(
+                    node->line,
+                    "unexpected attribute %s\n", (char*)attr->name);
+                return 1;
+            }
+        }
+        attr = attr->next;
+    }
+
+    if (!prefix) {
+        print_error(node->line, "missing prefix attribute\n");
+        return 1;
+    }
+    if (!uri) {
+        print_error(node->line, "missing uri attribute\n");
+        return 1;
+    }
+
+    if (dav_get_namespace(context, prefix)) {
+        print_error(node->line, "namespace prefix '%s' already used\n", prefix);
+        return 1;
+    }
+
+    return dav_add_namespace(context, prefix, uri);
+}
+
+int load_secretstore(const xmlNode* node) {
+    // currently only one secretstore is supported
+
+    if (!pstore) {
+        return 0;
+    }
+
+    node = node->children;
+    int error = 0;
+    while (node) {
+        if (node->type == XML_ELEMENT_NODE) {
+            char* value = util_xml_get_text(node);
+            if (value) {
+                if (xstreq(node->name, "unlock-command")) {
+                    pstore->unlock_cmd = strdup(value);
+                }
+                else if (xstreq(node->name, "lock-command")) {
+                    pstore->lock_cmd = strdup(value);
+                }
+            }
+        }
+        node = node->next;
+    }
+
+    return error;
+}
+
+PwdStore* get_pwdstore(void) {
+    return pstore;
+}
+
+int pwdstore_save(PwdStore* pwdstore) {
+    if (check_config_dir()) {
+        return 1;
+    }
+
+    char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
+    int ret = pwdstore_store(pwdstore, pwfile);
+    free(pwfile);
+    return ret;
+}

mercurial