Tue, 01 Nov 2022 15:39:21 +0100
fix invalid propfind pg queries created for allprop requests on collections
/* * 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(CfgManager *mgr, 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; } mgr->serverconf = serverconf; 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(serverconfig->a, 16); serverconfig->authdbs = ucx_map_new_a(serverconfig->a, 16); serverconfig->resources = ucx_map_new_a(serverconfig->a, 16); serverconfig->dav = ucx_map_new_a(serverconfig->a, 16); // STAGE 1 load_server_conf: // At stage 1 we load the file and get the Runtime infos for changing // the uid, which must be done before most steps. // Before the uid can be changed, we also need to bind listeners, // therefore we need to get the listener config and all dependencies. // // Runtime // Listener (dependencies: Threadpool, EventHandler) // load Runtime config UcxList *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); // load threadpool config log_ereport(LOG_DEBUG, "apply config: Threadpool"); 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; } // load eventhandler config log_ereport(LOG_DEBUG, "apply config: EventHandler"); 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); // load Listener config log_ereport(LOG_DEBUG, "apply config: Listener"); 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); // we return here, to let the webserver use the runtime info to // change the uid if needed return serverconfig; } ServerConfiguration* apply_server_conf(CfgManager *mgr) { ServerConfig *serverconf = mgr->serverconf; ServerConfiguration *serverconfig = mgr->cfg; /* * convert ServerConfig to ServerConfiguration * * its important to do this in the correct order: * LogFile (open log file first to log possible errors) * Threadpool * EventHandler * AuthDB * Listener (we set the VirtualServer later) * VirtualServer (dependencies: Listener) */ // init logfile first UcxList *list; log_ereport(LOG_DEBUG, "apply config: LogFile"); 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); log_ereport(LOG_DEBUG, "apply config: AccessLog"); 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); log_ereport(LOG_DEBUG, "apply config: AuthDB"); 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); log_ereport(LOG_DEBUG, "apply config: VirtualServer"); 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); log_ereport(LOG_DEBUG, "apply config: ResourcePool"); 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; } } log_ereport(LOG_DEBUG, "apply config: Dav"); 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")); int err = 0; if(file.ptr == NULL) { err = 1; log_ereport(LOG_MISCONFIG, "LogFile: parameter missing: File"); } if(lvl.ptr == NULL) { err = 1; log_ereport(LOG_MISCONFIG, "LogFile: parameter missing: Level"); } if(err) { 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)) { log_ereport(LOG_CATASTROPHE, "OOM"); ret = -1; break; } } if(ret) { break; } } cfg->mimetypes = mimemap; } else { log_ereport(LOG_CATASTROPHE, "OOM"); 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; }