src/server/daemon/config.c

Sat, 03 Aug 2019 17:54:54 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 03 Aug 2019 17:54:54 +0200
changeset 204
e870a7c8f223
parent 162
b169992137a8
child 254
4784c14aa639
permissions
-rw-r--r--

replace old build system with uwproj

/*
 * 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 "vserver.h"
#include "../util/pblock.h"
#include "../util/util.h"
#include "../util/atomic.h"
#include "ucx/buffer.h"

pool_handle_t *cfg_pool;

// TODO: Funktion für ConfigDirective -> directive
// TODO: Funktion für UcxList parameter list -> pblock

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;

    cfg_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(cfg_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(ServerConfiguration *old, char *file) {
    log_ereport(LOG_VERBOSE, "load_server_conf");

    ServerConfig *serverconf = load_server_config(file);
    if(serverconf == NULL) {
        log_ereport(LOG_FAILURE, "Cannot load server.conf");
    }
    ServerConfiguration *serverconfig = calloc(1, sizeof(ServerConfiguration));
    serverconfig->ref = 1;
    serverconfig->pool = pool_create();
    serverconfig->listeners = NULL;
    serverconfig->host_vs = ucx_map_new(16);
    serverconfig->authdbs = ucx_map_new(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 *lfl = ucx_map_sstr_get(serverconf->objects, sstrn("LogFile", 7));
    if(lfl != NULL) {
        ServerConfigObject *logobj = lfl->data;
        if(logobj == NULL) {
            // error
            return NULL;
        }
        
        int ret = cfg_handle_logfile(serverconfig, logobj);
        if(ret != 0) {
            // cannot initialize log file
            return NULL;
        }
    } else {
        // horrible error
        return NULL;
    }
     
    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
    UCX_FOREACH(elm, list) {
        ServerConfigObject *scfgobj = elm->data;
        if(cfg_handle_runtime(serverconfig, scfgobj)) {
            // error
            return NULL;
        }
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("Threadpool", 10));
    UCX_FOREACH(elm, list) {
        if(cfg_handle_threadpool(serverconfig, elm->data)) {
            return NULL;
        }
    }
    // check thread pool config
    if(check_thread_pool_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("EventHandler", 12));
    UCX_FOREACH(elm, list) {
        if(cfg_handle_eventhandler(
                serverconfig, (ServerConfigObject*)elm->data)) {
            // error            
            return NULL;
        }
    }
    // check event handler config
    if(check_event_handler_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("AccessLog", 9));
    UCX_FOREACH(elm, list) {
        ServerConfigObject *scfgobj = elm->data;
        if(cfg_handle_accesslog(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("AuthDB", 6));
    UCX_FOREACH(elm, list) {
        ServerConfigObject *scfgobj = elm->data;
        if(cfg_handle_authdb(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("Listener", 8));
    UCX_FOREACH(elm, list) {
        ServerConfigObject *scfgobj = elm->data;
        if(cfg_handle_listener(serverconfig, scfgobj)) {
            return NULL;
        }
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("VirtualServer", 13));
    UCX_FOREACH(elm, list) {
        ServerConfigObject *scfgobj = elm->data;
        if(cfg_handle_vs(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;
    }
    
    free_server_config(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) {
        // TODO: free configuration
        printf("free ServerConfiguration %"PRIxPTR"\n", (intptr_t)cfg);
    }
}


void init_server_config_parser() {
    
}

int cfg_handle_runtime(ServerConfiguration *cfg, ServerConfigObject *obj) {
    sstr_t user = cfg_directivelist_get_str(obj->directives, sstr("User"));
    if(user.ptr) {
        cfg->user = sstrdup_pool(cfg->pool, user);
    }
    sstr_t tmp = cfg_directivelist_get_str(obj->directives, sstr("Temp"));
    if(tmp.ptr) {
        cfg->tmp = sstrdup_pool(cfg->pool, tmp);
    } else {
        log_ereport(LOG_MISCONFIG, "no temporary directory specified");
        return -1;
    }
    
    // mime file
    sstr_t mf = cfg_directivelist_get_str(obj->directives, sstr("MimeFile"));  
    sstr_t base = sstr("config/"); 
    sstr_t file = sstrcat(2, base, mf);
    
    ConfigFile *f = cfgmgr_get_file(file);
    if(f == NULL) {
        f = malloc(sizeof(ConfigFile));
        f->data = NULL;
        f->file = sstrdup(file);
        f->reload = mime_conf_reload;
        f->last_modified = 0;
        
        // load the file content
        //f->reload(f, cfg);
        if(cfgmgr_reload_file(f, cfg, NULL)) {
            free(f->file.ptr);
            free(f);
            
            free(file.ptr);
            return -1;
        }
        cfgmgr_attach_file(f);
    }
    
    cfg->mimetypes = f->data;
    
    free(file.ptr);
    return 0;
}

int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj) {
    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File"));
    sstr_t lvl = cfg_directivelist_get_str(obj->directives, sstr("Level"));
    
    if(file.ptr == NULL || lvl.ptr == NULL) {
        /* missing log file parameters */
        return -1;
    }
    
    LogConfig logcfg;
    logcfg.file = sstrdup(file).ptr;
    logcfg.level = sstrdup(lvl).ptr;
    logcfg.log_stdout = 0;
    logcfg.log_stderr = 0;
    /* TODO: stdout, stderr config */
    
    int ret = init_log_file(&logcfg);
    
    free(logcfg.file);
    free(logcfg.level);

    return ret;
}

