src/server/daemon/config.c

Sat, 24 Sep 2022 16:19:24 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 24 Sep 2022 16:19:24 +0200
branch
webdav
changeset 384
f9e9f2b3e299
parent 373
f78a585e1a2f
child 388
30d29ef5b79a
permissions
-rw-r--r--

remove debug code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 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 "../public/nsapi.h"

#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <ucx/string.h>
#include <ucx/utils.h>

#include "httplistener.h"
#include "config.h"
#include "func.h"
#include "log.h"
#include "event.h"
#include "threadpools.h"
#include "ldap_auth.h"
#include "configmanager.h"
#include "resourcepool.h"

#include "vserver.h"
#include "../util/pblock.h"
#include "../util/util.h"
#include "../util/atomic.h"
#include "ucx/buffer.h"

pool_handle_t *init_pool;

char* cfg_config_file_path(const char *file) {
    sstr_t base = ST("config/");
    sstr_t path = sstrcat(2, base, scstr(file));
    return path.ptr;
}

int load_init_conf(char *file) {
    log_ereport(LOG_VERBOSE, "load_init_conf");

    InitConfig *cfg = load_init_config(file);
    if(cfg == NULL) {
        log_ereport(LOG_FAILURE, "Cannot load init.conf");
        return 1;
    }
    UcxAllocator *mp = cfg->parser.mp;

    init_pool = pool_create(); // one pool for one Configuration
    UcxList *dirs = cfg->directives;
    while(dirs != NULL) {
        ConfigDirective *dir = dirs->data;

        /* create NSAPI directive */
        directive *d = malloc(sizeof(directive));
        d->param = pblock_create_pool(init_pool, 8);
        UcxList *param = cfg_param_list(dir->value, mp);
        while(param != NULL) {
            ConfigParam *p = param->data;
            pblock_nvlinsert(
                    p->name.ptr,
                    p->name.length,
                    p->value.ptr,
                    p->value.length,
                    d->param);
            
            param = param->next;
        }

        /* get function */
        char *func_name = pblock_findval("fn", d->param);
        d->func = get_function(func_name);
        if(d->func == NULL) {
            pblock_free(d->param);
            free(d);
            //dirs = dirs->next;
            log_ereport(
                    LOG_MISCONFIG,
                    "Cannot find Init function %s",
                    func_name);
            return 1;
        }

        /* execute init directive */
        int ret = d->func->func(d->param, NULL, NULL);
        if(ret != REQ_PROCEED && ret != REQ_NOACTION) {
            log_ereport(
                    LOG_FAILURE,
                    "Error running Init function %s",
                    func_name);
            pblock_free(d->param);
            free(d);
            return 1;
        }
        
        pblock_free(d->param);
        free(d);
        dirs = dirs->next;
    }
    
    free_init_config(cfg);
    
    return 0;
}

