#include "location.h"
#include "config.h"
#include "request.h"
#include "rewrite.h"
#include "../util/util.h"
#include "../util/pblock.h"
#include "../util/pool.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,
"VFS")) {
DIR_CHECK_ARGC(
1);
location->config.vfs = cx_strdup_a(a, dir->args->value);
VfsType *vfs = vfs_get_type(cx_strcast(location->config.vfs));
if(!vfs) {
log_ereport(
LOG_MISCONFIG,
"unknown VFS type %s", location->config.vfs.ptr);
}
}
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);
}
else if(!cx_strcasecmp(name,
"Rewrite")) {
cxmutstr regex = (cxmutstr){
NULL,
0};
cxmutstr url = (cxmutstr){
NULL,
0};
if(argc ==
1) {
url = dir->args->value;
}
else if(argc ==
2) {
regex = dir->args->value;
url = dir->args->next->value;
}
else {
log_ereport(
LOG_FAILURE,
"rewrite [regex] <url>: illegal argc %d", argc);
return 1;
}
RewriteRule *rule = rewrite_rule_create(cfg, regex, url);
if(!rule) {
return 1;
}
if(!location->rewrite) {
location->rewrite = cxLinkedListCreate(a,
NULL,
CX_STORE_POINTERS);
if(!location->rewrite) {
return 1;
}
}
if(cxListAdd(location->rewrite, rule)) {
return 1;
}
}
return 0;
}
static void location_free_regex(WSLocation *loc) {
regfree(&loc->regex);
}
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;
int argc = serverconfig_directive_count_args(obj);
if(argc ==
2) {
cxmutstr type_str = obj->args->value;
if(!cx_strcmp(type_str,
"=")) {
}
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;
}
match_str = obj->args->next->value;
}
else if(argc ==
1) {
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;
}
ScfgDestr destr;
destr.destr = (cx_destructor_func)location_free_regex;
destr.data = location;
if(cxListAdd(cfg->destr, &destr)) {
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;
}
return 0;
}
int location_match(WSLocation *loc, cxstring uri,
regmatch_t *match) {
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,
WS_LOCATION_NMATCH, match,
0) ==
0;
}
return 0;
}
WSLocationConfig* location_match_and_get_config(
pool_handle_t *pool, Request *rq, cxstring uri, WSLocation *loc) {
WSLocationConfig *config = pool_malloc(pool,
sizeof(WSLocationConfig));
if(!config) {
return NULL;
}
ZERO(config,
sizeof(WSLocationConfig));
for(
int i=
0;i<
WS_LOCATION_NMATCH;i++) {
config->match[i].rm_so =
-1;
config->match[i].rm_eo =
-1;
}
const CxAllocator *a = pool_allocator(pool);
while(loc) {
if(location_match(loc, uri, config->match)) {
CxIterator iter = cxListIterator(loc->rewrite);
cx_foreach(RewriteRule *, rule, iter) {
char *new_url =
NULL;
if(!rewrite_url(rule, config->match,
WS_LOCATION_NMATCH, a, uri.ptr, &new_url)) {
pblock_removekey(pb_key_uri, rq->reqpb);
if(pblock_kvinsert(pb_key_uri, new_url, strlen(new_url), rq->reqpb) ==
NULL) {
return NULL;
}
}
}
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, rq, cx_str(uri), loc);
}