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/
32 * DomainTreeIgnore *.foo.com *.foo.co.uk
33 * DomainTreeForbid html.*
34 * <Directory "/sites/com/example/users">
35 * # e.g. a symlink to /home
61 #define MODULE "mod_domaintree"
62 #define AUTHOR "<mike@iworks.at>"
70 #include "apr_tables.h"
71 #include "apr_strings.h"
77 #define APR_WANT_MEMFUNC
78 #define APR_WANT_STRFUNC
82 #include "http_config.h"
83 #include "http_core.h"
85 #include "http_protocol.h"
86 #include "http_request.h"
89 /* {{{ domaintree_module */
91 module AP_MODULE_DECLARE_DATA domaintree_module
;
94 /* {{{ Macros & Types */
96 #ifndef HAVE_UNIX_SUEXEC
98 # define HAVE_UNIX_SUEXEC
101 #ifdef HAVE_UNIX_SUEXEC
107 #define MDT_CNF domaintree_conf
108 #define MDT_PTR (&domaintree_module)
110 #define GET_MDT_CNF(srv) ((MDT_CNF *) ap_get_module_config((srv)->module_config, MDT_PTR))
112 #define IF_SET_ELSE(a, b) (a != -1) ? (a) : (b)
115 #define EMPTY(str) ((str == NULL) || (*(str) == NUL))
117 # define local static
119 # define local static APR_INLINE
122 #define DT_LOG_ERR APLOG_MARK, APLOG_ERR, APR_SUCCESS, DT->server, "DomainTree: "
123 #define DT_LOG_WRN APLOG_MARK, APLOG_WARNING, APR_SUCCESS, DT->server, "DomainTree: "
124 #define DT_LOG_DBG APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: "
130 apr_table_t
*faketable
;
144 apr_global_mutex_t
*lock
;
147 typedef apr_array_header_t
*hostlist_t
, *pathlist_t
;
161 #ifdef HAVE_UNIX_SUEXEC
169 local
char *domaintree_cache_get(MDT_CNF
*DT
, apr_time_t atime
, const char *host
)
172 dircache_entry_t
*cache_entry
;
174 apr_global_mutex_lock(DT
->dircache
.lock
);
176 if ((cache_entry
= apr_hash_get(DT
->dircache
.hmap
, host
, APR_HASH_KEY_STRING
))) {
177 cache_entry
->lacc
= atime
;
178 path
= cache_entry
->path
;
181 apr_global_mutex_unlock(DT
->dircache
.lock
);
184 ap_log_error(DT_LOG_DBG
"cache hit = %s for %s", path
, host
);
190 local
void domaintree_cache_set(MDT_CNF
*DT
, apr_time_t atime
, const char *host
, const char *path
)
193 dircache_entry_t
*cache_entry
;
195 apr_pool_create(&pool
, DT
->dircache
.pool
);
196 cache_entry
= apr_palloc(pool
, sizeof(dircache_entry_t
));
198 cache_entry
->pool
= pool
;
199 cache_entry
->lacc
= atime
;
200 cache_entry
->host
= apr_pstrdup(pool
, host
);
201 cache_entry
->path
= apr_pstrdup(pool
, path
);
203 apr_global_mutex_lock(DT
->dircache
.lock
);
205 if (apr_hash_count(DT
->dircache
.hmap
) >= DT
->dircache
.clim
) {
206 apr_hash_index_t
*idx
;
207 dircache_entry_t
*purge_this
= NULL
;
209 ap_log_error(DT_LOG_WRN
"reached cache limit (%ld)", DT
->dircache
.clim
);
211 for (idx
= apr_hash_first(DT
->dircache
.pool
, DT
->dircache
.hmap
); idx
; idx
= apr_hash_next(idx
)) {
212 dircache_entry_t
*current
;
214 apr_hash_this(idx
, NULL
, NULL
, (void *) ¤t
);
215 if ((!purge_this
) || (purge_this
->lacc
> current
->lacc
)) {
216 purge_this
= current
;
221 ap_log_error(DT_LOG_DBG
"cache del = %s", purge_this
->host
);
222 apr_hash_set(DT
->dircache
.hmap
, purge_this
->host
, APR_HASH_KEY_STRING
, NULL
);
223 apr_pool_destroy(purge_this
->pool
);
226 apr_hash_set(DT
->dircache
.hmap
, cache_entry
->host
, APR_HASH_KEY_STRING
, cache_entry
);
228 apr_global_mutex_unlock(DT
->dircache
.lock
);
230 ap_log_error(DT_LOG_DBG
"cache set = %s for %s", path
, host
);
236 local
char *strtr(char *string
, char from
, char to
)
241 while ((ptr
= strchr(ptr
, from
))) {
250 #define TRIM_BOTH (TRIM_LEFT|TRIM_RIGHT)
251 local
char *trim(char *string
, size_t length
, char what
, int where
)
253 if (where
& TRIM_RIGHT
) {
254 while (length
-- && (string
[length
] == what
)) {
255 string
[length
] = NUL
;
258 if (where
& TRIM_LEFT
) {
259 while (*string
== what
) {
266 local
int strmatch(const char *match
, const char *string
, const char **begin
, const char **end
)
268 *begin
= *end
= NULL
;
270 while (*match
&& *string
)
275 while (*match
== '*' || *match
== '?') {
284 *end
= string
+ strlen(string
);
288 if (!(string
= strchr(string
, *match
))) {
303 if (*match
== *string
) {
323 local
char *struniqchr(char *string
, char uniq
)
328 if (ptr
[0] == uniq
&& ptr
[1] == uniq
) {
331 for (; pos
[1] == uniq
; ++pos
);
332 for (; pos
[0]; ++pos
) {
342 local
char *domaintree_host(apr_pool_t
*pool
, MDT_CNF
*DT
, const char *host_name
)
347 if (EMPTY(host_name
)) {
348 ap_log_error(DT_LOG_WRN
"no host/server name");
353 host
= apr_pstrdup(pool
, host_name
);
355 /* check for :NN port */
356 if ((port
= strchr(host
, ':'))) {
363 /* strip leading & trailing dots */
364 host
= trim(host
, len
, '.', TRIM_BOTH
);
366 ap_log_error(DT_LOG_DBG
"host name = %s for %s", host
, host_name
);
371 local
char *domaintree_path(apr_pool_t
*pool
, MDT_CNF
*DT
, const char *host_name
)
373 long depth
= 0, maxdepth
= IF_SET_ELSE(DT
->maxdepth
, 20);
374 const char *host
= host_name
;
375 char *path
= NULL
, *host_ptr
;
377 while ((host_ptr
= strchr(host
, '.'))) {
379 /* check max depth */
380 if (++depth
> maxdepth
) {
381 ap_log_error(DT_LOG_ERR
"maxdepth exceeded (%ld)", maxdepth
);
386 if (host_ptr
- host
) {
389 if ((DT
->stripwww
> 0) && (depth
== 1) && (!strncmp(host
, "www.", sizeof("www")))) {
390 ap_log_error(DT_LOG_DBG
"strip www");
392 path
= apr_pstrcat(pool
, apr_pstrndup(pool
, host
, host_ptr
- host
), "/", path
, NULL
);
399 /* append last part if any and duplicate full path */
401 path
= apr_pstrcat(pool
, host
, "/", path
, NULL
);
404 ap_log_error(DT_LOG_DBG
"path name = %s for %s", path
, host_name
);
409 local
void domaintree_fake(apr_pool_t
*pool
, MDT_CNF
*DT
, char **path
)
413 apr_pool_t
*local_pool
;
414 const apr_array_header_t
*header
= apr_table_elts(DT
->aliases
.faketable
);
415 apr_table_entry_t
*array
= (apr_table_entry_t
*) header
->elts
;
417 if (APR_SUCCESS
!= apr_pool_create(&local_pool
, pool
)) {
424 if (recurlevel
++ > DT
->aliases
.recursion
) {
425 ap_log_error(DT_LOG_ERR
"maximum alias recursion level (%ld) exceeded! Check if you have recursive definitions of DomainTreeAlias directives.", DT
->aliases
.recursion
);
429 for (i
= 0; i
< header
->nelts
; ++i
) {
430 const char *begin
, *end
;
432 ap_log_error(DT_LOG_DBG
"fake test = %s on %s", array
[i
].key
, *path
);
433 if (strmatch(array
[i
].key
, *path
, &begin
, &end
)) {
434 ap_log_error(DT_LOG_DBG
"fake done = %s (%s <> %s)", *path
, array
[i
].key
, array
[i
].val
);
435 *path
= apr_pstrcat(local_pool
, "/", apr_pstrndup(local_pool
, *path
, begin
- *path
), "/", array
[i
].val
, "/", end
, NULL
);
439 } while (more
&& (DT
->aliases
.recursion
> 0));
441 *path
= apr_pstrdup(pool
, struniqchr(*path
, '/'));
443 apr_pool_destroy(local_pool
);
446 #define TEST_IS_BOS 1
447 #define TEST_IS_EOS 2
448 #define TEST_IS_AOS 3
449 local
int domaintree_test(MDT_CNF
*DT
, const char *host
, int argc
, const char **argv
, int flags
, const char **bos
, const char **eos
)
453 const char *begin
, *end
, *host_end
= host
+ strlen(host
);
455 for (i
= 0; i
< argc
; ++i
) {
456 ap_log_error(DT_LOG_DBG
"host test = %s <> %s", argv
[i
], host
);
457 if (strmatch(argv
[i
], host
, &begin
, &end
)) {
458 if ((flags
& TEST_IS_BOS
) && begin
!= host
) {
461 if ((flags
& TEST_IS_EOS
) && end
!= host_end
) {
470 ap_log_error(DT_LOG_DBG
"test done = %s by %s", host
, argv
[i
]);
481 static STATUS
domaintree_hook_post_config(apr_pool_t
*pconf
, apr_pool_t
*plog
, apr_pool_t
*ptemp
, server_rec
*s
)
483 ap_add_version_component(pconf
, "MDT/" VERSION
);
487 static STATUS
domaintree_hook_translate_name(request_rec
*r
)
490 char *host
, *path
, *docroot
;
492 if ((!(DT
= GET_MDT_CNF(r
->server
))) || (DT
->enabled
< 1)) {
497 ap_log_error(DT_LOG_DBG
"processid = %d", (int) getpid());
500 /* get a usable host name */
501 if (!(host
= domaintree_host(r
->pool
, DT
, ap_get_server_name(r
)))) {
506 if (domaintree_test(DT
, host
, DT
->ignore
->nelts
, (const char **) DT
->ignore
->elts
, TEST_IS_AOS
, NULL
, NULL
)) {
511 if (domaintree_test(DT
, host
, DT
->forbid
->nelts
, (const char **) DT
->forbid
->elts
, TEST_IS_AOS
, NULL
, NULL
)) {
512 return HTTP_FORBIDDEN
;
516 if ((DT
->dircache
.clim
< 1) || (!(path
= domaintree_cache_get(DT
, r
->request_time
, host
)))) {
518 if (!(path
= domaintree_path(r
->pool
, DT
, host
))) {
522 /* apply any aliases */
523 if (apr_table_elts(DT
->aliases
.faketable
)->nelts
) {
524 domaintree_fake(r
->pool
, DT
, &path
);
528 if (DT
->dircache
.clim
> 0) {
529 domaintree_cache_set(DT
, r
->request_time
, host
, path
);
533 /* compose virtual docroot */
534 docroot
= struniqchr(apr_pstrcat(r
->pool
, DT
->prefix
, "/", path
, "/", DT
->suffix
, "/", NULL
), '/');
537 if (DT
->statroot
> 0) {
540 switch (apr_stat(&sb
, docroot
, APR_FINFO_MIN
, r
->pool
)) {
543 ap_log_error(DT_LOG_DBG
"stat path = %s (success)", docroot
);
547 ap_log_error(DT_LOG_DBG
"stat path = %s (failure)", docroot
);
552 /* set virtual docroot */
553 apr_table_set(r
->subprocess_env
, "VIRTUAL_DOCUMENT_ROOT", docroot
);
555 #ifdef HAVE_UNIX_SUEXEC
556 /* set suexec note */
558 const char *username
, *separator
;
560 if (domaintree_test(DT
, docroot
, DT
->suexec
->nelts
, DT
->suexec
->elts
, TEST_IS_BOS
, NULL
, &username
)) {
561 if ((separator
= strchr(username
, '/'))) {
562 username
= apr_pstrndup(r
->pool
, username
, separator
-username
);
564 username
= apr_pstrdup(r
->pool
, username
);
566 apr_table_setn(r
->notes
, "mod_domaintree.suexec", username
);
572 r
->canonical_filename
= "";
573 r
->filename
= apr_pstrcat(r
->pool
, docroot
, EMPTY(r
->uri
) ? NULL
: ('/' == *r
->uri
? r
->uri
+ 1 : r
->uri
), NULL
);
575 ap_log_error(DT_LOG_DBG
"path done = %s", r
->filename
);
580 #ifdef HAVE_UNIX_SUEXEC
581 static ap_unix_identity_t
*domaintree_hook_get_suexec_identity(const request_rec
*r
)
583 ap_unix_identity_t
*ugid
= NULL
;
585 const char *username
;
587 if ((username
= apr_table_get(r
->notes
, "mod_domaintree.suexec"))) {
588 if ((ugid
= apr_palloc(r
->pool
, sizeof(*ugid
)))) {
589 if (APR_SUCCESS
== apr_uid_get(&ugid
->uid
, &ugid
->gid
, username
, r
->pool
)) {
599 static void domaintree_hooks(apr_pool_t
*pool
)
601 static const char * const pre
[] = {"mod_alias.c", "mod_userdir.c", NULL
};
603 ap_hook_post_config(domaintree_hook_post_config
, NULL
, NULL
, APR_HOOK_MIDDLE
);
604 ap_hook_translate_name(domaintree_hook_translate_name
, pre
, NULL
, APR_HOOK_FIRST
);
605 #ifdef HAVE_UNIX_SUEXEC
606 ap_hook_get_suexec_identity(domaintree_hook_get_suexec_identity
, NULL
, NULL
, APR_HOOK_FIRST
);
611 /* {{{ Configuration */
613 static void *domaintree_create_srv(apr_pool_t
*p
, server_rec
*s
)
615 MDT_CNF
*DT
= (MDT_CNF
*) apr_palloc(p
, sizeof(MDT_CNF
));
623 DT
->prefix
= "/var/www";
624 DT
->suffix
= "public_html";
626 DT
->ignore
= apr_array_make(p
, 0, sizeof(char *));
627 DT
->forbid
= apr_array_make(p
, 0, sizeof(char *));
628 #ifdef HAVE_UNIX_SUEXEC
629 DT
->suexec
= apr_array_make(p
, 0, sizeof(char *));
632 DT
->aliases
.recursion
= -1;
633 DT
->aliases
.faketable
= apr_table_make(p
, 0);
635 DT
->dircache
.clim
= -1;
636 DT
->dircache
.hmap
= apr_hash_make(p
);
637 apr_pool_create(&DT
->dircache
.pool
, p
);
638 apr_global_mutex_create(&DT
->dircache
.lock
, __FILE__
, APR_LOCK_DEFAULT
, p
);
640 fprintf(stderr
, "MDT: cfg create %p\n", DT
);
645 static void *domaintree_merge_srv(apr_pool_t
*p
, void *old_cfg_ptr
, void *new_cfg_ptr
)
647 MDT_CNF
*old_cfg
= (MDT_CNF
*) old_cfg_ptr
, *new_cfg
= (MDT_CNF
*) new_cfg_ptr
;
648 MDT_CNF
*DT
= (MDT_CNF
*) apr_palloc(p
, sizeof(MDT_CNF
));
650 DT
->server
= new_cfg
->server
;
651 DT
->enabled
= IF_SET_ELSE(new_cfg
->enabled
, old_cfg
->enabled
);
652 DT
->stripwww
= IF_SET_ELSE(new_cfg
->stripwww
, old_cfg
->stripwww
);
653 DT
->statroot
= IF_SET_ELSE(new_cfg
->statroot
, old_cfg
->statroot
);
654 DT
->maxdepth
= IF_SET_ELSE(new_cfg
->maxdepth
, old_cfg
->maxdepth
);
656 DT
->prefix
= EMPTY(new_cfg
->prefix
) ? EMPTY(old_cfg
->prefix
) ? "/var/www" : old_cfg
->prefix
: new_cfg
->prefix
;
657 DT
->suffix
= EMPTY(new_cfg
->suffix
) ? EMPTY(old_cfg
->suffix
) ? "public_html" : old_cfg
->suffix
: new_cfg
->suffix
;
659 DT
->ignore
= apr_array_append(p
, new_cfg
->ignore
, old_cfg
->ignore
);
660 DT
->forbid
= apr_array_append(p
, new_cfg
->forbid
, old_cfg
->forbid
);
661 #ifdef HAVE_UNIX_SUEXEC
662 DT
->suexec
= apr_array_append(p
, new_cfg
->suexec
, old_cfg
->suexec
);
665 DT
->aliases
.recursion
= IF_SET_ELSE(new_cfg
->aliases
.recursion
, old_cfg
->aliases
.recursion
);
666 DT
->aliases
.faketable
= apr_table_overlay(p
, new_cfg
->aliases
.faketable
, old_cfg
->aliases
.faketable
);
668 DT
->dircache
.clim
= IF_SET_ELSE(new_cfg
->dircache
.clim
, old_cfg
->dircache
.clim
);
669 DT
->dircache
.hmap
= apr_hash_overlay(p
, new_cfg
->dircache
.hmap
, old_cfg
->dircache
.hmap
);
670 apr_global_mutex_create(&new_cfg
->dircache
.lock
, __FILE__
, APR_LOCK_DEFAULT
, p
);
672 fprintf(stderr
, "MDT: cfg merge %p + %p = %p\n", old_cfg
, new_cfg
, DT
);
677 static const char *domaintree_init_enable(cmd_parms
*cmd
, void *conf
, int flag
)
679 GET_MDT_CNF(cmd
->server
)->enabled
= flag
;
683 static const char *domaintree_init_stripwww(cmd_parms
*cmd
, void *conf
, int flag
)
685 GET_MDT_CNF(cmd
->server
)->stripwww
= flag
;
689 static const char *domaintree_init_statroot(cmd_parms
*cmd
, void *conf
, int flag
)
691 GET_MDT_CNF(cmd
->server
)->statroot
= flag
;
695 static const char *domaintree_init_prefix(cmd_parms
*cmd
, void *conf
, const char *prefix
)
697 GET_MDT_CNF(cmd
->server
)->prefix
= EMPTY(prefix
) ? "/" : trim(apr_pstrdup(cmd
->pool
, prefix
), strlen(prefix
), '/', TRIM_RIGHT
);
701 static const char *domaintree_init_suffix(cmd_parms
*cmd
, void *conf
, const char *suffix
)
703 GET_MDT_CNF(cmd
->server
)->suffix
= EMPTY(suffix
) ? "" : trim(apr_pstrdup(cmd
->pool
, suffix
), strlen(suffix
), '/', TRIM_BOTH
);
707 static const char *domaintree_init_maxdepth(cmd_parms
*cmd
, void *conf
, const char *max_depth
)
711 if ((depth
= atol(max_depth
))) {
713 GET_MDT_CNF(cmd
->server
)->maxdepth
= depth
;
715 return "Maximum DomainTree depth cannot be negative.";
722 static const char *domaintree_init_aliasrecursion(cmd_parms
*cmd
, void *conf
, const char *alias_recursion
)
726 if ((recursion
= atol(alias_recursion
))) {
727 if (recursion
>= 0L) {
728 GET_MDT_CNF(cmd
->server
)->aliases
.recursion
= recursion
;
730 return "DomainTree alias recursion cannot be negative.";
737 static const char *domaintree_init_alias(cmd_parms
*cmd
, void *conf
, const char *fake
, const char *real
)
739 char *f
= strtr(apr_pstrdup(cmd
->pool
, fake
), '.', '/'), *r
= strtr(apr_pstrdup(cmd
->pool
, real
), '.', '/');
741 apr_table_set(GET_MDT_CNF(cmd
->server
)->aliases
.faketable
, trim(f
, strlen(f
), '/', TRIM_BOTH
), trim(r
, strlen(r
), '/', TRIM_BOTH
));
746 static const char *domaintree_init_cache(cmd_parms
*cmd
, void *conf
, const char *cache
)
750 if ((limit
= atol(cache
))) {
752 GET_MDT_CNF(cmd
->server
)->dircache
.clim
= limit
;
754 return "DomainTree cache limit cannot be negative.";
761 static const char *domaintree_init_ignore(cmd_parms
*cmd
, void *conf
, const char *ignore
)
763 *((char **) apr_array_push(GET_MDT_CNF(cmd
->server
)->ignore
)) = trim(apr_pstrdup(cmd
->pool
, ignore
), strlen(ignore
), '.', TRIM_BOTH
);
767 static const char *domaintree_init_forbid(cmd_parms
*cmd
, void *conf
, const char *forbid
)
769 *((char **) apr_array_push(GET_MDT_CNF(cmd
->server
)->forbid
)) = trim(apr_pstrdup(cmd
->pool
, forbid
), strlen(forbid
), '.', TRIM_BOTH
);
773 static const char *domaintree_init_suexec(cmd_parms
*cmd
, void *conf
)
775 #ifdef HAVE_UNIX_SUEXEC
779 return "DomainTreeSuexec is a per directory configuration directive";
782 switch (apr_stat(&sb
, cmd
->path
, APR_FINFO_MIN
, cmd
->pool
)) {
787 return "DomainTreeSuexec must be defined for an existing path";
790 *((char **) apr_array_push(GET_MDT_CNF(cmd
->server
)->suexec
)) = trim(apr_pstrdup(cmd
->pool
, cmd
->path
), strlen(cmd
->path
), '.', TRIM_BOTH
);
794 return "HAVE_UNIX_SUEXEC was undefined at compile time";
801 static command_rec domaintree_commands
[] = {
803 "DomainTreeEnabled", domaintree_init_enable
, NULL
, RSRC_CONF
,
804 "Turn the module on or off."
808 "DomainTreeStripWWW", domaintree_init_stripwww
, NULL
, RSRC_CONF
,
809 "Strip leading www from host. (default On)"
813 "DomainTreeStatRoot", domaintree_init_statroot
, NULL
, RSRC_CONF
,
814 "Wheter to check for the evaluated virtual document root with a stat call. (default Off)"
818 "DomainTreePrefix", domaintree_init_prefix
, NULL
, RSRC_CONF
,
819 "DomainTree path prefix. (default /var/www) Do not forget the leading slash!"
823 "DomainTreeSuffix", domaintree_init_suffix
, NULL
, RSRC_CONF
,
824 "DomainTree path suffix. (default public_html)"
828 "DomainTreeMaxdepth", domaintree_init_maxdepth
, NULL
, RSRC_CONF
,
829 "DomainTree max path depth. (default 20)"
833 "DomainTreeAliasRecursion", domaintree_init_aliasrecursion
, NULL
, RSRC_CONF
,
834 "Whether (and how often at the maximum) DomainTree should walk recursively "
835 "through the aliases list as long as matching aliases are found. (default: 0 = turned off)"
839 "DomainTreeAlias", domaintree_init_alias
, NULL
, RSRC_CONF
,
840 "DomainTree aliases; e.g. DomainTreeAlias com/example/tickets com/example/support (dots or slashes equal)"
844 "DomainTreeCache", domaintree_init_cache
, NULL
, RSRC_CONF
,
845 "DomainTree server-wide host to directory cache; specify how many cache entries to allow (default: 0 = turned off)"
849 "DomainTreeIgnore", domaintree_init_ignore
, NULL
, RSRC_CONF
,
850 "DomainTree ignored hosts; uses the same matching alogrithm like DomainTreeAlias"
854 "DomainTreeForbid", domaintree_init_forbid
, NULL
, RSRC_CONF
,
855 "DomanTree forbidden hosts; uses the same matching algorithm like DomainTreeAlias"
859 "DomainTreeSuexec", domaintree_init_suexec
, NULL
, ACCESS_CONF
,
860 "DomainTree user home directory; enable suexec hook for domain based user-dir hosting in this directory"
867 /* {{{ Module Administrativa */
869 module AP_MODULE_DECLARE_DATA domaintree_module
= {
870 STANDARD20_MODULE_STUFF
,
871 NULL
, /* create per-dir */
872 NULL
, /* merge per-dir */
873 domaintree_create_srv
, /* create per-server */
874 domaintree_merge_srv
, /* merge per-server */
875 domaintree_commands
, /* config commands */
876 domaintree_hooks
/* hooks */
886 * vim600: noet sw=4 ts=4 fdm=marker
887 * vim<600: noet sw=4 ts=4