- add license
[m6w6/mod-domaintree] / mod_domaintree.c
1 /*
2 * mod_domaintree.c - Apache2
3 *
4 * $Id$
5 *
6 * Copyright 2005 Michael Wallner <mike@iworks.at
7 *
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
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
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.
19 *
20 *
21 * <pre>
22 * DomainTreeEnabled On
23 * DomainTreeMaxdepth 25
24 * DomainTreeStripWWW On
25 * DomainTreePrefix /sites
26 * DomainTreeSuffix /html
27 *
28 * /sites
29 * +- /at
30 * | +- /co
31 * | | +- /company
32 * | | +- /html
33 * | | +- /sub1
34 * | | | +- /html
35 * | | +- /sub2
36 * | | +- /html
37 * | +- /or
38 * | | +- /organisation
39 * | | +- /html
40 * | +- /example
41 * | +- /html
42 * +- /com
43 * +- /example
44 * +- /html
45 * </pre>
46 */
47
48 #define MODULE "mod_domaintree"
49 #define AUTHOR "mike@php.net"
50 #define VERSION "1.0"
51
52 /* {{{ Includes */
53
54 #include "httpd.h"
55 #include "http_config.h"
56
57 #define CORE_PRIVATE
58 #include "http_core.h"
59 #undef CORE_PRIVATE
60
61 #include "http_log.h"
62 #include "http_protocol.h"
63 #include "http_request.h"
64
65 #include "apr.h"
66 #include "apr_lib.h"
67 #include "apr_ring.h"
68 #include "apr_strings.h"
69
70 #define APR_WANT_STRFUNC
71 #include "apr_want.h"
72
73 /* }}} */
74 /* {{{ domaintree_module */
75
76 module AP_MODULE_DECLARE_DATA domaintree_module;
77
78 /* }}} */
79 /* {{{ Macros & Types */
80
81 #define MOD_DT_CNF domaintree_conf
82 #define MOD_DT_PTR (&domaintree_module)
83
84 #define NUL '\0'
85 #define EMPTY(str) ((str == NULL) || (*(str) == NUL))
86
87 typedef int STATUS;
88
89 typedef struct {
90 server_rec *server;
91 int enabled;
92 int stripwww;
93 size_t maxdepth;
94 char *prefix;
95 char *suffix;
96 } domaintree_conf;
97
98 struct domaintree_entry {
99 char *name;
100 APR_RING_ENTRY(domaintree_entry) link;
101 };
102 APR_RING_HEAD(domaintree, domaintree_entry);
103
104 /* }}} */
105 /* {{{ Helpers */
106
107 static APR_INLINE char *domaintree_host(apr_pool_t *pool, MOD_DT_CNF *DT, const char *name)
108 {
109 if (EMPTY(name)) {
110 ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, DT->server, "DomainTree: no host/server name");
111 return NULL;
112 } else {
113 size_t len;
114 char *port, *ptr, *host;
115
116 ptr = host = apr_pstrdup(pool, name);
117 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host name = %s", host);
118
119 /* check for :NN port */
120 if ((port = strchr(ptr, ':'))) {
121 len = port - ptr;
122 } else {
123 len = strlen(ptr);
124 }
125
126 /* strip leading and trailing dots */
127 while (ptr[len - 1] == '.') {
128 --len;
129 }
130 while (*ptr == '.') {
131 ++ptr;
132 --len;
133 }
134 host = ptr;
135
136 /* terminate & lowercase */
137 ptr[len] = NUL;
138 while (*ptr) {
139 apr_tolower(*ptr++);
140 }
141
142 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: sane host = %s", host);
143 return host;
144 }
145 }
146
147 static APR_INLINE const char *domaintree_append(apr_pool_t *pool, struct domaintree *tree, const char *name, size_t length)
148 {
149 struct domaintree_entry *elem = apr_palloc(pool, sizeof(struct domaintree_entry));
150
151 APR_RING_ELEM_INIT(elem, link);
152 APR_RING_INSERT_HEAD(tree, elem, domaintree_entry, link);
153
154 return elem->name = apr_pstrndup(pool, name, length);
155 }
156
157 static APR_INLINE struct domaintree *domaintree_tree(apr_pool_t *pool, MOD_DT_CNF *DT, char *host)
158 {
159 size_t depth = 0;
160 char *host_ptr = host;
161 struct domaintree *tree = apr_palloc(pool, sizeof(struct domaintree));
162
163 APR_RING_INIT(tree, domaintree_entry, link);
164
165 while ((host_ptr = strchr(host, '.'))) {
166
167 /* check max depth */
168 if (++depth > DT->maxdepth) {
169 ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, DT->server, "DomainTree: maxdepth exceeded = %s", host);
170 return NULL;
171 }
172
173 /* append part */
174 if (host_ptr - host) {
175
176 /* strip WWW */
177 if (DT->stripwww && (depth == 1) && (!strncmp(host, "www.", sizeof("www")))) {
178 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: stripping www.");
179 } else {
180 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host part (%d) = %s",
181 depth - 1, domaintree_append(pool, tree, host, host_ptr - host)
182 );
183 }
184 }
185
186 host = host_ptr + 1;
187 }
188
189 /* append last part */
190 if (!EMPTY(host)) {
191 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: host part (%d) = %s",
192 depth, domaintree_append(pool, tree, host, strlen(host))
193 );
194 }
195
196 return tree;
197 }
198
199 static APR_INLINE char *domaintree_path(apr_pool_t *pool, MOD_DT_CNF *DT, struct domaintree *tree)
200 {
201 struct domaintree_entry *elem;
202 char *path = apr_pstrdup(pool, DT->prefix);
203 APR_RING_FOREACH(elem, tree, domaintree_entry, link) {
204 path = apr_pstrcat(pool, path, "/", elem->name, NULL);
205 }
206 return path = apr_pstrcat(pool, path, "/", DT->suffix, NULL);
207 }
208
209 /* }}} */
210 /* {{{ Hooks */
211
212 static STATUS domaintree_hook_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
213 {
214 ap_add_version_component(pconf, MODULE "/" VERSION);
215 return OK;
216 }
217
218 static STATUS domaintree_hook_translate_name(request_rec *r)
219 {
220 MOD_DT_CNF *DT = NULL;
221 struct domaintree *tree;
222 char *path, *host;
223
224 DT = ap_get_module_config(r->server->module_config, MOD_DT_PTR);
225 if ((!DT) || (!DT->enabled)) {
226 return DECLINED;
227 }
228
229 /* get a usable host name */
230 if (!(host = domaintree_host(r->pool, DT, ap_get_server_name(r)))) {
231 return DECLINED;
232 }
233
234 /* build domain tree */
235 if (!(tree = domaintree_tree(r->pool, DT, host))) {
236 return DECLINED;
237 }
238
239 /* build path */
240 if (!(path = domaintree_path(r->pool, DT, tree))) {
241 return DECLINED;
242 }
243
244 /* done */
245 r->canonical_filename = "";
246 r->filename = apr_pstrcat(r->pool, path, r->uri, NULL);
247 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, DT->server, "DomainTree: final path = %s", r->filename);
248
249 return OK;
250 }
251
252 static void domaintree_hooks(apr_pool_t *pool)
253 {
254 ap_hook_post_config(domaintree_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE);
255 ap_hook_translate_name(domaintree_hook_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
256 }
257
258 /* }}} */
259 /* {{{ Configuration */
260
261 static void *domaintree_create_srv(apr_pool_t *p, server_rec *s)
262 {
263 MOD_DT_CNF *DT;
264
265 DT = (MOD_DT_CNF *) apr_palloc(p, sizeof(MOD_DT_CNF));
266
267 DT->server = s;
268 DT->enabled = 1;
269 DT->stripwww = 1;
270 DT->maxdepth = 20;
271
272 DT->prefix = "/var/www";
273 DT->suffix = "public_html";
274
275 return DT;
276 }
277
278 static const char *domaintree_enable(cmd_parms *cmd, void *conf, int flag)
279 {
280 MOD_DT_CNF *DT;
281
282 DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR);
283 DT->enabled = flag;
284
285 return NULL;
286 }
287
288 static const char *domaintree_stripwww(cmd_parms *cmd, void *conf, int flag)
289 {
290 MOD_DT_CNF *DT;
291
292 DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR);
293 DT->stripwww = flag;
294
295 return NULL;
296 }
297
298 static const char *domaintree_prefix(cmd_parms *cmd, void *conf, const char *prefix)
299 {
300 MOD_DT_CNF *DT;
301
302 DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR);
303 DT->prefix = EMPTY(prefix) ? "/" : apr_pstrdup(cmd->pool, prefix);
304
305 return NULL;
306 }
307
308 static const char *domaintree_suffix(cmd_parms *cmd, void *conf, const char *suffix)
309 {
310 MOD_DT_CNF *DT;
311
312 DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR);
313 DT->suffix = EMPTY(suffix) ? "/" : apr_pstrdup(cmd->pool, suffix);
314
315 return NULL;
316 }
317
318 static const char *domaintree_maxdepth(cmd_parms *cmd, void *conf, const char *max_depth)
319 {
320 int depth;
321
322 if ((depth = atoi(max_depth))) {
323 if (depth > 0) {
324 MOD_DT_CNF *DT;
325 DT = ap_get_module_config(cmd->server->module_config, MOD_DT_PTR);
326 DT->maxdepth = (size_t) depth;
327 } else {
328 return "Maximum DomainTree depth cannot be negative.";
329 }
330 }
331
332 return NULL;
333 }
334
335 /* }}} */
336 /* {{{ Commands */
337
338 static command_rec domaintree_commands[] = {
339 AP_INIT_FLAG(
340 "DomainTreeEnabled", domaintree_enable, NULL, RSRC_CONF,
341 "Turn the module on or off."
342 ),
343
344 AP_INIT_FLAG(
345 "DomainTreeStripWWW", domaintree_stripwww, NULL, RSRC_CONF,
346 "Strip leading www from host. (default On)"
347 ),
348
349 AP_INIT_TAKE1(
350 "DomainTreePrefix", domaintree_prefix, NULL, RSRC_CONF,
351 "DomainTree path prefix. (default /var/www)"
352 ),
353
354 AP_INIT_TAKE1(
355 "DomainTreeSuffix", domaintree_suffix, NULL, RSRC_CONF,
356 "DomainTree path suffix. (default public_html)"
357 ),
358
359 AP_INIT_TAKE1(
360 "DomainTreeMaxdepth", domaintree_maxdepth, NULL, RSRC_CONF,
361 "DomainTree max path depth. (default 20)"
362 ),
363
364 { NULL }
365 };
366
367 /* }}} */
368 /* {{{ Module Administrativa */
369
370 module AP_MODULE_DECLARE_DATA domaintree_module = {
371 STANDARD20_MODULE_STUFF,
372 NULL, /* create per-dir */
373 NULL, /* merge per-dir */
374 domaintree_create_srv, /* create per-server */
375 NULL, /* merge per-server */
376 domaintree_commands, /* apr_table_t commands */
377 domaintree_hooks /* register hooks */
378 };
379
380 /* }}} */
381
382 /*
383 * Local variables:
384 * tab-width: 4
385 * c-basic-offset: 4
386 * End:
387 * vim600: noet sw=4 ts=4 fdm=marker
388 * vim<600: noet sw=4 ts=4
389 */