ServerConfiguration* load_server_conf(char *file) {
    log_ereport(LOG_VERBOSE, "load_server_conf");

    ServerConfig *serverconf = serverconfig_load(file);
    if(!serverconf) {
        log_ereport(LOG_FAILURE, "Cannot load server.conf");
        return NULL;
    }
    
    pool_handle_t *pool = pool_create();
    
    
    ServerConfiguration *serverconfig = pool_calloc(pool, 1, sizeof(ServerConfiguration));
    serverconfig->ref = 1;
    serverconfig->pool = pool;
    
    UcxAllocator allocator = util_pool_allocator(serverconfig->pool);
    serverconfig->a = pool_malloc(pool, sizeof(UcxAllocator));
    *serverconfig->a = allocator;
    
    serverconfig->listeners = NULL;
    serverconfig->host_vs = ucx_map_new_a(&allocator, 16);
    serverconfig->authdbs = ucx_map_new_a(&allocator, 16);
    serverconfig->resources = ucx_map_new_a(&allocator, 16);
    serverconfig->dav = ucx_map_new_a(&allocator, 16);
    
    
    
    // TODO: init serverconfig stuff
    
    
    /*
     * convert ServerConfig to ServerConfiguration
     * 
     * its important to do this in the correct order:
     * LogFile (open log file first to log possible errors)
     * Runtime
     * Threadpool
     * EventHandler
     * AuthDB
     * Listener (we set the VirtualServer later)
     * VirtualServer (dependencies: Listener)
     */
    
    /*
     * free stuff on error
     */
    
    // init logfile first
    UcxList *list = NULL;
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("LogFile"));
    if(list) {
        ConfigNode *logobj = list->data;
        if(!logobj) {
            // error
            return NULL; // TODO: fix memory leak
        }
        
        int ret = cfg_handle_logfile(serverconfig, logobj);
        if(ret != 0) {
            // cannot initialize log file
            return NULL; // TODO: fix memory leak
        }
    } else {
        // horrible error
        return NULL;
    }
    ucx_list_free(list);
     
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime"));
    UCX_FOREACH(elm, list) {
        ConfigNode *runtimeobj = elm->data;
        if(cfg_handle_runtime(serverconfig, runtimeobj)) {
            // error
            return NULL;
        }
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Threadpool"));
    UCX_FOREACH(elm, list) {
        if(cfg_handle_threadpool(serverconfig, elm->data)) {
            return NULL;
        }
    }
    ucx_list_free(list);
    // check thread pool config
    if(check_thread_pool_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("EventHandler"));
    UCX_FOREACH(elm, list) {
        if(cfg_handle_eventhandler(
                serverconfig, elm->data)) {
            // error            
            return NULL;
        }
    }
    // check event handler config
    if(check_event_handler_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AccessLog"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_accesslog(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AuthDB"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_authdb(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Listener"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_listener(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("VirtualServer"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_vs(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    ucx_list_free(list);
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("ResourcePool"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_resourcepool(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    
    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Dav"));
    UCX_FOREACH(elm, list) {
        ConfigNode *scfgobj = elm->data;
        if(cfg_handle_dav(serverconfig, scfgobj)) {
            return NULL;
        }
    }

    // set VirtualServer for all listeners
    UcxList *ls = serverconfig->listeners;
    while(ls) {
        HttpListener *listener = ls->data;

        sstr_t vsname = sstr(listener->default_vs.vs_name);

        // search for VirtualServer
        //int b = 0;
        UcxMapIterator iter = ucx_map_iterator(serverconfig->host_vs);
        VirtualServer *vs;
        UCX_MAP_FOREACH(key, vs, iter) {
            if(!sstrcmp(vsname, vs->name)) {
                listener->default_vs.vs = vs;
                break;
            }
        }

        ls = ls->next;
    }
    
    serverconfig_free(serverconf);
    
    return serverconfig;
}

void cfg_ref(ServerConfiguration *cfg) {
    ws_atomic_inc32(&cfg->ref);
}

void cfg_unref(ServerConfiguration *cfg) {
    uint32_t ref = ws_atomic_dec32(&cfg->ref);
    if(ref == 0) {
        pool_destroy(cfg->pool);
    }
}


void init_server_config_parser() {
    
}

int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) { 
    scstr_t user = serverconfig_directive_value(obj, SC("User"));
    if(user.ptr) {
        cfg->user = sstrdup_a(cfg->a, user);
    }
    scstr_t tmp = serverconfig_directive_value(obj, SC("Temp"));
    if(tmp.ptr) {
        cfg->tmp = sstrdup_a(cfg->a, tmp);
    } else {
        // TODO: do this check after all config loading is done
        log_ereport(LOG_MISCONFIG, "no temporary directory specified");
        return -1;
    }
    
    // mime file
    scstr_t mf = serverconfig_directive_value(obj, SC("MimeFile"));  
    scstr_t base = SC("config/"); 
    sstr_t file = sstrcat(2, base, mf);
    
    if(mime_conf_load(cfg, file)) {
        return -1;
    }
    
    free(file.ptr);
    return 0;
}

int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj) {
    scstr_t file = serverconfig_directive_value(obj, SC("File"));
    scstr_t lvl = serverconfig_directive_value(obj, SC("Level"));
    
    if(file.ptr == NULL || lvl.ptr == NULL) {
        /* missing log file parameters */
        return -1;
    }
    
    LogConfig logcfg;
    logcfg.file = file.ptr;
    logcfg.level = lvl.ptr;
    logcfg.log_stdout = 0;
    logcfg.log_stderr = 0;
    /* TODO: stdout, stderr config */
    
    int ret = init_log_file(&logcfg);

    return ret;
}

int cfg_handle_threadpool(ServerConfiguration *cfg, ConfigNode *obj) {
    ThreadPoolConfig poolcfg;
    poolcfg.min_threads = 4;
    poolcfg.min_threads = 4;
    poolcfg.max_threads = 8;
    poolcfg.queue_size = 64;
    poolcfg.stack_size = 262144;
    
    scstr_t name  = serverconfig_directive_value(obj, SC("Name"));
    scstr_t min   = serverconfig_directive_value(obj, SC("MinThreads"));
    scstr_t max   = serverconfig_directive_value(obj, SC("MaxThreads"));
    scstr_t stack = serverconfig_directive_value(obj, SC("StackSize"));
    scstr_t queue = serverconfig_directive_value(obj, SC("QueueSize"));
    // TODO: Type
    
    if(name.length == 0) {
        // TODO: log error
        return 1;
    }
    
    if(min.length != 0) {
        int64_t value;
        if(util_strtoint(min.ptr, &value)) {
            poolcfg.min_threads = value;
        } else {
            log_ereport(LOG_MISCONFIG, "Threadpool: MinThreads not an integer");
            return 1;
        }
    }
    
    if(max.length != 0) {
        int64_t value;
        if(util_strtoint(max.ptr, &value)) {
            poolcfg.max_threads = value;
        } else {
            log_ereport(LOG_MISCONFIG, "Threadpool: MaxThreads not an integer");
            return 1;
        }
    }
    
    if(stack.length != 0) {
        int64_t value;
        if(util_strtoint(stack.ptr, &value)) {
            poolcfg.stack_size = value;
        } else {
            log_ereport(LOG_MISCONFIG, "Threadpool: StackSize not an integer");
        }
    }
    
    if(queue.length != 0) {
        int64_t value;
        if(util_strtoint(queue.ptr, &value)) {
            poolcfg.queue_size = value;
        } else {
            log_ereport(LOG_MISCONFIG, "Threadpool: QueueSize not an integer");
        }
    }
    
    create_threadpool(name, &poolcfg);
    
    return 0;
}

#define EV_MAX_THREADS 2048
int cfg_handle_eventhandler(ServerConfiguration *c, ConfigNode *obj) {
    EventHandlerConfig evcfg;
    
    scstr_t name      = serverconfig_directive_value(obj, SC("Name"));
    scstr_t threads   = serverconfig_directive_value(obj, SC("Threads"));
    scstr_t isdefault = serverconfig_directive_value(obj, SC("Default"));
    
    evcfg.name = name;
    
    int64_t value;
    if(!util_strtoint(threads.ptr, &value)) {
        log_ereport(LOG_MISCONFIG, "EventHandler: Threads: '%s' is not an integer", threads.ptr);
        return 1;
    }
    if(value < 1 || value > EV_MAX_THREADS) {
        log_ereport(LOG_MISCONFIG, "EventHandler: Invalid number of threads (1 .. %d)", EV_MAX_THREADS);
        return 1;
    }
    
    evcfg.nthreads = value;
    
    evcfg.isdefault = util_getboolean(isdefault.ptr, 0);
    
    return create_event_handler(&evcfg);
}

int cfg_handle_resourcepool(ServerConfiguration *cfg, ConfigNode *obj) {
    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
    scstr_t type = serverconfig_directive_value(obj, SC("Type"));
    
    int ret = 0;
    if(resourcepool_new(cfg, type, name, obj)) {
        ret = 1;
    }
    
    return ret;
}

int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) {
    // TODO: use a name to identify the log file
    
    scstr_t file = serverconfig_directive_value(obj, SC("File"));
    if(file.ptr == NULL) {
        return 0;
    }
    sstr_t format;
    format.ptr = NULL;
    format.length = 0;
    
    //AccessLog *log = get_access_log(file, format);
    LogFile *log_file = get_access_log_file(file);
    if(!log_file) {
        // TODO: error/warning
        return 0;
    }
    AccessLog *log = pool_malloc(cfg->pool, sizeof(AccessLog));
    log->file = sstrdup_a(cfg->a, file);
    log->format = format;
    log->log = log_file;
    cfg->logfiles = ucx_list_append_a(cfg->a, cfg->logfiles, log);
    
    if(!cfg->default_log) {
        cfg->default_log = log;
    }
    
    return 0;
}

int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj) {
    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
    scstr_t type = serverconfig_directive_value(obj, SC("Type"));
    
    AuthDB *authdb = NULL;
    
    if(!sstrcmp(type, sstr("ldap"))) {
        LDAPConfig conf;
        
        scstr_t host = serverconfig_directive_value(obj, SC("Host"));
        scstr_t port = serverconfig_directive_value( obj, SC("Port"));
        scstr_t basedn = serverconfig_directive_value(obj, SC("BaseDN"));
        scstr_t binddn = serverconfig_directive_value(obj, SC("BindDN"));
        scstr_t basepw = serverconfig_directive_value(obj, SC("BindPW"));
        
        conf.hostname = sstrdup_a(cfg->a, host).ptr;
        conf.port = atoi(port.ptr);
        conf.basedn = sstrdup_a(cfg->a, basedn).ptr;
        conf.binddn = sstrdup_a(cfg->a, binddn).ptr;
        conf.bindpw = sstrdup_a(cfg->a, basepw).ptr;
        
        authdb = create_ldap_authdb(cfg, name.ptr, &conf);      
    } else if(!sstrcmp(type, sstr("keyfile"))) {
        // we only need the file parameter
        scstr_t file = serverconfig_directive_value(obj, SC("File"));
        if(file.length == 0) {
            log_ereport(
                    LOG_MISCONFIG,
                    "missing File parameter for keyfile authdb");
            return 1;
        }
        
        // load keyfile
        authdb = keyfile_load(cfg, file);
    }

    if(authdb) {
        if(ucx_map_sstr_put(cfg->authdbs, name, authdb)) {
            return -1;
        }
    }
    
    return 0;
}

