Sun, 23 Nov 2025 14:59:52 +0100
implement Location AddACL directive
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2025 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 "location.h" #include "config.h" #include "request.h" #include "../util/util.h" #include "../util/pblock.h" #include "vserver.h" #include <cx/linked_list.h> #include <cx/array_list.h> #define DIR_CHECK_ARGC(n) if(argc != n) { \ log_ereport(LOG_FAILURE, "%s directive argc != %d", name.ptr, n); \ return 1; \ } static void location_list_add(WSLocation *location, WSLocation *sub) { void **begin = (void**)&location->children_begin; void **end = (void**)&location->children_end; cx_linked_list_add(begin, end, offsetof(WSLocation, prev), offsetof(WSLocation, next), sub); } static int add_location_config(ServerConfiguration *cfg, WSLocation *location, ConfigNode *dir) { const CxAllocator *a = cfg->a; cxmutstr name = dir->name; int argc = serverconfig_directive_count_args(dir); if(!cx_strcasecmp(name, "DirectoryIndex")) { DIR_CHECK_ARGC(1); location->config.set_dirindex = TRUE; location->config.dirindex = util_getboolean_s(cx_strcast(dir->args->value), FALSE); } else if(!cx_strcasecmp(name, "DocumentRoot")) { DIR_CHECK_ARGC(1); location->config.docroot = cx_strdup_a(a, dir->args->value); } else if(!cx_strcasecmp(name, "AddACL")) { DIR_CHECK_ARGC(1); if(!location->config.acls) { CxList *aclList = cxLinkedListCreate(cfg->a, NULL, CX_STORE_POINTERS); if(!aclList) { return 1; } location->config.acls = aclList; cxmutstr aclName = dir->args->value; if(cxListAdd(aclList, aclName.ptr)) { return 1; } } } else if(!cx_strcasecmp(name, "Location")) { WSLocation *sub_location = cfg_location_get(cfg, dir); if(!sub_location) { return 1; } location_list_add(location, sub_location); } return 0; } WSLocation* cfg_location_get(ServerConfiguration *cfg, ConfigNode *obj) { const CxAllocator *a = cfg->a; WSLocationMatch match = WS_LOCATION_MATCH_EXACT; cxmutstr match_str; int regex_flags = REG_EXTENDED | REG_NOSUB; int argc = serverconfig_directive_count_args(obj); if(argc == 2) { // arg0: match type cxmutstr type_str = obj->args->value; if(!cx_strcmp(type_str, "=")) { // noop } else if(!cx_strcmp(type_str, "^~")) { match = WS_LOCATION_MATCH_PREFIX; } else if(!cx_strcmp(type_str, "~")) { match = WS_LOCATION_MATCH_CS_REGEX; } else if(!cx_strcmp(type_str, "~*")) { match = WS_LOCATION_MATCH_CI_REGEX; regex_flags |= REG_ICASE; } else { log_ereport(LOG_FAILURE, "Location: unknown operator %.*s", (int)type_str.length, type_str.ptr); return NULL; } // arg1: match_string match_str = obj->args->next->value; } else if(argc == 1) { // arg0: match_string match_str = obj->args->value; } else if(argc > 2) { log_ereport(LOG_FAILURE, "Location directive must have 0-2 args"); return NULL; } WSLocation *location = cxZalloc(a, sizeof(WSLocation)); location->match = match; location->match_string = cx_strdup_a((a), match_str); if(match == WS_LOCATION_MATCH_CS_REGEX || match == WS_LOCATION_MATCH_CI_REGEX) { if(regcomp(&location->regex, match_str.ptr, regex_flags)) { log_ereport(LOG_FAILURE, "Location: cannot compile regex pattern %s", match_str.ptr); return NULL; } } ConfigNode *dir = obj->children_begin; while(dir) { if(dir->type == CONFIG_NODE_OBJECT || dir->type == CONFIG_NODE_DIRECTIVE) { if(add_location_config(cfg, location, dir)) { log_ereport(LOG_FAILURE, "Location %s: abort", match_str.ptr); return NULL; } } dir = dir->next; } return location; } int location_apply_config(WSLocationConfig *target, WSLocation *loc) { WSLocationConfig *src = &loc->config; if(src->set_dirindex) { target->set_dirindex = TRUE; target->dirindex = src->dirindex; } if(src->set_forcetls) { target->set_forcetls = TRUE; target->forcetls = src->forcetls; } if(src->docroot.ptr) { target->docroot = src->docroot; } if(src->name.ptr) { target->name = src->name; } if(src->dav) { target->dav = src->dav; } // TODO: ... return 0; } int location_match(WSLocation *loc, cxstring uri) { if(loc->match == WS_LOCATION_MATCH_EXACT) { return !cx_strcmp(loc->match_string, uri); } else if(loc->match == WS_LOCATION_MATCH_PREFIX) { return cx_strprefix(uri, loc->match_string); } else { return regexec(&loc->regex, uri.ptr, 0, NULL, 0) == 0; } return 0; } WSLocationConfig* location_match_and_get_config(pool_handle_t *pool, cxstring uri, WSLocation *loc) { WSLocationConfig *config = pool_malloc(pool, sizeof(WSLocationConfig)); if(!config) { return NULL; } ZERO(config, sizeof(WSLocationConfig)); while(loc) { if(location_match(loc, uri)) { if(location_apply_config(config, loc)) { return NULL; } } loc = loc->next; } return config; } WSLocationConfig* cfg_location_match(Session *sn, Request *rq) { NSAPIRequest *req = (NSAPIRequest*)rq; WSLocation *loc = req->vs->locations_begin; char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); return location_match_and_get_config(sn->pool, cx_str(uri), loc); }