int cfg_handle_threadpool(ServerConfiguration *cfg, ServerConfigObject *obj) {
    ThreadPoolConfig poolcfg;
    poolcfg.min_threads = 4;
    poolcfg.min_threads = 4;
    poolcfg.max_threads = 8;
    poolcfg.queue_size = 64;
    poolcfg.stack_size = 262144;
    
    sstr_t name = cfg_directivelist_get_str(
            obj->directives,
            sstr("Name"));
    sstr_t min = cfg_directivelist_get_str(
            obj->directives,
            sstr("MinThreads"));
    sstr_t max = cfg_directivelist_get_str(
            obj->directives,
            sstr("MaxThreads"));
    sstr_t stack = cfg_directivelist_get_str(
            obj->directives,
            sstr("StackSize"));
    sstr_t queue = cfg_directivelist_get_str(
            obj->directives,
            sstr("QueueSize"));
    // TODO: Type
    
    if(name.length == 0) {
        // TODO: log error
        return 1;
    }
    
    if(min.length != 0) {
        min = sstrdup(min);
        poolcfg.min_threads = atoi(min.ptr);
        free(min.ptr);
    }
    
    if(max.length != 0) {
        max = sstrdup(max);
        poolcfg.max_threads = atoi(max.ptr);
        free(max.ptr);
    }
    
    if(stack.length != 0) {
        stack = sstrdup(stack);
        poolcfg.stack_size = atoi(stack.ptr);
        free(stack.ptr);
    }
    
    if(queue.length != 0) {
        queue = sstrdup(queue);
        poolcfg.queue_size = atoi(queue.ptr);
        free(queue.ptr);
    }
    
    create_threadpool(name, &poolcfg);
    
    return 0;
}

int cfg_handle_eventhandler(ServerConfiguration *c, ServerConfigObject *obj) {
    EventHandlerConfig evcfg;
    
    sstr_t name      = cfg_directivelist_get_str(obj->directives, sstr("Name"));
    sstr_t threads   = cfg_directivelist_get_str(
            obj->directives,
            sstr("Threads"));
    sstr_t isdefault = cfg_directivelist_get_str(
            obj->directives,
            sstr("Default"));
    
    evcfg.name = name;
    
    sstr_t s = sstrdup(threads);
    evcfg.nthreads = atoi(s.ptr);
    free(s.ptr);
    
    evcfg.isdefault = util_getboolean(isdefault.ptr, 0);
    
    return create_event_handler(&evcfg);
}