int cfg_handle_listener(ServerConfiguration *cfg, ConfigNode *obj) {
    ListenerConfig lc;
    ZERO(&lc, sizeof(ListenerConfig));
    lc.cfg = cfg;
    lc.port = 8080;
    lc.nacceptors = 1;
    
    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
    scstr_t port = serverconfig_directive_value(obj, SC("Port"));
    scstr_t vs   = serverconfig_directive_value(obj, SC("DefaultVS"));
    scstr_t thrp = serverconfig_directive_value(obj, SC("Threadpool"));
    scstr_t blck = serverconfig_directive_value(obj, SC("BlockingIO"));
    
    // TODO: use sstrdup_pool?
    int64_t port_value;
    if(!util_strtoint(port.ptr, &port_value)) {
        log_ereport(LOG_MISCONFIG, "Listener: Invalid argument for parameter 'Port': '%s'", port.ptr);
        return 1;
    }
    if(port_value < 1 || port_value > 65535) {
        log_ereport(LOG_MISCONFIG, "Listener: Port number out of range (1 .. 65535)");
        return 1;
    }
    
    lc.name = sstrdup(name);
    lc.port = port_value;
    lc.vs = sstrdup(vs);
    lc.threadpool = sstrdup(thrp);
    
    lc.blockingio = util_getboolean_s(blck, WS_FALSE);
    
    scstr_t ssl = serverconfig_directive_value(obj, SC("SSL"));
    if(util_getboolean_s(ssl, WS_FALSE)) {
        scstr_t cert        = serverconfig_directive_value(obj, SC("Cert"));
        scstr_t privkey     = serverconfig_directive_value(obj, SC("Key"));
        scstr_t chain       = serverconfig_directive_value(obj, SC("CertChain"));
        scstr_t disableprot = serverconfig_directive_value(obj, SC("SSLDisableProtocol"));
        
        WSBool config_ok = WS_TRUE;
        // TODO: log error
        if(!cert.ptr && !chain.ptr) {
            log_ereport(
                    LOG_MISCONFIG,
                    "SSL Listener %s: Missing Cert or ChainCert directive",
                    lc.name.ptr);
            config_ok = WS_FALSE;
        }
        if(!privkey.ptr) {
            log_ereport(
                    LOG_MISCONFIG,
                    "SSL Listener %s: Missing Key directive",
                    lc.name.ptr);
            config_ok = WS_FALSE;
        }
        
        if(config_ok) {
            lc.certfile = cert;
            lc.privkeyfile = privkey;
            lc.chainfile = chain;
            lc.disable_proto = disableprot;
            lc.ssl = WS_TRUE;
        }
    } else {
        lc.ssl = WS_FALSE;
    }
    
    // TODO: check if all important configs are set
    
    HttpListener *listener = http_listener_create(&lc);
    if(!listener) {
        return 1;
    }
    
    listener->default_vs.vs_name = sstrdup_a(cfg->a, lc.vs).ptr;
    cfg->listeners = ucx_list_append_a(cfg->a, cfg->listeners, listener); 
    
    return 0;
}

