2 * mod_domaintree.c - Apache2
6 * Copyright 2005 Michael Wallner <mike@iworks.at>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
22 * DomainTreeEnabled On
23 * DomainTreeMaxdepth 25
24 * DomainTreeStripWWW On
25 * DomainTreePrefix /sites
26 * DomainTreeSuffix /html
28 * DomainTreeAliasRecursion Off
29 * DomainTreeAlias /??/exmaple /com/exmaple
30 * DomainTreeAlias /???/example /com/example
31 * DomainTreeAlias /*one/ /anyone/
55 #define MODULE "mod_domaintree"
56 #define AUTHOR "<mike@iworks.at"
64 #include "apr_tables.h"
65 #include "apr_strings.h"
71 #define APR_WANT_MEMFUNC
72 #define APR_WANT_STRFUNC
76 #include "http_config.h"
77 #include "http_core.h"
79 #include "http_protocol.h"
80 #include "http_request.h"
83 /* {{{ domaintree_module */
85 module AP_MODULE_DECLARE_DATA domaintree_module
;
88 /* {{{ Macros & Types */
90 #define MOD_DT_CNF domaintree_conf
91 #define MOD_DT_PTR (&domaintree_module)
93 #define GET_MOD_DT_CNF(srv) ((MOD_DT_CNF *) ap_get_module_config((srv)->module_config, MOD_DT_PTR))
96 #define EMPTY(str) ((str == NULL) || (*(str) == NUL))
97 #define local static APR_INLINE
99 #define DT_LOG_ERR ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, DT->server,
100 #define DT_LOG_WRN ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, DT->server,
101 #define DT_LOG_DBG ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server,
102 #define DT_LOG_END );
108 apr_table_t
*faketable
;
111 struct dircache_entry
{
122 apr_global_mutex_t
*lock
;
140 local
char *strtr(char *string
, char from
, char to
)
145 while ((ptr
= strchr(ptr
, from
))) {
154 #define TRIM_BOTH (TRIM_LEFT|TRIM_RIGHT)
155 local
char *trim(char *string
, size_t length
, char what
, int where
)
157 if (where
& TRIM_RIGHT
) {
158 while (length
-- && (string
[length
] == what
)) {
159 string
[length
] = NUL
;
162 if (where
& TRIM_LEFT
) {
163 while (*string
== what
) {
170 local
int strmatch(const char *match
, const char *string
, const char **begin
, const char **end
)
172 *begin
= *end
= NULL
;
179 while (*match
== '*' || *match
== '?') {
188 *end
= string
+ strlen(string
);
192 if (!(string
= strchr(string
, *match
))) {
207 if (*match
== *string
) {
227 local
char *struniqchr(char *string
, char uniq
)
232 if (*ptr
== uniq
&& *(ptr
+ 1) == uniq
) {
235 while (*(pos
+ 1) == uniq
) {
239 memmove(ptr
, pos
, strlen(pos
) + 1);
247 local
char *domaintree_host(apr_pool_t
*pool
, MOD_DT_CNF
*DT
, const char *host_name
)
252 if (EMPTY(host_name
)) {
254 "DomainTree: no host/server name"
260 host
= apr_pstrdup(pool
, host_name
);
262 /* check for :NN port */
263 if ((port
= strchr(host
, ':'))) {
270 /* strip leading & trailing dots */
271 host
= trim(host
, len
, '.', TRIM_BOTH
);
274 "DomainTree: host name = %s for %s", host
, host_name
280 local
char *domaintree_path(apr_pool_t
*pool
, MOD_DT_CNF
*DT
, const char *host_name
)
283 const char *host
= host_name
;
284 char *path
= NULL
, *host_ptr
;
286 while ((host_ptr
= strchr(host
, '.'))) {
288 /* check max depth */
289 if (++depth
> DT
->maxdepth
) {
291 "DomainTree: maxdepth exceeded (%ld)", DT
->maxdepth
297 if (host_ptr
- host
) {
300 if (DT
->stripwww
&& (depth
== 1) && (!strncmp(host
, "www.", sizeof("www")))) {
302 "DomainTree: strip www"
305 path
= apr_pstrcat(pool
, apr_pstrndup(pool
, host
, host_ptr
- host
), "/", path
, NULL
);
312 /* append last part if any and duplicate full path */
314 path
= apr_pstrcat(pool
, host
, "/", path
, NULL
);
318 "DomainTree: path name = %s for %s", path
, host_name
324 local
void domaintree_fake(apr_pool_t
*pool
, MOD_DT_CNF
*DT
, char **path
)
328 apr_pool_t
*local_pool
;
329 const apr_array_header_t
*header
= apr_table_elts(DT
->aliases
.faketable
);
330 apr_table_entry_t
*array
= (apr_table_entry_t
*) header
->elts
;
332 if (APR_SUCCESS
!= apr_pool_create(&local_pool
, pool
)) {
339 if (recurlevel
++ > DT
->aliases
.recursion
) {
341 "DomainTree: maximum alias recursion level (%ld) exceeded! "
342 "Check if you have recursive definitions of DomainTreeAlias directives.",
343 DT
->aliases
.recursion
348 for (i
= 0; i
< header
->nelts
; ++i
) {
349 const char *begin
, *end
;
352 "DomainTree: fake test = %s on %s", array
[i
].key
, *path
355 if (strmatch(array
[i
].key
, *path
, &begin
, &end
)) {
357 *path
= apr_pstrcat(local_pool
, "/", apr_pstrndup(local_pool
, *path
, begin
- *path
), "/", array
[i
].val
, "/", end
, NULL
);
360 "DomainTree: fake done = %s (%s <> %s)", *path
, array
[i
].key
, array
[i
].val
364 } while (more
&& DT
->aliases
.recursion
);
366 *path
= apr_pstrdup(pool
, struniqchr(*path
, '/'));
368 apr_pool_destroy(local_pool
);
371 local
char *domaintree_cache_get(MOD_DT_CNF
*DT
, apr_time_t atime
, const char *host
)
374 struct dircache_entry
*cache_entry
;
376 apr_global_mutex_lock(DT
->dircache
.lock
);
378 if ((cache_entry
= apr_hash_get(DT
->dircache
.hmap
, host
, APR_HASH_KEY_STRING
))) {
379 cache_entry
->lacc
= atime
;
380 path
= cache_entry
->path
;
383 apr_global_mutex_unlock(DT
->dircache
.lock
);
387 "DomainTree: cache hit = %s for %s", path
, host
394 local
void domaintree_cache_set(MOD_DT_CNF
*DT
, apr_time_t atime
, const char *host
, const char *path
)
397 struct dircache_entry
*cache_entry
;
399 apr_pool_create(&pool
, DT
->dircache
.pool
);
400 cache_entry
= apr_palloc(pool
, sizeof(struct dircache_entry
));
402 cache_entry
->pool
= pool
;
403 cache_entry
->lacc
= atime
;
404 cache_entry
->host
= apr_pstrdup(pool
, host
);
405 cache_entry
->path
= apr_pstrdup(pool
, path
);
407 apr_global_mutex_lock(DT
->dircache
.lock
);
409 if (apr_hash_count(DT
->dircache
.hmap
) >= DT
->dircache
.clim
) {
410 apr_hash_index_t
*idx
;
411 struct dircache_entry
*purge_this
= NULL
;
414 "DomainTree: reached cache limit (%ld)", DT
->dircache
.clim
417 for (idx
= apr_hash_first(DT
->dircache
.pool
, DT
->dircache
.hmap
); idx
; idx
= apr_hash_next(idx
)) {
418 struct dircache_entry
*current
;
420 apr_hash_this(idx
, NULL
, NULL
, (void **) ¤t
);
421 if ((!purge_this
) || (purge_this
->lacc
> current
->lacc
)) {
422 purge_this
= current
;
428 "DomainTree: cache del = %s", purge_this
->host
430 apr_hash_set(DT
->dircache
.hmap
, purge_this
->host
, APR_HASH_KEY_STRING
, NULL
);
431 apr_pool_destroy(purge_this
->pool
);
434 apr_hash_set(DT
->dircache
.hmap
, cache_entry
->host
, APR_HASH_KEY_STRING
, cache_entry
);
436 apr_global_mutex_unlock(DT
->dircache
.lock
);
439 "DomainTree: cache set = %s for %s", path
, host
446 static STATUS
domaintree_hook_post_config(apr_pool_t
*pconf
, apr_pool_t
*plog
, apr_pool_t
*ptemp
, server_rec
*s
)
448 ap_add_version_component(pconf
, "MDT/" VERSION
);
452 static STATUS
domaintree_hook_translate_name(request_rec
*r
)
454 MOD_DT_CNF
*DT
= NULL
;
455 char *host
, *path
, *docroot
;
457 if ((!(DT
= GET_MOD_DT_CNF(r
->server
))) || (!DT
->enabled
)) {
462 "DomainTree: processid = %d", (int) getpid()
465 /* get a usable host name */
466 if (!(host
= domaintree_host(r
->pool
, DT
, ap_get_server_name(r
)))) {
471 if ((!DT
->dircache
.clim
) || (!(path
= domaintree_cache_get(DT
, r
->request_time
, host
)))) {
473 if (!(path
= domaintree_path(r
->pool
, DT
, host
))) {
477 /* apply any aliases */
478 if (apr_table_elts(DT
->aliases
.faketable
)->nelts
) {
479 domaintree_fake(r
->pool
, DT
, &path
);
483 if (DT
->dircache
.clim
) {
484 domaintree_cache_set(DT
, r
->request_time
, host
, path
);
488 /* compose virtual docroot */
489 docroot
= struniqchr(apr_pstrcat(r
->pool
, DT
->prefix
, "/", path
, "/", DT
->suffix
, "/", NULL
), '/');
495 switch (apr_stat(&sb
, docroot
, APR_FINFO_MIN
, r
->pool
))
500 "DomainTree: stat path = %s (success)", docroot
506 "DomainTree: stat path = %s (failure)", docroot
513 /* set virtual docroot */
514 apr_table_set(r
->subprocess_env
, "VIRTUAL_DOCUMENT_ROOT", docroot
);
517 r
->canonical_filename
= "";
518 r
->filename
= apr_pstrcat(r
->pool
, docroot
, EMPTY(r
->uri
) ? NULL
: ('/' == *r
->uri
? r
->uri
+ 1 : r
->uri
), NULL
);
521 "DomainTree: path done = %s", r
->filename
527 static void domaintree_hooks(apr_pool_t
*pool
)
529 static const char * const pre
[] = {"mod_alias.c", "mod_userdir.c", NULL
};
531 ap_hook_post_config(domaintree_hook_post_config
, NULL
, NULL
, APR_HOOK_MIDDLE
);
532 ap_hook_translate_name(domaintree_hook_translate_name
, pre
, NULL
, APR_HOOK_FIRST
);
536 /* {{{ Configuration */
538 static void *domaintree_create_srv(apr_pool_t
*p
, server_rec
*s
)
542 DT
= (MOD_DT_CNF
*) apr_palloc(p
, sizeof(MOD_DT_CNF
));
550 DT
->prefix
= "/var/www";
551 DT
->suffix
= "public_html";
553 DT
->aliases
.recursion
= 0;
554 DT
->aliases
.faketable
= apr_table_make(p
, 0);
556 DT
->dircache
.clim
= 0;
557 DT
->dircache
.hmap
= apr_hash_make(p
);
558 apr_pool_create(&DT
->dircache
.pool
, p
);
559 apr_global_mutex_create(&DT
->dircache
.lock
, __FILE__
, APR_LOCK_DEFAULT
, p
);
564 static const char *domaintree_enable(cmd_parms
*cmd
, void *conf
, int flag
)
566 GET_MOD_DT_CNF(cmd
->server
)->enabled
= flag
;
570 static const char *domaintree_stripwww(cmd_parms
*cmd
, void *conf
, int flag
)
572 GET_MOD_DT_CNF(cmd
->server
)->stripwww
= flag
;
576 static const char *domaintree_statroot(cmd_parms
*cmd
, void *conf
, int flag
)
578 GET_MOD_DT_CNF(cmd
->server
)->statroot
= flag
;
582 static const char *domaintree_prefix(cmd_parms
*cmd
, void *conf
, const char *prefix
)
584 GET_MOD_DT_CNF(cmd
->server
)->prefix
= EMPTY(prefix
) ? "/" : trim(apr_pstrdup(cmd
->pool
, prefix
), strlen(prefix
), '/', TRIM_RIGHT
);
588 static const char *domaintree_suffix(cmd_parms
*cmd
, void *conf
, const char *suffix
)
590 GET_MOD_DT_CNF(cmd
->server
)->suffix
= EMPTY(suffix
) ? "" : trim(apr_pstrdup(cmd
->pool
, suffix
), strlen(suffix
), '/', TRIM_BOTH
);
594 static const char *domaintree_maxdepth(cmd_parms
*cmd
, void *conf
, const char *max_depth
)
598 if ((depth
= atol(max_depth
))) {
600 GET_MOD_DT_CNF(cmd
->server
)->maxdepth
= depth
;
602 return "Maximum DomainTree depth cannot be negative.";
609 static const char *domaintree_aliasrecursion(cmd_parms
*cmd
, void *conf
, const char *alias_recursion
)
613 if ((recursion
= atol(alias_recursion
))) {
614 if (recursion
> 0L) {
615 GET_MOD_DT_CNF(cmd
->server
)->aliases
.recursion
= recursion
;
617 return "DomainTree alias recursion cannot be negative.";
624 static const char *domaintree_alias(cmd_parms
*cmd
, void *conf
, const char *fake
, const char *real
)
626 char *f
= strtr(apr_pstrdup(cmd
->pool
, fake
), '.', '/'), *r
= strtr(apr_pstrdup(cmd
->pool
, real
), '.', '/');
628 apr_table_set(GET_MOD_DT_CNF(cmd
->server
)->aliases
.faketable
, trim(f
, strlen(f
), '/', TRIM_BOTH
), trim(r
, strlen(r
), '/', TRIM_BOTH
));
633 static const char *domaintree_cache(cmd_parms
*cmd
, void *conf
, const char *cache
)
637 if ((limit
= atol(cache
))) {
639 GET_MOD_DT_CNF(cmd
->server
)->dircache
.clim
= limit
;
641 return "DomainTree cache limit cannot be negative.";
651 static command_rec domaintree_commands
[] = {
653 "DomainTreeEnabled", domaintree_enable
, NULL
, RSRC_CONF
,
654 "Turn the module on or off."
658 "DomainTreeStripWWW", domaintree_stripwww
, NULL
, RSRC_CONF
,
659 "Strip leading www from host. (default On)"
663 "DomainTreeStatRoot", domaintree_statroot
, NULL
, RSRC_CONF
,
664 "Wheter to check for the evaluated virtual document root with a stat call. (default Off)"
668 "DomainTreePrefix", domaintree_prefix
, NULL
, RSRC_CONF
,
669 "DomainTree path prefix. (default /var/www) Do not forget the leading slash!"
673 "DomainTreeSuffix", domaintree_suffix
, NULL
, RSRC_CONF
,
674 "DomainTree path suffix. (default public_html)"
678 "DomainTreeMaxdepth", domaintree_maxdepth
, NULL
, RSRC_CONF
,
679 "DomainTree max path depth. (default 20)"
683 "DomainTreeAliasRecursion", domaintree_aliasrecursion
, NULL
, RSRC_CONF
,
684 "Whether (and how often at the maximum) DomainTree should walk recursively "
685 "through the aliases list as long as matching aliases are found. (default: 0 = turned off)"
689 "DomainTreeAlias", domaintree_alias
, NULL
, RSRC_CONF
,
690 "DomainTree aliases; e.g. DomainTreeAlias com/example/tickets com/example/support (dots or slashes equal)"
694 "DomainTreeCache", domaintree_cache
, NULL
, RSRC_CONF
,
695 "DomainTree server-wide host to directory cache; specify how many cache entries to allow (default: 0 = turned off)"
702 /* {{{ Module Administrativa */
704 module AP_MODULE_DECLARE_DATA domaintree_module
= {
705 STANDARD20_MODULE_STUFF
,
706 NULL
, /* create per-dir */
707 NULL
, /* merge per-dir */
708 domaintree_create_srv
, /* create per-server */
709 NULL
, /* merge per-server */
710 domaintree_commands
, /* config commands */
711 domaintree_hooks
/* hooks */
721 * vim600: noet sw=4 ts=4 fdm=marker
722 * vim<600: noet sw=4 ts=4