int cfg_handle_accesslog(ServerConfiguration *cfg, ServerConfigObject *obj) {
    // TODO: use a name to identify the log file
    
    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("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_pool(cfg->pool, file);
    log->format = format;
    log->log = log_file;
    cfg->logfiles = ucx_list_append(cfg->logfiles, log);
    
    if(!cfg->default_log) {
        cfg->default_log = log;
    }
    
    return 0;
}

int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj) {
    sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name"));
    sstr_t type = cfg_directivelist_get_str(obj->directives, sstr("Type"));
    
    if(!sstrcmp(type, sstr("ldap"))) {
        LDAPConfig conf;
        
        sstr_t host = cfg_directivelist_get_str(
                obj->directives,
                sstr("Host"));
        sstr_t port = cfg_directivelist_get_str(
                obj->directives,
                sstr("Port"));
        sstr_t basedn = cfg_directivelist_get_str(
                obj->directives,
                sstr("BaseDN"));
        sstr_t binddn = cfg_directivelist_get_str(
                obj->directives,
                sstr("BindDN"));
        sstr_t basepw = cfg_directivelist_get_str(
                obj->directives,
                sstr("BindPW"));
        
        host = sstrdup(host);
        port = sstrdup(port);
        basedn = sstrdup(basedn);
        binddn = sstrdup(binddn);
        basepw = sstrdup(basepw);
        
        conf.hostname = host.ptr;
        conf.port = atoi(port.ptr);
        conf.basedn = basedn.ptr;
        conf.binddn = binddn.ptr;
        conf.bindpw = basepw.ptr;
        
        name = sstrdup(name);
        
        AuthDB *authdb = create_ldap_authdb(name.ptr, &conf);
        ucx_map_sstr_put(cfg->authdbs, name, authdb);
        
        // TODO: create_ldap_authdb should copy the strings
        /*
        free(host.ptr);
        free(port.ptr);
        free(basedn.ptr);
        free(binddn.ptr);
        free(basepw.ptr);
        free(name.ptr);
        */
        
    } else if(!sstrcmp(type, sstr("keyfile"))) {
        // we only need the file parameter
        sstr_t file = cfg_directivelist_get_str(
                obj->directives,
                sstr("File"));
        if(file.length == 0) {
            log_ereport(
                    LOG_MISCONFIG,
                    "missing File parameter for keyfile authdb");
            return 1;
        }
        
        // load keyfile
        ConfigFile *f = cfgmgr_get_file(file);
        if(f == NULL) {
            f = malloc(sizeof(ConfigFile));
            f->data = NULL;
            f->file = sstrdup(file);
            f->reload = keyfile_reload;
            f->last_modified = 0;
            //f->reload(f, cfg);
            if(cfgmgr_reload_file(f, cfg, NULL)) {
                free(f->file.ptr);
                free(f);
                return -1;
            }
            cfgmgr_attach_file(f);
        }
        
        // add keyfile authdb
        Keyfile *keyfile = f->data;
        keyfile->authdb.name = sstrdup(name).ptr;
        ucx_map_sstr_put(cfg->authdbs, name, keyfile);
    }

    return 0;
}