int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) {
    VirtualServer *vs = vs_new();

    vs->name = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Name")));
    vs->host = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Host")));
    vs->document_root = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("DocRoot")));
    
    scstr_t objfile = serverconfig_directive_value(obj, SC("ObjectFile"));
    scstr_t aclfile = serverconfig_directive_value(obj, SC("ACLFile"));
    
    // load the object config file
    sstr_t base = sstr("config/");
    sstr_t file = sstrcat(2, base, objfile);
    // sstrcat with allocator because we want to keep the string
    file = sstrcat_a(cfg->a, 2, base, objfile);

    HTTPObjectConfig *httpobj = objconf_load(cfg, file);
    if(!httpobj) {
        return -1;
    }
    vs->objectfile = file;
    vs->objects = httpobj;
    
    
    // load acl config file
    file = sstrcat(2, base, aclfile);
    
    ACLData *acldata = acl_conf_load(cfg, file);
    if(!acldata) {
        return -1;
    }
    vs->acls = acldata;
    
    free(file.ptr);
    
    
    // set the access log for the virtual server
    // TODO: don't always use the default
    vs->log = cfg->default_log;

    ucx_map_sstr_put(cfg->host_vs, vs->host, vs);
    
    return 0;
}

int cfg_handle_dav(ServerConfiguration *cfg, ConfigNode *obj) {
    UcxList *backends = NULL; // list of ConfigArg*
    UcxAllocator a = util_pool_allocator(cfg->pool);
    int init_error;
    
    // parse args
    char *uri = NULL;
    char *ppath = NULL;
    char *name = NULL;
    UCX_FOREACH(elm, obj->args) {
        ConfigArg *arg = elm->data;
        if(arg->name.ptr == NULL) {
            // default: uri
            uri = arg->value.ptr;
        } else if(!sstrcasecmp(arg->name, SC("uri"))) {
            uri = arg->value.ptr;
        } else if(!sstrcasecmp(arg->name, SC("ppath"))) {
            ppath = arg->value.ptr;
        } else if(!sstrcasecmp(arg->name, SC("name"))) {
            name = arg->value.ptr;
        }
    }
    if(!uri && !ppath && !name) {
        return 1;
    }
    
    // get a list of all DavBackends
    UCX_FOREACH(elm, obj->children) {
        ConfigNode *node = elm->data;
        if(!sstrcasecmp(node->name, SC("DavBackend"))) {
            if(node->type == CONFIG_NODE_DIRECTIVE) {
                if(ucx_list_size(node->args) == 1) {
                    ConfigArg *arg = node->args->data;
                    backends = ucx_list_append(backends, arg);
                } else {
                    log_ereport(LOG_MISCONFIG, "DavBackend must have only one value");
                    ucx_list_free(backends);
                    return 1;
                }
            } else {
                log_ereport(LOG_MISCONFIG, "DavBackend must be a directive");
                ucx_list_free(backends);
                return 1;
            }
        }
    }
    
    int ret = 0;
    WebdavRepository *repository = pool_malloc(cfg->pool, sizeof(WebdavRepository));
    repository->vfs = NULL;
    repository->vfsInitData = NULL;
    repository->davBackends = NULL;
    
    // initialize backends
    UCX_FOREACH(elm, backends) {
        // the DavBackend value should contain the dav class name
        ConfigArg *backendArg = elm->data;
        
        WebdavType *dav = webdav_get_type((scstr_t){backendArg->value.ptr, backendArg->value.length});
        if(!dav) {
            log_ereport(LOG_MISCONFIG, "Unknown webdav backend type '%s'", backendArg->value.ptr);
            ret = 1;
            break;
        }
        
        // call backend init
        // init_data can be NULL, errors will be indicated by init_error
        void *init_data = webdav_init_backend(cfg, cfg->pool, dav, obj, &init_error);
        if(init_error) {
            log_ereport(LOG_FAILURE, "Failed to initialize webdav backend %s", backendArg->value.ptr);
            ret = 1;
            break;
        }
        
        WebdavBackendInitData *davInit = pool_malloc(cfg->pool, sizeof(WebdavBackendInitData));
        if(!davInit) {
            log_ereport(LOG_FAILURE, "Failed to initialize webdav backend %s: OOM", backendArg->value.ptr);
            ret = 1;
            break;
        }
        davInit->davType = dav;
        davInit->davInitData = init_data;
        
        repository->davBackends = ucx_list_append_a(&a, repository->davBackends, davInit);
    }
    
    // initialize vfs
    scstr_t vfs_class = serverconfig_directive_value(obj, SC("VFS"));
    if(vfs_class.length > 0) {
        VfsType *vfs = vfs_get_type((scstr_t){vfs_class.ptr, vfs_class.length});
        if(vfs) {
            repository->vfs = vfs;
            repository->vfsInitData = vfs_init_backend(cfg, cfg->pool, vfs, obj, &init_error);
            ret = init_error;
        } else {
            log_ereport(LOG_FAILURE, "Unknown vfs type '%s'", vfs_class.ptr);
            ret = 1;
        }
    }
    
    scstr_t object = serverconfig_directive_value(obj, SC("Object"));
    if(object.length > 0) {
        repository->object = sstrdup_a(&a, object);
        if(repository->object.length != object.length) {
            // OOM
            log_ereport(LOG_FAILURE, "Cannot create webdav repository: OOM");
            ret = 1;
        }
    }
    
    if(!ret) {
        if(name) {
            ucx_map_cstr_put(cfg->dav, name, repository);
        } else {
            log_ereport(LOG_FAILURE, "TODO: location based dav repositories not implemented");
            ret = 1;
        }
    }
    
    return ret;    
}

