X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fmod-domaintree;a=blobdiff_plain;f=mod_domaintree.c;h=6cf8962c7afc304c16cd84b8e71550336ba4b3b4;hp=d282450ad808fc667db726d43092e1e484584e33;hb=8fdd0756bb6410f8238ad9a41b48a907b6a21f7e;hpb=8ff2bbbb7bf36e78359cdb4e9d410d65877598ae diff --git a/mod_domaintree.c b/mod_domaintree.c index d282450..6cf8962 100644 --- a/mod_domaintree.c +++ b/mod_domaintree.c @@ -3,6 +3,20 @@ * * $Id$ * + * Copyright 2005 Michael Wallner * DomainTreeEnabled On @@ -10,6 +24,10 @@ * DomainTreeStripWWW On * DomainTreePrefix /sites * DomainTreeSuffix /html + * DomainTreeAliasRecursion Off + * DomainTreeAlias /??/exmaple /com/exmaple + * DomainTreeAlias /???/example /com/example + * DomainTreeAlais /*one/ /anyone/ * * /sites * +- /at @@ -21,10 +39,8 @@ * | | +- /sub2 * | | +- /html * | +- /or - * | | +- /organisation - * | | +- /html - * | +- /example - * | +- /html + * | +- /organisation + * | +- /html * +- /com * +- /example * +- /html @@ -33,7 +49,7 @@ #define MODULE "mod_domaintree" #define AUTHOR "mike@php.net" -#define VERSION "1.0" +#define VERSION "1.2" /* {{{ Includes */ @@ -51,6 +67,7 @@ #include "apr.h" #include "apr_lib.h" #include "apr_ring.h" +#include "apr_hash.h" #include "apr_strings.h" #define APR_WANT_STRFUNC @@ -70,8 +87,18 @@ module AP_MODULE_DECLARE_DATA domaintree_module; #define NUL '\0' #define EMPTY(str) ((str == NULL) || (*(str) == NUL)) +#define DT_LOG_ERR ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, DT->server, +#define DT_LOG_WRN ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, DT->server, +#define DT_LOG_DBG ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, +#define DT_LOG_END ); + typedef int STATUS; +typedef struct { + apr_hash_t *hashtable; + size_t recursion; +} aliases_t; + typedef struct { server_rec *server; int enabled; @@ -79,6 +106,7 @@ typedef struct { size_t maxdepth; char *prefix; char *suffix; + aliases_t aliases; } domaintree_conf; struct domaintree_entry { @@ -90,53 +118,183 @@ APR_RING_HEAD(domaintree, domaintree_entry); /* }}} */ /* {{{ Helpers */ +static APR_INLINE char *strtr(char *string, char from, char to) +{ + char *ptr = string; + + if (from != to) { + while ((ptr = strchr(ptr, from))) { + *ptr = to; + } + } + return string; +} + +static APR_INLINE char *trim(char *string, size_t length, char what, int l, int r) +{ + if (r) { + while (length-- && (string[length] == what)) { + string[length] = NUL; + } + } + if (l) { + while (*string == what) { + ++string; + } + } + return string; +} + +static APR_INLINE char *strcase(char *string, int case_type) +{ +#ifndef CASE_LOWER +# define CASE_LOWER 1 +#endif +#ifndef CASE_UPPER +# define CASE_UPPER 2 +#endif + + char *ptr = string; + + switch (case_type) + { + case CASE_LOWER: + while (*ptr) { + apr_tolower(*ptr++); + } + break; + + case CASE_UPPER: + while (*ptr) { + apr_toupper(*ptr++); + } + break; + + default: + break; + } + return string; +} + +static APR_INLINE int strmatch(char *match, char *string, char **begin, char **end) +{ + *begin = *end = NULL; + + while (*match) + { + switch (*match) + { + case '*': + while (*match == '*' || *match == '?') { + ++match; + } + + if (!*begin) { + *begin = string; + } + + if (!*match) { + *end = string + strlen(string); + return 1; + } + + if (!(string = strchr(string, *match))) { + *end = string; + return 0; + } + break; + + case '?': + if (!*begin) { + *begin = string; + } + ++string; + ++match; + break; + + default: + if (*match == *string) { + if (!*begin) { + *begin = string; + } + ++match; + } else { + if (*begin) { + *end = string - 1; + return 0; + } + } + ++string; + break; + } + } + + *end = string; + return 1; +} + +static APR_INLINE char *struniqchr(char *string, char uniq) +{ + char *ptr = string; + + while (*ptr) { + if (*ptr == uniq && *(ptr + 1) == uniq) { + char *pos = ptr + 1; + + while (*(pos + 1) == uniq) { + ++pos; + } + + memmove(ptr, pos, strlen(pos) + 1); + } + ++ptr; + } + + return string; +} + static APR_INLINE char *domaintree_host(apr_pool_t *pool, MOD_DT_CNF *DT, const char *name) { if (EMPTY(name)) { - ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, DT->server, "DomainTree: no host/server name"); + DT_LOG_WRN + "DomainTree: no host/server name" + DT_LOG_END return NULL; } else { size_t len; char *port, *ptr, *host; - + ptr = host = apr_pstrdup(pool, name); - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host name = %s", host); - + + DT_LOG_DBG + "DomainTree: host name = %s", host + DT_LOG_END + /* check for :NN port */ if ((port = strchr(ptr, ':'))) { len = port - ptr; } else { len = strlen(ptr); } - - /* strip leading and trailing dots */ - while (ptr[len - 1] == '.') { - --len; - } - while (*ptr == '.') { - ++ptr; - --len; - } - host = ptr; - - /* terminate & lowercase */ - ptr[len] = NUL; - while (*ptr) { - apr_tolower(*ptr++); - } - - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: sane host = %s", host); + + /* strip leading & trailing dots, then lowercase */ + ptr = host = strcase(trim(ptr, len, '.', 1, 1), CASE_LOWER); + + DT_LOG_DBG + "DomainTree: sane host = %s", host + DT_LOG_END + return host; } } -static APR_INLINE const char *domaintree_append(apr_pool_t *pool, struct domaintree *tree, const char *name, size_t length) +static APR_INLINE const char *domaintree_elem(apr_pool_t *pool, struct domaintree *tree, const char *name, size_t length) { struct domaintree_entry *elem = apr_palloc(pool, sizeof(struct domaintree_entry)); - + APR_RING_ELEM_INIT(elem, link); APR_RING_INSERT_HEAD(tree, elem, domaintree_entry, link); - + return elem->name = apr_pstrndup(pool, name, length); } @@ -145,51 +303,100 @@ static APR_INLINE struct domaintree *domaintree_tree(apr_pool_t *pool, MOD_DT_CN size_t depth = 0; char *host_ptr = host; struct domaintree *tree = apr_palloc(pool, sizeof(struct domaintree)); - + APR_RING_INIT(tree, domaintree_entry, link); - + while ((host_ptr = strchr(host, '.'))) { - + /* check max depth */ if (++depth > DT->maxdepth) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, DT->server, "DomainTree: maxdepth exceeded = %s", host); + DT_LOG_ERR + "DomainTree: maxdepth exceeded = %s", host + DT_LOG_END return NULL; } - + /* append part */ if (host_ptr - host) { - + /* strip WWW */ if (DT->stripwww && (depth == 1) && (!strncmp(host, "www.", sizeof("www")))) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: stripping www."); + DT_LOG_DBG + "DomainTree: stripping www." + DT_LOG_END } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host part (%d) = %s", - depth - 1, domaintree_append(pool, tree, host, host_ptr - host) - ); + DT_LOG_DBG + "DomainTree: host part (%d) = %s", depth - 1, + domaintree_elem(pool, tree, host, host_ptr - host) + DT_LOG_END } } - + host = host_ptr + 1; } - + /* append last part */ if (!EMPTY(host)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host part (%d) = %s", - depth, domaintree_append(pool, tree, host, strlen(host)) - ); + DT_LOG_DBG + "DomainTree: host part (%d) = %s", depth, + domaintree_elem(pool, tree, host, strlen(host)) + DT_LOG_END } - + return tree; } static APR_INLINE char *domaintree_path(apr_pool_t *pool, MOD_DT_CNF *DT, struct domaintree *tree) { struct domaintree_entry *elem; - char *path = apr_pstrdup(pool, DT->prefix); + char *path = ""; + APR_RING_FOREACH(elem, tree, domaintree_entry, link) { path = apr_pstrcat(pool, path, "/", elem->name, NULL); } - return path = apr_pstrcat(pool, path, "/", DT->suffix, NULL); + + return path; +} + +static APR_INLINE void domaintree_fake(apr_pool_t *pool, MOD_DT_CNF *DT, char **path) +{ + int more; + apr_hash_index_t *idx; + size_t recurlevel = 0; + + do { + more = 0; + + if (recurlevel++ > DT->aliases.recursion) { + DT_LOG_ERR + "DomainTree: maximum alias recursion level (%d) exceeded! " + "Check if you have recursive definitions of DomainTreeAlias directives.", + DT->aliases.recursion + DT_LOG_END + break; + } + + for (idx = apr_hash_first(pool, DT->aliases.hashtable); idx; idx = apr_hash_next(idx)) { + char *fake, *real, *begin, *end; + + apr_hash_this(idx, (const void **) &fake, NULL, (void **) &real); + + DT_LOG_DBG + "DomainTree: fake test %s on %s", fake, *path + DT_LOG_END + + if (strmatch(fake, *path, &begin, &end)) { + *path = apr_pstrcat(pool, "/", apr_pstrndup(pool, *path, begin - *path), "/", real, "/", end, NULL); + struniqchr(*path, '/'); + + DT_LOG_DBG + "DomainTree: fake done %s<>%s = %s", fake, real, *path + DT_LOG_END + + more = 1; + } + } + } while (more && DT->aliases.recursion); } /* }}} */ @@ -206,32 +413,39 @@ static STATUS domaintree_hook_translate_name(request_rec *r) MOD_DT_CNF *DT = NULL; struct domaintree *tree; char *path, *host; - + DT = ap_get_module_config(r->server->module_config, MOD_DT_PTR); if ((!DT) || (!DT->enabled)) { return DECLINED; } - + /* get a usable host name */ if (!(host = domaintree_host(r->pool, DT, ap_get_server_name(r)))) { return DECLINED; } - + /* build domain tree */ if (!(tree = domaintree_tree(r->pool, DT, host))) { return DECLINED; } - + /* build path */ if (!(path = domaintree_path(r->pool, DT, tree))) { return DECLINED; } - + + /* apply any aliases */ + domaintree_fake(r->pool, DT, &path); + /* done */ r->canonical_filename = ""; - r->filename = apr_pstrcat(r->pool, path, r->uri, NULL); - ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: final path = %s", r->filename); - + r->filename = apr_pstrcat(r->pool, DT->prefix, "/", path, "/", DT->suffix, r->uri, NULL); + struniqchr(r->filename, '/'); + + DT_LOG_DBG + "DomainTree: path done = %s", r->filename + DT_LOG_END + return OK; } @@ -247,66 +461,69 @@ static void domaintree_hooks(apr_pool_t *pool) static void *domaintree_create_srv(apr_pool_t *p, server_rec *s) { MOD_DT_CNF *DT; - + DT = (MOD_DT_CNF *) apr_palloc(p, sizeof(MOD_DT_CNF)); - + DT->server = s; - DT->enabled = 1; + DT->enabled = 0; DT->stripwww = 1; DT->maxdepth = 20; - + DT->prefix = "/var/www"; DT->suffix = "public_html"; - + + DT->aliases.hashtable = apr_hash_make(p); + DT->aliases.recursion = 0; + return DT; } static const char *domaintree_enable(cmd_parms *cmd, void *conf, int flag) { MOD_DT_CNF *DT; - + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); DT->enabled = flag; - + return NULL; } static const char *domaintree_stripwww(cmd_parms *cmd, void *conf, int flag) { MOD_DT_CNF *DT; - + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); DT->stripwww = flag; - + return NULL; } static const char *domaintree_prefix(cmd_parms *cmd, void *conf, const char *prefix) { MOD_DT_CNF *DT; - + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); - DT->prefix = EMPTY(prefix) ? "/" : apr_pstrdup(cmd->pool, prefix); - + DT->prefix = EMPTY(prefix) ? "/" : trim(apr_pstrdup(cmd->pool, prefix), strlen(prefix), '/', 0, 1); + return NULL; } static const char *domaintree_suffix(cmd_parms *cmd, void *conf, const char *suffix) { MOD_DT_CNF *DT; - + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); - DT->suffix = EMPTY(suffix) ? "/" : apr_pstrdup(cmd->pool, suffix); - + DT->suffix = EMPTY(suffix) ? "" : trim(apr_pstrdup(cmd->pool, suffix), strlen(suffix), '/', 1, 1); + return NULL; } static const char *domaintree_maxdepth(cmd_parms *cmd, void *conf, const char *max_depth) { - int depth; - - if ((depth = atoi(max_depth))) { - if (depth > 0) { + long depth; + + if ((depth = atol(max_depth))) { + if (depth > 0L) { MOD_DT_CNF *DT; DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); DT->maxdepth = (size_t) depth; @@ -314,10 +531,39 @@ static const char *domaintree_maxdepth(cmd_parms *cmd, void *conf, const char *m return "Maximum DomainTree depth cannot be negative."; } } + + return NULL; +} + +static const char *domaintree_aliasrecursion(cmd_parms *cmd, void *conf, const char *alias_recursion) +{ + long recursion; + + if ((recursion = atol(alias_recursion))) { + if (recursion > 0L) { + MOD_DT_CNF *DT; + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); + DT->aliases.recursion = (size_t) recursion; + } else { + return "DomainTree alias recursion cannot be negative."; + } + } + + return NULL; +} +static const char *domaintree_alias(cmd_parms *cmd, void *conf, const char *fake, const char *real) +{ + MOD_DT_CNF *DT; + char *f = strtr(apr_pstrdup(cmd->pool, fake), '.', '/'), *r = strtr(apr_pstrdup(cmd->pool, real), '.', '/'); + + DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR); + apr_hash_set(DT->aliases.hashtable, trim(f, strlen(f), '/', 1, 1), APR_HASH_KEY_STRING, trim(r, strlen(r), '/', 1, 1)); + return NULL; } + /* }}} */ /* {{{ Commands */ @@ -334,7 +580,7 @@ static command_rec domaintree_commands[] = { AP_INIT_TAKE1( "DomainTreePrefix", domaintree_prefix, NULL, RSRC_CONF, - "DomainTree path prefix. (default /var/www)" + "DomainTree path prefix. (default /var/www) Do not forget the leading slash!" ), AP_INIT_TAKE1( @@ -347,6 +593,17 @@ static command_rec domaintree_commands[] = { "DomainTree max path depth. (default 20)" ), + AP_INIT_TAKE1( + "DomainTreeAliasRecursion", domaintree_aliasrecursion, NULL, RSRC_CONF, + "Whether (and how often at the maximum) DomainTree should walk recursively " + "through the aliases list as long as matching aliases are found. (Default: 0 = turned off)" + ), + + AP_INIT_TAKE2( + "DomainTreeAlias", domaintree_alias, NULL, RSRC_CONF, + "DomainTree aliases; e.g. DomainTreeAlias com/example/tickets com/example/support (dots or slashes equal)" + ), + { NULL } }; @@ -360,7 +617,7 @@ module AP_MODULE_DECLARE_DATA domaintree_module = { domaintree_create_srv, /* create per-server */ NULL, /* merge per-server */ domaintree_commands, /* apr_table_t commands */ - domaintree_hooks /* register hooks */ + domaintree_hooks /* hooks */ }; /* }}} */