int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj) {
    ListenerConfig lc;
    ZERO(&lc, sizeof(ListenerConfig));
    lc.cfg = cfg;
    lc.port = 8080;
    lc.nacceptors = 1;
    
    // TODO: use sstrdup_pool?
    lc.name = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Name")));
    lc.port = atoi(cfg_directivelist_get_str(
            obj->directives,
            sstr("Port")).ptr);
    lc.vs = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("DefaultVS")));
    lc.threadpool = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Threadpool")));
    
    sstr_t blockingio = cfg_directivelist_get_str(
            obj->directives,
            sstr("BlockingIO"));
    if(blockingio.ptr) {
        lc.blockingio = util_getboolean_s(blockingio, WS_FALSE);
    }
    
    sstr_t ssl = cfg_directivelist_get_str(obj->directives, S("SSL"));
    if(util_getboolean_s(ssl, WS_FALSE)) {
        sstr_t cert = cfg_directivelist_get_str(obj->directives, S("Cert"));
        sstr_t privkey = cfg_directivelist_get_str(obj->directives, S("Key"));
        sstr_t chain = cfg_directivelist_get_str(obj->directives, S("CertChain"));
        sstr_t disableprot = cfg_directivelist_get_str(
                obj->directives,
                S("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 = lc.vs.ptr;
    cfg->listeners = ucx_list_append(cfg->listeners, listener); 
    
    return 0;
}

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

    vs->name = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Name")));
    vs->host = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Host")));
    vs->document_root = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("DocRoot")));
    sstr_t objfile = cfg_directivelist_get_str(
            obj->directives,
            sstr("ObjectFile"));
    sstr_t aclfile = cfg_directivelist_get_str(
            obj->directives,
            sstr("ACLFile"));
    
    // load the object config file
    sstr_t base = sstr("config/");
    sstr_t file = sstrcat(2, base, objfile);
    file = sstrcat(2, base, objfile);

    // the file is managed by the configuration manager
    ConfigFile *f = cfgmgr_get_file(file);
    if(f == NULL) {
        f = malloc(sizeof(ConfigFile));
        f->data = NULL;
        f->file = sstrdup(file);
        f->reload = object_conf_reload;
        f->last_modified = 0;
        //f->reload(f, cfg);
        if(cfgmgr_reload_file(f, cfg, NULL)) {
            free(f->file.ptr);
            free(f);
            
            free(file.ptr);
            return -1;
        }
        cfgmgr_attach_file(f);
    }
    vs->objectfile = sstrdup(file);
    vs->objects = (HTTPObjectConfig*)f->data;
    free(file.ptr);
    
    
    // load acl config file
    file = sstrcat(2, base, aclfile);
    
    ConfigFile *aclf = cfgmgr_get_file(file);
    if(aclf == NULL) {
        aclf = malloc(sizeof(ConfigFile));
        aclf->data = NULL;
        aclf->file = sstrdup(file);
        aclf->reload = acl_conf_reload;
        aclf->last_modified = 0;
        //aclf->reload(aclf, cfg);
        if(cfgmgr_reload_file(aclf, cfg, NULL)) {
            free(aclf->file.ptr);
            free(aclf);
            
            free(file.ptr);
            return -1;
        }
        cfgmgr_attach_file(aclf);
    }
    vs->acls = aclf->data;
    free(file.ptr);
    
    // set the access log for the virtual server
    // TODO: don't use always the default
    vs->log = cfg->default_log;

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


int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
    HTTPObjectConfig *old_conf = file->data;
    file->data = load_obj_conf(file->file.ptr);
    if(old_conf) {
        object_conf_unref(old_conf);
    }
    if(file->data) {
        return 0;
    } else {
        return 1;
    }
}

void object_conf_ref(HTTPObjectConfig *conf) {
    if(conf) {
        ws_atomic_inc32(&conf->ref);
    }
}

void object_conf_unref(HTTPObjectConfig *conf) {
    uint32_t ref = ws_atomic_dec32(&conf->ref);
    if(ref == 0) {
        printf("free HTTPObjectConfig %"PRIxPTR"\n", (intptr_t)conf);
        pool_destroy(conf->pool);
    }
}

HTTPObjectConfig* load_obj_conf(char *file) {
    log_ereport(LOG_VERBOSE, "load_obj_conf");

    // new conf function test
    ObjectConfig *cfg = load_object_config(file);
    UcxAllocator *mp = cfg->parser.mp;
    if(cfg == NULL) {
        return NULL;
    }

    // create object config
    pool_handle_t *pool = pool_create();
    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
    conf->pool = pool;

    // convert ObjectConfig to HTTPObjectConfig

    // add objects
    conf->nobj = ucx_list_size(cfg->objects);
    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
    
    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(cob->ppath.length > 0) {
            ppath = sstrdup_pool(pool, cob->ppath).ptr;
        }

        // create and add object
        httpd_object *obj = object_new(pool, name);
        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(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, 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);
                if(!func_name) {
                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file);
                    return NULL;
                }
                d->func = get_function(func_name);
                if(!d->func) {
                    log_ereport(LOG_MISCONFIG, "func %s not found", func_name);
                    return NULL;
                }

                dirs = dirs->next;

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

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

    free_object_config(cfg);

    return conf;
}