static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, sstr_t file) {
    pool_handle_t *pool = conf->pool;
    
    UcxList *objlist = cfg->objects;
    int i = 0;
    while(objlist != NULL) {
        ConfigObject *cob = objlist->data;

        // get name and ppath
        char *name = NULL;
        char *ppath = NULL;
        if(cob->name.length > 0) {
            name = sstrdup_pool(pool, cob->name).ptr;
            if(!name) return -1;
        }
        if(cob->ppath.length > 0) {
            ppath = sstrdup_pool(pool, cob->ppath).ptr;
            if(!ppath) return -1;
        }

        // create and add object
        httpd_object *obj = object_new(pool, name);
        if(!obj) return -1;
        obj->path = NULL;

        conf->objects[i] = obj;

        // add directives
        for(int j=0;j<NUM_NSAPI_TYPES-1;j++) {
            UcxList *dirs = cob->directives[j];
            while(dirs != NULL) {
                ConfigDirective *cfgdir = dirs->data;

                directive *d = pool_malloc(pool, sizeof(directive));
                if(!d) return -1;
                if(cfgdir->condition) {
                    sstr_t expr = cfgdir->condition->param_str;
                    d->cond = condition_from_str(pool, expr.ptr, expr.length);
                } else {
                    d->cond = NULL;
                }
                d->param = pblock_create_pool(pool, 8);

                // add params
                UcxList *param = cfg_param_list(cfgdir->value, scfg->a);
                while(param != NULL) {
                    ConfigParam *p = param->data;
                    pblock_nvlinsert(
                            p->name.ptr,
                            p->name.length,
                            p->value.ptr,
                            p->value.length,
                            d->param);
                    param = param->next;
                }

                // get function
                char *func_name = pblock_findval("fn", d->param);
                if(!func_name) {
                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file.ptr);
                    return -1;
                }
                d->func = get_function(func_name);
                if(!d->func) {
                    log_ereport(LOG_MISCONFIG, "func %s not found", func_name);
                    return -1;
                }

                dirs = dirs->next;

                // add function to dtable
                object_add_directive(obj, d, cfgdir->type_num);
            }
        }

        // next
        i++;
        objlist = objlist->next;
    }
    
    return 0;
}

HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file) {
    log_ereport(LOG_VERBOSE, "load_obj_conf");
    
    int ret = 0;

    // create object config
    pool_handle_t *pool = scfg->pool;
    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
    if(!conf) {
        return NULL;
    }
    conf->pool = pool;
    
    // load obj config file
    ObjectConfig *cfg = load_object_config(file.ptr);
    if(!cfg) {
        return NULL;
    }

    // convert ObjectConfig to HTTPObjectConfig

    // add objects
    conf->nobj = ucx_list_size(cfg->objects);
    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
    if(conf->objects) {
        ret = convert_objconf(scfg, cfg, conf, file);
    } else {
        ret = -1;
    }

    free_object_config(cfg);

    return !ret ? conf : NULL;
}

int mime_conf_load(ServerConfiguration *cfg, sstr_t file) {
    MimeConfig *mimecfg = load_mime_config(file.ptr);
    if(!mimecfg) {
        return -1;
    }
    
    int ret = 0;
    
    // cleanup in case of errors is done by the allocator
    MimeMap *mimemap = almalloc(cfg->a, sizeof(MimeMap));
    UcxMap *map = ucx_map_new_a(cfg->a, (mimecfg->ntypes * 3) / 2);
    
    if(mimemap && map) {
        mimemap->map = map;
        
        // add ext type pairs
        UCX_FOREACH(md, mimecfg->directives) {
            MimeDirective *d = md->data;
            // add the type for each extension to the map
            UCX_FOREACH(xl, d->exts) {
                sstr_t ext = sstr(xl->data);
                sstr_t value = sstrdup(d->type);
                if(ucx_map_sstr_put(map, ext, value.ptr)) {
                    ret = -1;
                    break;
                }
            }
            if(ret) {
                break;
            }
        }
        
        cfg->mimetypes = mimemap;
    } else {
        ret = -1;
    }
    
    free_mime_config(mimecfg);
    return ret;
}



ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file) {
    ACLFile *aclfile = load_acl_file(file.ptr);
    
    // TODO: malloc return checks
    
    ACLData *acldata = acl_data_new(cfg->a);
    UCX_FOREACH(elm, aclfile->namedACLs) {
        ACLConfig *ac = elm->data;
        ACLList *acl = acl_config_convert(cfg, ac);
        log_ereport(LOG_VERBOSE, "add acl: %.*s", (int)ac->id.length, ac->id.ptr);
        ucx_map_sstr_put(acldata->namedACLs, ac->id, acl);
    }
    free_acl_file(aclfile);
    
    return acldata;
}

ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
    UcxAllocator *a = cfg->a;
    
    WSAcl *acllist = almalloc(cfg->a, sizeof(WSAcl));
    acllist->acl.check = (acl_check_f)wsacl_check;
    acllist->acl.authdb = NULL;
    acllist->acl.authprompt = NULL;
    acllist->acl.isextern = 0;
    acllist->ace = NULL;
    acllist->ece = NULL;
    
    if(acl->type.ptr && !sstrcmp(acl->type, sstr("fs"))) {
        acllist->acl.isextern = 1;
    }
    
    size_t s = ucx_list_size(acl->entries);
    WSAce **tmp_aces = calloc(s, sizeof(WSAce*));
    WSAce **tmp_eces = calloc(s, sizeof(WSAce*));
    int ai = 0;
    int ei = 0;
    
    // convert entries
    UCX_FOREACH(elm, acl->entries) {
        ACEConfig *acecfg = elm->data;
        
        // copy data
        WSAce *ace = almalloc(a, sizeof(WSAce));
        ace->access_mask = acecfg->access_mask;
        ace->flags = acecfg->flags;
        ace->type = acecfg->type;
        ace->who = sstrdup_a(a, acecfg->who).ptr;
        
        // add the entry to the correct array
        if(ace->type >= ACL_TYPE_AUDIT) {
            tmp_eces[ei] = ace;
            ei++;
        } else {
            tmp_aces[ai] = ace;
            ai++;
        }
    }
    
    // create new entrie arrays with perfect fitting size
    if(ai > 0) {
        acllist->ace = alcalloc(a, ai, sizeof(WSAce*));
    }
    if(ei > 0) {
        acllist->ece = alcalloc(a, ei, sizeof(WSAce*));
    }
    memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*));
    memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*));
    acllist->acenum = ai;
    acllist->ecenum = ei;
    
    free(tmp_aces);
    free(tmp_eces);
    
    // get authentication information
    if(acl->authparam) {
        sstr_t authdb_str = cfg_param_get(acl->authparam, sstr("authdb"));
        sstr_t prompt_str = cfg_param_get(acl->authparam, sstr("prompt"));
        
        if(authdb_str.ptr) {
            AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str);
            acllist->acl.authdb = authdb;
            if(authdb && prompt_str.ptr) {
                acllist->acl.authprompt = sstrdup_a(a, prompt_str).ptr;
            }
        }
    }
    
    return &acllist->acl;
}

AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file) {
    Keyfile *keyfile = keyfile_new(cfg->a);
    if(!keyfile) {
        return NULL;
    }
    
    KeyfileConfig *conf = load_keyfile_config(file.ptr);
    if(!conf) {
        return NULL;
    }
    
    AuthDB *ret = &keyfile->authdb;
    
    UCX_FOREACH(elm, conf->users) {
        KeyfileEntry *user = elm->data;
        if(keyfile_add_user(
                keyfile,
                user->name,
                user->hashtype,
                user->hashdata,
                user->groups,
                user->numgroups))
        {
            ret = NULL;
            break;
        }
    }
    
    free_keyfile_config(conf);
    
    return ret;
}

pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj) {
    pblock *pb = pblock_create_pool(pool, 8);
    UCX_FOREACH(elm, obj->children) {
        ConfigNode *d = elm->data;
        if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && ucx_list_size(d->args) == 1) {
            ConfigArg *arg = d->args->data;
            pblock_nvlinsert(d->name.ptr, d->name.length, arg->value.ptr, arg->value.length, pb);
        }
    }
    return pb;
}

mercurial