]> granicus.if.org Git - apache/blob - modules/mappers/mod_alias.c
Use the new command-handler initializer macros in some more modules.
[apache] / modules / mappers / mod_alias.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * http_alias.c: Stuff for dealing with directory aliases
61  * 
62  * Original by Rob McCool, rewritten in succession by David Robinson
63  * and rst.
64  * 
65  */
66
67 #include "ap_config.h"
68 #include "httpd.h"
69 #include "http_config.h"
70 #include "http_request.h"
71
72 typedef struct {
73     char *real;
74     char *fake;
75     char *handler;
76     regex_t *regexp;
77     int redir_status;           /* 301, 302, 303, 410, etc */
78 } alias_entry;
79
80 typedef struct {
81     ap_array_header_t *aliases;
82     ap_array_header_t *redirects;
83 } alias_server_conf;
84
85 typedef struct {
86     ap_array_header_t *redirects;
87 } alias_dir_conf;
88
89 module MODULE_VAR_EXPORT alias_module;
90
91 static void *create_alias_config(ap_pool_t *p, server_rec *s)
92 {
93     alias_server_conf *a =
94     (alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf));
95
96     a->aliases = ap_make_array(p, 20, sizeof(alias_entry));
97     a->redirects = ap_make_array(p, 20, sizeof(alias_entry));
98     return a;
99 }
100
101 static void *create_alias_dir_config(ap_pool_t *p, char *d)
102 {
103     alias_dir_conf *a =
104     (alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf));
105     a->redirects = ap_make_array(p, 2, sizeof(alias_entry));
106     return a;
107 }
108
109 static void *merge_alias_config(ap_pool_t *p, void *basev, void *overridesv)
110 {
111     alias_server_conf *a =
112     (alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf));
113     alias_server_conf *base = (alias_server_conf *) basev, *overrides = (alias_server_conf *) overridesv;
114
115     a->aliases = ap_append_arrays(p, overrides->aliases, base->aliases);
116     a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects);
117     return a;
118 }
119
120 static void *merge_alias_dir_config(ap_pool_t *p, void *basev, void *overridesv)
121 {
122     alias_dir_conf *a =
123     (alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf));
124     alias_dir_conf *base = (alias_dir_conf *) basev, *overrides = (alias_dir_conf *) overridesv;
125     a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects);
126     return a;
127 }
128
129 static const char *add_alias_internal(cmd_parms *cmd, void *dummy, char *f, char *r,
130                                       int use_regex)
131 {
132     server_rec *s = cmd->server;
133     alias_server_conf *conf =
134     (alias_server_conf *) ap_get_module_config(s->module_config, &alias_module);
135     alias_entry *new = ap_push_array(conf->aliases);
136
137     /* XX r can NOT be relative to DocumentRoot here... compat bug. */
138
139     if (use_regex) {
140         new->regexp = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
141         if (new->regexp == NULL)
142             return "Regular expression could not be compiled.";
143     }
144
145     new->fake = f;
146     new->real = r;
147     new->handler = cmd->info;
148
149     return NULL;
150 }
151
152 static const char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r)
153 {
154     return add_alias_internal(cmd, dummy, f, r, 0);
155 }
156
157 static const char *add_alias_regex(cmd_parms *cmd, void *dummy, char *f, char *r)
158 {
159     return add_alias_internal(cmd, dummy, f, r, 1);
160 }
161
162 static const char *add_redirect_internal(cmd_parms *cmd, alias_dir_conf * dirconf,
163                                          const char *arg1, const char *arg2, 
164                                          const char *arg3, int use_regex)
165 {
166     alias_entry *new;
167     server_rec *s = cmd->server;
168     alias_server_conf *serverconf =
169     (alias_server_conf *) ap_get_module_config(s->module_config, &alias_module);
170     int status = (int) (long) cmd->info;
171     regex_t *r = NULL;
172     char *f = arg2;
173     char *url = arg3;
174
175     if (!strcasecmp(arg1, "gone"))
176         status = HTTP_GONE;
177     else if (!strcasecmp(arg1, "permanent"))
178         status = HTTP_MOVED_PERMANENTLY;
179     else if (!strcasecmp(arg1, "temp"))
180         status = HTTP_MOVED_TEMPORARILY;
181     else if (!strcasecmp(arg1, "seeother"))
182         status = HTTP_SEE_OTHER;
183     else if (ap_isdigit(*arg1))
184         status = atoi(arg1);
185     else {
186         f = arg1;
187         url = arg2;
188     }
189
190     if (use_regex) {
191         r = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
192         if (r == NULL)
193             return "Regular expression could not be compiled.";
194     }
195
196     if (ap_is_HTTP_REDIRECT(status)) {
197         if (!url)
198             return "URL to redirect to is missing";
199         if (!use_regex && !ap_is_url(url))
200             return "Redirect to non-URL";
201     }
202     else {
203         if (url)
204             return "Redirect URL not valid for this status";
205     }
206
207     if (cmd->path)
208         new = ap_push_array(dirconf->redirects);
209     else
210         new = ap_push_array(serverconf->redirects);
211
212     new->fake = f;
213     new->real = url;
214     new->regexp = r;
215     new->redir_status = status;
216     return NULL;
217 }
218
219 static const char *add_redirect(cmd_parms *cmd, alias_dir_conf * dirconf, 
220                                 const char *arg1, const char *arg2, const char *arg3)
221 {
222     return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 0);
223 }
224
225 static const char *add_redirect_regex(cmd_parms *cmd, alias_dir_conf * dirconf,
226                                       char *arg1, char *arg2, char *arg3)
227 {
228     return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 1);
229 }
230
231 static const command_rec alias_cmds[] =
232 {
233     AP_INIT_TAKE2("Alias", add_alias, NULL, RSRC_CONF,
234                   "a fakename and a realname"),
235     AP_INIT_TAKE2("ScriptAlias", add_alias, "cgi-script", RSRC_CONF,
236                   "a fakename and a realname"),
237     AP_INIT_TAKE23("Redirect", add_redirect, (void *) HTTP_MOVED_TEMPORARILY,
238                    OR_FILEINFO,
239                    "an optional status, then document to be redirected and "
240                    "destination URL"),
241     AP_INIT_TAKE2("AliasMatch", add_alias_regex, NULL, RSRC_CONF,
242                   "a regular expression and a filename"),
243     AP_INIT_TAKE2("ScriptAliasMatch", add_alias_regex, "cgi-script", RSRC_CONF,
244                   "a regular expression and a filename"),
245     AP_INIT_TAKE23("RedirectMatch", add_redirect_regex, 
246                    (void *) HTTP_MOVED_TEMPORARILY, OR_FILEINFO,
247                    "an optional status, then a regular expression and "
248                    "destination URL"),
249     AP_INIT_TAKE2("RedirectTemp", add_redirect, (void *) HTTP_MOVED_TEMPORARILY,
250                   OR_FILEINFO,
251                   "a document to be redirected, then the destination URL"),
252     AP_INIT_TAKE2("RedirectPermanent", add_redirect, 
253                   (void *) HTTP_MOVED_PERMANENTLY, OR_FILEINFO,
254                   "a document to be redirected, then the destination URL"),
255     {NULL}
256 };
257
258 static int alias_matches(const char *uri, const char *alias_fakename)
259 {
260     const char *end_fakename = alias_fakename + strlen(alias_fakename);
261     const char *aliasp = alias_fakename, *urip = uri;
262
263     while (aliasp < end_fakename) {
264         if (*aliasp == '/') {
265             /* any number of '/' in the alias matches any number in
266              * the supplied URI, but there must be at least one...
267              */
268             if (*urip != '/')
269                 return 0;
270
271             while (*aliasp == '/')
272                 ++aliasp;
273             while (*urip == '/')
274                 ++urip;
275         }
276         else {
277             /* Other characters are compared literally */
278             if (*urip++ != *aliasp++)
279                 return 0;
280         }
281     }
282
283     /* Check last alias path component matched all the way */
284
285     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
286         return 0;
287
288     /* Return number of characters from URI which matched (may be
289      * greater than length of alias, since we may have matched
290      * doubled slashes)
291      */
292
293     return urip - uri;
294 }
295
296 static char *try_alias_list(request_rec *r, ap_array_header_t *aliases, int doesc, int *status)
297 {
298     alias_entry *entries = (alias_entry *) aliases->elts;
299     regmatch_t regm[10];
300     char *found = NULL;
301     int i;
302
303     for (i = 0; i < aliases->nelts; ++i) {
304         alias_entry *p = &entries[i];
305         int l;
306
307         if (p->regexp) {
308             if (!ap_regexec(p->regexp, r->uri, p->regexp->re_nsub + 1, regm, 0)) {
309                 if (p->real) {
310                     found = ap_pregsub(r->pool, p->real, r->uri,
311                                     p->regexp->re_nsub + 1, regm);
312                     if (found && doesc) {
313                         found = ap_escape_uri(r->pool, found);
314                     }
315                 }
316                 else {
317                     /* need something non-null */
318                     found = ap_pstrdup(r->pool, "");
319                 }
320             }
321         }
322         else {
323             l = alias_matches(r->uri, p->fake);
324
325             if (l > 0) {
326                 if (doesc) {
327                     char *escurl;
328                     escurl = ap_os_escape_path(r->pool, r->uri + l, 1);
329
330                     found = ap_pstrcat(r->pool, p->real, escurl, NULL);
331                 }
332                 else
333                     found = ap_pstrcat(r->pool, p->real, r->uri + l, NULL);
334             }
335         }
336
337         if (found) {
338             if (p->handler) {   /* Set handler, and leave a note for mod_cgi */
339                 r->handler = p->handler;
340                 ap_table_setn(r->notes, "alias-forced-type", r->handler);
341             }
342
343             *status = p->redir_status;
344
345             return found;
346         }
347
348     }
349
350     return NULL;
351 }
352
353 static int translate_alias_redir(request_rec *r)
354 {
355     void *sconf = r->server->module_config;
356     alias_server_conf *serverconf =
357     (alias_server_conf *) ap_get_module_config(sconf, &alias_module);
358     char *ret;
359     int status;
360
361     if (r->uri[0] != '/' && r->uri[0] != '\0')
362         return DECLINED;
363
364     if ((ret = try_alias_list(r, serverconf->redirects, 1, &status)) != NULL) {
365         if (ap_is_HTTP_REDIRECT(status)) {
366             /* include QUERY_STRING if any */
367             if (r->args) {
368                 ret = ap_pstrcat(r->pool, ret, "?", r->args, NULL);
369             }
370             ap_table_setn(r->headers_out, "Location", ret);
371         }
372         return status;
373     }
374
375     if ((ret = try_alias_list(r, serverconf->aliases, 0, &status)) != NULL) {
376         r->filename = ret;
377         return OK;
378     }
379
380     return DECLINED;
381 }
382
383 static int fixup_redir(request_rec *r)
384 {
385     void *dconf = r->per_dir_config;
386     alias_dir_conf *dirconf =
387     (alias_dir_conf *) ap_get_module_config(dconf, &alias_module);
388     char *ret;
389     int status;
390
391     /* It may have changed since last time, so try again */
392
393     if ((ret = try_alias_list(r, dirconf->redirects, 1, &status)) != NULL) {
394         if (ap_is_HTTP_REDIRECT(status))
395             ap_table_setn(r->headers_out, "Location", ret);
396         return status;
397     }
398
399     return DECLINED;
400 }
401
402 static void register_hooks(void)
403 {
404     static const char * const aszPre[]={ "mod_userdir.c",NULL };
405
406     ap_hook_translate_name(translate_alias_redir,aszPre,NULL,AP_HOOK_MIDDLE);
407     ap_hook_fixups(fixup_redir,NULL,NULL,AP_HOOK_MIDDLE);
408 }
409
410 module MODULE_VAR_EXPORT alias_module =
411 {
412     STANDARD20_MODULE_STUFF,
413     create_alias_dir_config,    /* dir config creater */
414     merge_alias_dir_config,     /* dir merger --- default is to override */
415     create_alias_config,        /* server config */
416     merge_alias_config,         /* merge server configs */
417     alias_cmds,                 /* command ap_table_t */
418     NULL,                       /* handlers */
419     register_hooks              /* register hooks */
420 };