int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
    MimeConfig *mimecfg = load_mime_config(file->file.ptr);
    MimeMap *old_conf = file->data;
    
    MimeMap *mimemap = malloc(sizeof(MimeMap));
    mimemap->ref = 1;
    UcxMap *map = ucx_map_new((mimecfg->ntypes * 3) / 2);
    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);
            ucx_map_sstr_put(map, ext, value.ptr);
        }
    }
    
    file->data = mimemap;
    
    if(old_conf) {
        mime_conf_unref(old_conf);
    }
    
    free_mime_config(mimecfg);
    return 0;
}

void mime_conf_ref(MimeMap *conf) {
    if(conf) {
        ws_atomic_inc32(&conf->ref);
    }
}

void mime_conf_unref(MimeMap *conf) {
    uint32_t ref = ws_atomic_dec32(&conf->ref);
    if(ref == 0) {
        printf("free MimeConfig %"PRIxPTR"\n", (intptr_t)conf);
        UcxMapIterator i = ucx_map_iterator(conf->map);
        char *str;
        UCX_MAP_FOREACH(key, str, i) {
            free(str);
        }
        ucx_map_free(conf->map);
        free(conf);
    }
}

int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
    ACLFile *aclfile = load_acl_file(file->file.ptr);
    
    ACLData *acldata = acl_data_new();
    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);
    
    ACLData *old_data = file->data;
    file->data = acldata;
    if(old_data) {
        acl_data_unref(old_data);
    }
    
    return 0;
}

ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
    WSAcl *acllist = malloc(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 **aces = calloc(s, sizeof(WSAce*));
    WSAce **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 = malloc(sizeof(WSAce));
        ace->access_mask = acecfg->access_mask;
        ace->flags = acecfg->flags;
        ace->type = acecfg->type;
        ace->who = sstrdup(acecfg->who).ptr;
        
        // add the entry to the correct array
        if(ace->type >= ACL_TYPE_AUDIT) {
            eces[ei] = ace;
            ei++;
        } else {
            aces[ai] = ace;
            ai++;
        }
    }
    
    // create new entrie arrays with perfect fitting size
    if(ai > 0) {
        acllist->ace = calloc(ai, sizeof(WSAce*));
    }
    if(ei > 0) {
        acllist->ece = calloc(ei, sizeof(WSAce*));
    }
    memcpy(acllist->ace, aces, ai*sizeof(WSAce*));
    memcpy(acllist->ece, eces, ei*sizeof(WSAce*));
    acllist->acenum = ai;
    acllist->ecenum = ei;
    
    free(aces);
    free(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(prompt_str).ptr;
            }
        }
    }
    
    return &acllist->acl;
}

int keyfile_reload(ConfigFile *file, ServerConfiguration *cfg) {
    KeyfileConfig *conf = load_keyfile_config(file->file.ptr);
    if(!conf) {
        return 1;
    }
    
    Keyfile *keyfile = keyfile_new();
    
    UCX_FOREACH(elm, conf->users) {
        KeyfileEntry *user = elm->data;
        keyfile_add_user(
                keyfile,
                user->name,
                user->hashtype,
                user->hashdata,
                user->groups,
                user->numgroups);
    }
    
    free_keyfile_config(conf);
    
    Keyfile *old_data = file->data;
    file->data = keyfile;
    if(old_data) {
        keyfile_unref(old_data);
    }
    
    return 0;
}


sstr_t cfg_load_file(sstr_t file) {
    sstr_t r;
    r.ptr = NULL;
    r.length = 0;
    
    if(!file.ptr) {
        return r;
    }
    
    sstr_t f = sstrdup(file);
    FILE *in = fopen(f.ptr, "r");
    if(!in) {
        return r;
    }
    
    UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
    if(!buf) {
        fclose(in);
        return r;
    }
    
    if(ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write) == 0) {
        fclose(in);
        ucx_buffer_free(buf);
        return r;
    }
    
    r.ptr = buf->space;
    r.length = buf->pos;
    
    free(buf);
    fclose(in);
    
    return r;
}

mercurial