#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 <cx/string.h>
#include <cx/utils.h>
#include <cx/hash_map.h>
#include <cx/linked_list.h>
#include <cx/compare.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 "cx/buffer.h"
static pool_handle_t *init_pool;
pool_handle_t* cfg_get_init_pool(
void) {
return init_pool;
}
char* cfg_config_file_path(
const char *file) {
cxstring base =
CX_STR(
"config/");
cxmutstr path = cx_strcat(
2, base, cx_str(file));
return path.ptr;
}
InitConfig* load_init_conf(
const char *file) {
log_ereport(
LOG_VERBOSE,
"load_init_conf");
InitConfig *cfg = initconfig_load(file);
if(cfg ==
NULL) {
log_ereport(
LOG_FAILURE,
"Cannot load init.conf");
return NULL;;
}
return cfg;
}
int apply_init_conf(InitConfig *cfg) {
init_pool = pool_create();
ConfigNode *dir = cfg->root->children_begin;
while(dir) {
if(dir->type !=
CONFIG_NODE_DIRECTIVE) {
dir = dir->next;
continue;
}
directive *d = pool_malloc(init_pool,
sizeof(directive));
d->param = pblock_create_pool(init_pool,
8);
ConfigParam *param = dir->args;
while(param !=
NULL) {
pblock_nvlinsert(
param->name.ptr,
param->name.length,
param->value.ptr,
param->value.length,
d->param);
param = param->next;
}
char *func_name = pblock_findval(
"fn", d->param);
d->func = get_function(func_name);
if(d->func ==
NULL) {
log_ereport(
LOG_MISCONFIG,
"Cannot find Init function %s",
func_name);
return 1;
}
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);
return 1;
}
dir = dir->next;
}
return 0;
}
void free_init_conf(InitConfig *cfg) {
initconfig_free(cfg);
}
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;
CxAllocator *allocator = pool_allocator(serverconfig->pool);
serverconfig->a = allocator;
serverconfig->listeners = cxLinkedListCreate(serverconfig->a,
NULL,
CX_STORE_POINTERS);
serverconfig->logfiles = cxLinkedListCreate(serverconfig->a,
NULL,
CX_STORE_POINTERS);
serverconfig->host_vs = cxHashMapCreate(serverconfig->a,
CX_STORE_POINTERS,
16);
serverconfig->authdbs = cxHashMapCreate(serverconfig->a,
CX_STORE_POINTERS,
16);
serverconfig->resources = cxHashMapCreate(serverconfig->a,
CX_STORE_POINTERS,
16);
serverconfig->dav = cxHashMapCreate(serverconfig->a,
CX_STORE_POINTERS,
16);
CxList *list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"Runtime"));
CxIterator iter = cxListIterator(list);
cx_foreach(ConfigNode *, runtimeobj, iter) {
if(cfg_handle_runtime(serverconfig, runtimeobj)) {
log_ereport(
LOG_FAILURE,
"server.conf runtime");
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: Threadpool");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"Threadpool"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, elm, iter) {
if(cfg_handle_threadpool(serverconfig, elm)) {
log_ereport(
LOG_FAILURE,
"server.conf threadpool");
return NULL;
}
}
cxListDestroy(list);
if(check_thread_pool_cfg() !=
0) {
return NULL;
}
log_ereport(
LOG_DEBUG,
"apply config: EventHandler");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"EventHandler"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, elm, iter) {
if(cfg_handle_eventhandler(serverconfig, elm)) {
log_ereport(
LOG_FAILURE,
"cannot create event handler");
return NULL;
}
}
if(check_event_handler_cfg() !=
0) {
return NULL;
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: Listener");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"Listener"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_listener(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
return serverconfig;
}
ServerConfiguration* apply_server_conf(CfgManager *mgr) {
ServerConfig *serverconf = mgr->serverconf;
ServerConfiguration *serverconfig = mgr->cfg;
CxList *list;
log_ereport(
LOG_DEBUG,
"apply config: LogFile");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"LogFile"));
CxIterator iter = cxListIterator(list);
cx_foreach(ConfigNode *, logobj, iter) {
if(!logobj) {
cxListDestroy(list);
return NULL;
}
int ret = cfg_handle_logfile(serverconfig, logobj);
if(ret !=
0) {
cxListDestroy(list);
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: AccessLog");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"AccessLog"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_accesslog(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: AuthDB");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"AuthDB"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_authdb(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: VirtualServer");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"VirtualServer"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_vs(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: ResourcePool");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"ResourcePool"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_resourcepool(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
log_ereport(
LOG_DEBUG,
"apply config: Dav");
list = serverconfig_get_node_list(serverconf->root,
CONFIG_NODE_OBJECT, cx_str(
"Dav"));
iter = cxListIterator(list);
cx_foreach(ConfigNode *, scfgobj, iter) {
if(cfg_handle_dav(serverconfig, scfgobj)) {
return NULL;
}
}
cxListDestroy(list);
CxList *ls = serverconfig->listeners;
iter = cxListIterator(ls);
cx_foreach(HttpListener *, listener, iter) {
cxstring vsname = cx_str(listener->default_vs.vs_name);
CxIterator map_iter = cxMapIteratorValues(serverconfig->host_vs);
cx_foreach(VirtualServer *, vs, map_iter) {
if(!cx_strcmp(vsname, (cxstring){vs->name.ptr, vs->name.length})) {
listener->default_vs.vs = vs;
break;
}
}
}
serverconfig_free(serverconf);
return serverconfig;
}
int migrate_server_conf(ServerConfiguration *old_cfg, ServerConfiguration *new_cfg) {
old_cfg->next = new_cfg;
cfg_ref(new_cfg);
CxIterator old_listeners = cxListIterator(old_cfg->listeners);
cx_foreach(HttpListener*, oldls, old_listeners) {
if(oldls->next) {
log_ereport(
LOG_WARN,
"migrate_server_conf: oldls->next not NULL");
continue;
}
CxIterator new_listeners = cxListIterator(new_cfg->listeners);
cx_foreach(HttpListener*, newls, new_listeners) {
if(http_listener_socket_eq(oldls, newls)) {
http_listener_set_next(oldls, newls);
break;
}
}
http_listener_shutdown_acceptors(oldls);
}
return 0;
}
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) {
if(cfg->next) {
cfg_unref(cfg->next);
}
log_ereport(
LOG_VERBOSE,
"destroy configuration %p", cfg);
CxIterator i = cxListIterator(cfg->listeners);
cx_foreach(HttpListener*, listener, i) {
http_listener_destroy(listener);
}
pool_destroy(cfg->pool);
}
}
void init_server_config_parser() {
}
int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) {
cxstring user = serverconfig_object_directive_value(obj, cx_str(
"User"));
if(user.ptr) {
cfg->user = cx_strdup_a(cfg->a, user);
}
cxstring tmp = serverconfig_object_directive_value(obj, cx_str(
"Temp"));
if(tmp.ptr) {
cfg->tmp = cx_strdup_a(cfg->a, tmp);
}
else {
log_ereport(
LOG_MISCONFIG,
"no temporary directory specified");
return -
1;
}
cxstring mf = serverconfig_object_directive_value(obj, cx_str(
"MimeFile"));
cxstring base = cx_str(
"config/");
cxmutstr file = cx_strcat(
2, base, mf);
if(mime_conf_load(cfg, file)) {
return -
1;
}
free(file.ptr);
return 0;
}
int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj) {
cxstring file = serverconfig_object_directive_value(obj, cx_str(
"File"));
cxstring lvl = serverconfig_object_directive_value(obj, cx_str(
"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;
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;
cxstring name = serverconfig_object_directive_value(obj, cx_str(
"Name"));
cxstring min = serverconfig_object_directive_value(obj, cx_str(
"MinThreads"));
cxstring max = serverconfig_object_directive_value(obj, cx_str(
"MaxThreads"));
cxstring stack = serverconfig_object_directive_value(obj, cx_str(
"StackSize"));
cxstring queue = serverconfig_object_directive_value(obj, cx_str(
"QueueSize"));
if(name.length ==
0) {
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;
cxstring name = serverconfig_object_directive_value(obj, cx_str(
"Name"));
cxstring threads = serverconfig_object_directive_value(obj, cx_str(
"Threads"));
cxstring isdefault = serverconfig_object_directive_value(obj, cx_str(
"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) {
cxstring name = serverconfig_object_directive_value(obj, cx_str(
"Name"));
cxstring type = serverconfig_object_directive_value(obj, cx_str(
"Type"));
int ret =
0;
if(resourcepool_new(cfg, type, name, obj)) {
ret =
1;
}
return ret;
}
int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) {
cxstring file = serverconfig_object_directive_value(obj, cx_str(
"File"));
if(file.ptr ==
NULL) {
return 0;
}
cxmutstr format;
format.ptr =
NULL;
format.length =
0;
LogFile *log_file = get_access_log_file(file);
if(!log_file) {
return 0;
}
AccessLog *log = pool_malloc(cfg->pool,
sizeof(AccessLog));
log->file = cx_strdup_a(cfg->a, file);
log->format = format;
log->log = log_file;
cxListAdd(cfg->logfiles, log);
if(!cfg->default_log) {
cfg->default_log = log;
}
return 0;
}
int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj) {
cxstring name = serverconfig_object_directive_value(obj, cx_str(
"Name"));
cxstring type = serverconfig_object_directive_value(obj, cx_str(
"Type"));
AuthDB *authdb =
NULL;
if(!cx_strcmp(type, cx_str(
"ldap"))) {
authdb = create_ldap_authdb(cfg, name.ptr, obj);
}
else if(!cx_strcmp(type, cx_str(
"keyfile"))) {
cxstring file = serverconfig_object_directive_value(obj, cx_str(
"File"));
if(file.length ==
0) {
log_ereport(
LOG_MISCONFIG,
"missing File parameter for keyfile authdb");
return 1;
}
authdb = keyfile_load(cfg, file);
}
if(authdb) {
if(cxMapPut(cfg->authdbs, cx_hash_key_bytes((
const unsigned char*)name.ptr, name.length), 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;
cxstring name = serverconfig_object_directive_value(obj, cx_str(
"Name"));
cxstring port = serverconfig_object_directive_value(obj, cx_str(
"Port"));
cxstring vs = serverconfig_object_directive_value(obj, cx_str(
"DefaultVS"));
cxstring thrp = serverconfig_object_directive_value(obj, cx_str(
"Threadpool"));
cxstring blck = serverconfig_object_directive_value(obj, cx_str(
"BlockingIO"));
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 = cx_strdup(name);
lc.port = port_value;
lc.vs = cx_strdup(vs);
lc.threadpool = cx_strdup(thrp);
lc.blockingio = util_getboolean_s(blck,
WS_FALSE);
cxstring ssl = serverconfig_object_directive_value(obj, cx_str(
"SSL"));
if(util_getboolean_s(ssl,
WS_FALSE)) {
cxstring cert = serverconfig_object_directive_value(obj, cx_str(
"Cert"));
cxstring privkey = serverconfig_object_directive_value(obj, cx_str(
"Key"));
cxstring chain = serverconfig_object_directive_value(obj, cx_str(
"CertChain"));
cxstring disableprot = serverconfig_object_directive_value(obj, cx_str(
"SSLDisableProtocol"));
WSBool config_ok =
WS_TRUE;
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;
}
int ret =
0;
HttpListener *listener = http_listener_create(&lc);
if(listener) {
listener->default_vs.vs_name = cx_strdup_a(cfg->a, (cxstring){lc.vs.ptr, lc.vs.length}).ptr;
cxListAdd(cfg->listeners, listener);
}
else {
ret =
1;
}
free(lc.name.ptr);
free(lc.vs.ptr);
free(lc.threadpool.ptr);
return 0;
}
int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) {
VirtualServer *vs = vs_new(cfg->pool);
vs->name = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str(
"Name")));
vs->host = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str(
"Host")));
vs->document_root = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str(
"DocRoot")));
cxstring objfile = serverconfig_object_directive_value(obj, cx_str(
"ObjectFile"));
cxstring aclfile = serverconfig_object_directive_value(obj, cx_str(
"ACLFile"));
cxstring base = cx_str(
"config/");
cxmutstr file = cx_strcat_a(cfg->a,
2, base, objfile);
HTTPObjectConfig *httpobj = objconf_load(cfg, file);
if(!httpobj) {
return -
1;
}
vs->objectfile = file;
vs->objects = httpobj;
cxmutstr acl_filepath = cx_strcat(
2, base, aclfile);
ACLData *acldata = acl_conf_load(cfg, acl_filepath.ptr);
free(acl_filepath.ptr);
if(!acldata) {
return -
1;
}
vs->acls = acldata;
vs->log = cfg->default_log;
cxMapPut(cfg->host_vs, cx_hash_key_bytes((
unsigned const char*)vs->host.ptr, vs->host.length), vs);
return 0;
}
int cfg_handle_dav(ServerConfiguration *cfg, ConfigNode *obj) {
CxAllocator *a = pool_allocator(cfg->pool);
CxList *backends = cxLinkedListCreate(a,
NULL,
CX_STORE_POINTERS);
int init_error;
char *uri =
NULL;
char *ppath =
NULL;
char *name =
NULL;
for(ConfigParam *arg=obj->args;arg;arg=arg->next) {
cxstring arg_name = (cxstring){ arg->name.ptr, arg->name.length };
if(arg->name.ptr ==
NULL) {
uri = arg->value.ptr;
}
else if(!cx_strcasecmp(arg_name, cx_str(
"uri"))) {
uri = arg->value.ptr;
}
else if(!cx_strcasecmp(arg_name, cx_str(
"ppath"))) {
ppath = arg->value.ptr;
}
else if(!cx_strcasecmp(arg_name, cx_str(
"name"))) {
name = arg->value.ptr;
}
}
if(!uri && !ppath && !name) {
return 1;
}
for(ConfigNode *node=obj->children_begin;node;node=node->next) {
cxstring node_name = cx_strn(node->name.ptr, node->name.length);
if(!cx_strcasecmp(node_name, cx_str(
"DavBackend"))) {
if(node->type ==
CONFIG_NODE_DIRECTIVE) {
if(
CFG_NUM_PARAMS(node->args) ==
1) {
cxListAdd(backends, node->args);
}
else {
log_ereport(
LOG_MISCONFIG,
"DavBackend must have only one value");
cxListDestroy(backends);
return 1;
}
}
else {
log_ereport(
LOG_MISCONFIG,
"DavBackend must be a directive");
cxListDestroy(backends);
return 1;
}
}
}
int ret =
0;
WebdavRepository *repository = pool_malloc(cfg->pool,
sizeof(WebdavRepository));
repository->vfs =
NULL;
repository->vfsInitData =
NULL;
repository->davBackends = cxLinkedListCreate(a,
NULL,
CX_STORE_POINTERS);
CxIterator i = cxListIterator(backends);
cx_foreach(ConfigParam *, backendArg, i) {
WebdavType *dav = webdav_get_type((cxstring){backendArg->value.ptr, backendArg->value.length});
if(!dav) {
log_ereport(
LOG_MISCONFIG,
"Unknown webdav backend type ''%s''", backendArg->value.ptr);
ret =
1;
break;
}
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;
cxListAdd(repository->davBackends, davInit);
}
cxListDestroy(backends);
cxstring vfs_class = serverconfig_object_directive_value(obj, cx_str(
"VFS"));
if(vfs_class.length >
0) {
VfsType *vfs = vfs_get_type((cxstring){vfs_class.ptr, vfs_class.length});
if(vfs) {
repository->vfs = vfs;
repository->vfsInitData = vfs_init_backend(cfg, cfg->pool, vfs, obj, &init_error);
if(!ret) {
ret = init_error;
}
}
else {
log_ereport(
LOG_FAILURE,
"Unknown vfs type ''%s''", vfs_class.ptr);
ret =
1;
}
}
cxstring object = serverconfig_object_directive_value(obj, cx_str(
"Object"));
if(object.length >
0) {
repository->object = cx_strdup_a(a, object);
if(repository->object.length != object.length) {
log_ereport(
LOG_FAILURE,
"Cannot create webdav repository: OOM");
ret =
1;
}
}
if(!ret) {
if(name) {
cxMapPut(cfg->dav, cx_hash_key_str(name), repository);
}
else {
log_ereport(
LOG_FAILURE,
"TODO: location based dav repositories not implemented");
ret =
1;
}
}
return ret;
}
#define OBJ_CONF_MAX_CONDITION_DEPTH 500
static int set_client_condition(
pool_handle_t *pool, ConfigNode *node, Condition *condition) {
return 0;
}
static int set_if_condition(
pool_handle_t *pool, ConfigNode *node, Condition *condition) {
CxList *tokens = cxLinkedListCreate(pool_allocator(pool),
NULL,
sizeof(cxmutstr));
ConfigParam *arg = node->args;
while(arg) {
if(arg->name.length >
0) {
cxListAdd(tokens, &arg->name);
cxmutstr op = (cxmutstr){
"=",
1 };
cxListAdd(tokens, &op);
}
if(cxListAdd(tokens, &arg->value)) {
cxListDestroy(tokens);
return 1;
}
arg = arg->next;
}
int ret =
0;
condition->expression = condition_create(pool, tokens);
if(!condition->expression) {
ret =
1;
}
cxListDestroy(tokens);
return ret;
}
static Condition* convert_objconf_condition(
pool_handle_t *pool,
ConfigNode *node,
Condition *prev_condition,
Condition *parent_condition,
int *condition_index)
{
const char *condnames[] = {
"Client",
"If",
"ElseIf",
"Else" };
size_t typeindex;
if(serverconfig_validate_directive_name(node, condnames,
4, &typeindex)) {
return NULL;
}
if((typeindex ==
2 || typeindex ==
3) && prev_condition ==
NULL) {
return NULL;
}
Condition *condition = pool_malloc(pool,
sizeof(Condition));
ZERO(condition,
sizeof(Condition));
condition->index = *condition_index;
condition->parent = parent_condition;
if(typeindex ==
0) {
if(set_client_condition(pool, node, condition)) {
return NULL;
}
}
else {
condition->ifnot = prev_condition;
if(typeindex !=
4 && set_if_condition(pool, node, condition)) {
return NULL;
}
}
(*condition_index)++;
return condition;
}
static int convert_objconf_directives(
pool_handle_t *pool,
const char *file,
httpd_object *obj,
ConfigNode *node,
Condition *parent_condition,
int *condition_index)
{
Condition *prev_condition =
NULL;
for(;node;node=node->next) {
if(node->type ==
CONFIG_NODE_OBJECT) {
Condition *condition = convert_objconf_condition(pool, node, prev_condition, parent_condition, condition_index);
if(!condition) {
return 1;
}
if(convert_objconf_directives(pool, file, obj, node->children_begin, condition, condition_index)) {
return 1;
}
if(!strcmp(node->name.ptr,
"If") || !strcmp(node->name.ptr,
"ElseIf")) {
prev_condition = condition;
}
else {
prev_condition =
NULL;
}
}
else if(node->type ==
CONFIG_NODE_DIRECTIVE) {
directive *d = pool_malloc(pool,
sizeof(directive));
if(!d)
return -
1;
d->param = pblock_create_pool(pool,
8);
d->cond = parent_condition;
ConfigParam *param = node->args;
while(param !=
NULL) {
pblock_nvlinsert(
param->name.ptr,
param->name.length,
param->value.ptr,
param->value.length,
d->param);
param = param->next;
}
char *func_name = pblock_findval(
"fn", d->param);
if(!func_name) {
log_ereport(
LOG_MISCONFIG,
"%s: Missing fn parameter", file);
return -
1;
}
d->func = get_function(func_name);
if(!d->func) {
log_ereport(
LOG_MISCONFIG,
"func %s not found", func_name);
return -
1;
}
int dir_type = cfg_get_directive_type_num(cx_strcast(node->name));
if(dir_type <
0) {
log_ereport(
LOG_MISCONFIG,
"unknown directive type %s", node->name);
}
object_add_directive(obj, d, dir_type);
prev_condition =
NULL;
}
}
return 0;
}
static int convert_objconf(ServerConfiguration *scfg, ObjectConfig2 *cfg, HTTPObjectConfig *conf, cxmutstr file) {
pool_handle_t *pool = conf->pool;
int condition_index =
0;
int i =
0;
for(ConfigNode *objnode=cfg->root->children_begin;objnode;objnode=objnode->next) {
if(objnode->type !=
CONFIG_NODE_OBJECT) {
if(objnode->type ==
CONFIG_NODE_DIRECTIVE) {
return 1;
}
continue;
}
if(strcmp(objnode->name.ptr,
"Object")) {
return 1;
}
cxmutstr cfg_name = cfg_param_get(objnode->args, cx_str(
"name"));
cxmutstr cfg_ppath = cfg_param_get(objnode->args, cx_str(
"ppath"));
char *name =
NULL;
char *ppath =
NULL;
if(cfg_name.length >
0) {
name = cx_strdup_pool(pool, cfg_name).ptr;
if(!name)
return -
1;
}
if(cfg_ppath.length >
0) {
ppath = cx_strdup_pool(pool, cfg_ppath).ptr;
if(!ppath)
return -
1;
}
httpd_object *obj = object_new(pool, name);
if(!obj)
return -
1;
obj->path =
NULL;
conf->objects[i] = obj;
if(convert_objconf_directives(pool, file.ptr, obj, objnode->children_begin,
NULL, &condition_index)) {
return 1;
}
i++;
}
return 0;
}
HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file) {
log_ereport(
LOG_VERBOSE,
"load_obj_conf");
int ret =
0;
pool_handle_t *pool = scfg->pool;
HTTPObjectConfig *conf = pool_calloc(pool,
sizeof(HTTPObjectConfig),
1);
if(!conf) {
return NULL;
}
conf->pool = pool;
ObjectConfig2 *cfg = objectconf_load(file.ptr);
if(!cfg) {
return NULL;
}
conf->nobj = serverconfig_children_count(cfg->root,
CONFIG_NODE_OBJECT);
conf->objects = pool_calloc(pool, conf->nobj,
sizeof(httpd_object*));
if(conf->objects) {
ret = convert_objconf(scfg, cfg, conf, file);
}
else {
ret = -
1;
}
objectconf_free(cfg);
return !ret ? conf :
NULL;
}
int mime_conf_load(ServerConfiguration *cfg, cxmutstr file) {
MimeConfig *mimecfg = load_mime_config(file.ptr);
if(!mimecfg) {
return -
1;
}
int ret =
0;
MimeMap *mimemap = cxMalloc(cfg->a,
sizeof(MimeMap));
CxMap *map = cxHashMapCreate(cfg->a,
CX_STORE_POINTERS, (mimecfg->ntypes *
3) /
2);
if(mimemap && map) {
mimemap->map = map;
for(MimeDirective *d=mimecfg->directives_begin;d;d=d->next) {
for(
int i=
0;i<d->nextensions;i++) {
cxstring ext = d->extensions[i];
cxmutstr value = cx_strdup(cx_strn(d->type.ptr, d->type.length));
if(cxMapPut(map, cx_hash_key_bytes((
const unsigned char *)ext.ptr, ext.length), 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,
const char *file) {
ACLFile *aclfile = load_acl_file(file);
if(!aclfile) {
log_ereport(
LOG_FAILURE,
"Cannot load acl file %s", file);
return NULL;
}
ACLData *acldata = acl_data_new(cfg->a);
CxIterator iter = cxListIterator(aclfile->namedACLs);
cx_foreach(ACLConfig *, ac, iter) {
ACLList *acl = acl_config_convert(cfg, ac);
log_ereport(
LOG_VERBOSE,
"add acl: %.*s", (
int)ac->id.length, ac->id.ptr);
cxMapPut(acldata->namedACLs, cx_hash_key(ac->id.ptr, ac->id.length), acl);
}
free_acl_file(aclfile);
return acldata;
}
ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
CxAllocator *a = cfg->a;
WSAcl *acllist = cxMalloc(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 && !cx_strcmp(cx_strn(acl->type.ptr, acl->type.length), cx_str(
"fs"))) {
acllist->acl.isextern =
1;
}
size_t s =
CFG_ACE_LIST_SIZE(acl->entries);
WSAce **tmp_aces = calloc(s,
sizeof(WSAce*));
WSAce **tmp_eces = calloc(s,
sizeof(WSAce*));
int ai =
0;
int ei =
0;
for(ACEConfig *acecfg=acl->entries;acecfg;acecfg=acecfg->next) {
WSAce *ace = cxMalloc(a,
sizeof(WSAce));
ace->access_mask = acecfg->access_mask;
ace->flags = acecfg->flags;
ace->type = acecfg->type;
ace->who = cx_strdup_a(a, cx_strcast(acecfg->who)).ptr;
if(ace->type >=
ACL_TYPE_AUDIT) {
tmp_eces[ei] = ace;
ei++;
}
else {
tmp_aces[ai] = ace;
ai++;
}
}
if(ai >
0) {
acllist->ace = cxCalloc(a, ai,
sizeof(WSAce*));
}
if(ei >
0) {
acllist->ece = cxCalloc(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);
if(acl->authparam) {
cxmutstr authdb_str = cfg_param_get(acl->authparam, cx_str(
"authdb"));
cxmutstr prompt_str = cfg_param_get(acl->authparam, cx_str(
"prompt"));
if(authdb_str.ptr) {
AuthDB *authdb = cxMapGet(cfg->authdbs, cx_hash_key(authdb_str.ptr, authdb_str.length));
acllist->acl.authdb = authdb;
if(authdb && prompt_str.ptr) {
acllist->acl.authprompt = cx_strdup_a(a, cx_strcast(prompt_str)).ptr;
}
}
}
return &acllist->acl;
}
AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring 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;
for(KeyfileEntry *user=conf->users_begin;user;user=user->next) {
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);
for(ConfigNode *d=obj->children_begin;d;d=d->next) {
if(d->type ==
CONFIG_NODE_DIRECTIVE && d->name.length >
0 &&
CFG_NUM_PARAMS(d->args) ==
1) {
ConfigParam *arg = d->args;
pblock_nvlinsert(d->name.ptr, d->name.length, arg->value.ptr, arg->value.length, pb);
}
}
return pb;
}