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