application/config.c

Mon, 29 Jan 2024 10:41:00 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 29 Jan 2024 10:41:00 +0100
changeset 6
09ac07345656
child 7
905ac52c910f
permissions
-rw-r--r--

add config related code from dav / load config and fill repo list

/*
 * 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