mod_rewrite.c revision 99c475e760470ab8ca3d17da82a2f6158466f918
2d2eda71267231c2526be701fe655db125852c1ffielding/* ====================================================================
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * The Apache Software License, Version 1.1
b99dbaab171d91e1b664397cc40e039d0c087c65fielding * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * reserved.
2d2eda71267231c2526be701fe655db125852c1ffielding * Redistribution and use in source and binary forms, with or without
2d2eda71267231c2526be701fe655db125852c1ffielding * modification, are permitted provided that the following conditions
2d2eda71267231c2526be701fe655db125852c1ffielding * 1. Redistributions of source code must retain the above copyright
2d2eda71267231c2526be701fe655db125852c1ffielding * notice, this list of conditions and the following disclaimer.
2d2eda71267231c2526be701fe655db125852c1ffielding * 2. Redistributions in binary form must reproduce the above copyright
2d2eda71267231c2526be701fe655db125852c1ffielding * notice, this list of conditions and the following disclaimer in
2d2eda71267231c2526be701fe655db125852c1ffielding * the documentation and/or other materials provided with the
2d2eda71267231c2526be701fe655db125852c1ffielding * distribution.
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * 3. The end-user documentation included with the redistribution,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * if any, must include the following acknowledgment:
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * "This product includes software developed by the
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * Apache Software Foundation (http://www.apache.org/)."
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * Alternately, this acknowledgment may appear in the software itself,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * if and wherever such third-party acknowledgments normally appear.
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * 4. The names "Apache" and "Apache Software Foundation" must
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * not be used to endorse or promote products derived from this
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * software without prior written permission. For written
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * permission, please contact apache@apache.org.
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * 5. Products derived from this software may not be called "Apache",
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * nor may "Apache" appear in their name, without prior written
64185f9824e42f21ca7b9ae6c004484215c031a7rbb * permission of the Apache Software Foundation.
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
2d2eda71267231c2526be701fe655db125852c1ffielding * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * SUCH DAMAGE.
2d2eda71267231c2526be701fe655db125852c1ffielding * ====================================================================
2d2eda71267231c2526be701fe655db125852c1ffielding * This software consists of voluntary contributions made by many
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * individuals on behalf of the Apache Software Foundation. For more
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * information on the Apache Software Foundation, please see
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * Portions of this software are based upon public domain software
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * originally written at the National Center for Supercomputing Applications,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * University of Illinois, Urbana-Champaign.
2d2eda71267231c2526be701fe655db125852c1ffielding** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
2d2eda71267231c2526be701fe655db125852c1ffielding** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
b980ad7fdc218b4855cde9f75a747527f50c554dwrowe** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
2d2eda71267231c2526be701fe655db125852c1ffielding** URL Rewriting Module
2d2eda71267231c2526be701fe655db125852c1ffielding** This module uses a rule-based rewriting engine (based on a
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** regular-expression parser) to rewrite requested URLs on the fly.
3ef7956b993aff4b4882c643910fb688a0e707e3ben** It supports an unlimited number of additional rule conditions (which can
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** operate on a lot of variables, even on HTTP headers) for granular
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** matching and even external database lookups (either via plain text
2d2eda71267231c2526be701fe655db125852c1ffielding** tables, DBM hash files or even external processes) for advanced URL
2d2eda71267231c2526be701fe655db125852c1ffielding** substitution.
2d2eda71267231c2526be701fe655db125852c1ffielding** It operates on the full URLs (including the PATH_INFO part) both in
2d2eda71267231c2526be701fe655db125852c1ffielding** per-server context (httpd.conf) and per-dir context (.htaccess) and even
2d2eda71267231c2526be701fe655db125852c1ffielding** can generate QUERY_STRING parts on result. The rewriting result finally
2d2eda71267231c2526be701fe655db125852c1ffielding** can lead to internal subprocessing, external request redirection or even
2d2eda71267231c2526be701fe655db125852c1ffielding** to internal proxy throughput.
2d2eda71267231c2526be701fe655db125852c1ffielding** This module was originally written in April 1996 and
2d2eda71267231c2526be701fe655db125852c1ffielding** gifted exclusively to the The Apache Software Foundation in July 1997 by
2d2eda71267231c2526be701fe655db125852c1ffielding** Ralf S. Engelschall
2d2eda71267231c2526be701fe655db125852c1ffielding** rse@engelschall.com
59d7dc153347a9f606c5712f0fae7b65e96682d9rbb#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb#define MOD_REWRITE_SET_MUTEX_PERMS /* XXX Apache should define something */
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** +-------------------------------------------------------+
59d7dc153347a9f606c5712f0fae7b65e96682d9rbb** | static module configuration
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** +-------------------------------------------------------+
59d7dc153347a9f606c5712f0fae7b65e96682d9rbb** Our interface to the Apache server kernel:
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** o Runtime logic of a request is as following:
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** while(request or subrequest)
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** foreach(stage #0...#9)
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** foreach(module) (**)
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** try to run hook
dd95dce618f5c95c71733a363f7e3c8acb02f8d4rbb** o the order of modules at (**) is the inverted order as
f0c7e2f3f183d7c289eb176434d68a922b66a008wrowe** given in the "Configuration" file, i.e. the last module
b980ad7fdc218b4855cde9f75a747527f50c554dwrowe** specified is the first one called for each hook!
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** The core module is always the last!
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** o there are two different types of result checking and
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** continue processing:
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** for hook #0,#1,#4,#5,#6,#8:
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** hook run loop stops on first modules which gives
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** back a result != DECLINED, i.e. it usually returns OK
3d96ee83babeec32482c9082c9426340cee8c44dwrowe** which says "OK, module has handled this _stage_" and for #1
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** this have not to mean "Ok, the filename is now valid".
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** for hook #2,#3,#7,#9:
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** all hooks are run, independend of result
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** o at the last stage, the core module always
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** - says "HTTP_BAD_REQUEST" if r->filename does not begin with "/"
3d96ee83babeec32482c9082c9426340cee8c44dwrowe** - prefix URL with document_root or replaced server_root
2d2eda71267231c2526be701fe655db125852c1ffielding** with document_root and sets r->filename
2d2eda71267231c2526be701fe655db125852c1ffielding** - always return a "OK" independed if the file really exists
2d2eda71267231c2526be701fe655db125852c1ffielding /* the module (predeclaration) */
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb /* rewritemap int: handler function registry */
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb /* the cache */
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb /* whether proxy module is available or not */
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbbstatic const char *lockname;
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbbstatic apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** +-------------------------------------------------------+
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** | configuration directive handling
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** +-------------------------------------------------------+
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** per-server configuration structure handling
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbbstatic void *config_server_create(apr_pool_t *p, server_rec *s)
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a = (rewrite_server_conf *)apr_pcalloc(p, sizeof(rewrite_server_conf));
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a->rewritemaps = apr_array_make(p, 2, sizeof(rewritemap_entry));
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry));
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry));
5d54ba1fdf6f8d7167fafcd93bef30df3906b1aecoar return (void *)a;
5d54ba1fdf6f8d7167fafcd93bef30df3906b1aecoarstatic void *config_server_merge(apr_pool_t *p, void *basev, void *overridesv)
2d2eda71267231c2526be701fe655db125852c1ffielding * local directives override
2d2eda71267231c2526be701fe655db125852c1ffielding * and anything else is inherited
3ef7956b993aff4b4882c643910fb688a0e707e3ben a->rewritemaps = apr_array_append(p, overrides->rewritemaps,
3ef7956b993aff4b4882c643910fb688a0e707e3ben a->rewriteconds = apr_array_append(p, overrides->rewriteconds,
3ef7956b993aff4b4882c643910fb688a0e707e3ben a->rewriterules = apr_array_append(p, overrides->rewriterules,
b45c1c292ff1fa635004ae81fa691f8cb3cdda85rbb * local directives override
b45c1c292ff1fa635004ae81fa691f8cb3cdda85rbb * and anything else gets defaults
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb return (void *)a;
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb** per-directory configuration structure handling
42ce672c516baf6e4eaed18ccc1647de2d456d8edougmstatic void *config_perdir_create(apr_pool_t *p, char *path)
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a = (rewrite_perdir_conf *)apr_pcalloc(p, sizeof(rewrite_perdir_conf));
42ce672c516baf6e4eaed18ccc1647de2d456d8edougm a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry));
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbb a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry));
3ef7956b993aff4b4882c643910fb688a0e707e3ben /* make sure it has a trailing slash */
3ef7956b993aff4b4882c643910fb688a0e707e3ben return (void *)a;
4743e6e0c6d5bc44f086d4f0032ae41a68192dd1rbbstatic void *config_perdir_merge(apr_pool_t *p, void *basev, void *overridesv)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb a->rewriteconds = apr_array_append(p, overrides->rewriteconds,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb a->rewriterules = apr_array_append(p, overrides->rewriterules,
return NULL;
while (*option) {
if (limit <= 0) {
return NULL;
return NULL;
const char *a1)
return NULL;
const char *a2)
const char *ignored_fname;
int bad = 0;
if (colon) {
++bad;
++bad;
if (bad) {
return NULL;
const char *error;
return error;
if (!lockname) {
return NULL;
const char *a1)
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
char *cp;
const char *err;
int rc;
return err;
cp++;
== NULL);
if (rc) {
return NULL;
char *str)
char *cp;
char *cp1;
char *cp2;
char *cp3;
char *key;
char *val;
const char *err;
return err;
return NULL;
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
char *cp;
const char *err;
int mode;
return err;
cp++;
sizeof(rewritecond_entry));
sizeof(rewritecond_entry));
return NULL;
char *str)
char *cp;
char *cp1;
char *cp2;
char *cp3;
char *key;
char *val;
const char *err;
return err;
return NULL;
int status = 0;
if (i < MAX_ENV_FLAGS) {
if (i < MAX_COOKIE_FLAGS) {
return NULL;
if (map_pfn_register) {
return OK;
server_rec *s)
void *data;
int first_time = 0;
if (!data) {
return HTTP_INTERNAL_SERVER_ERROR;
#ifdef MOD_REWRITE_SET_MUTEX_PERMS
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
for (; s; s = s->next) {
open_rewritelog(s, p);
if (!first_time) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
lockname, p);
const char *saved_rulestatus;
const char *var;
const char *thisserver;
char *thisport;
const char *thisurl;
const char *ccp;
unsigned int port;
int rulestatus;
return DECLINED;
return DECLINED;
r->filename);
if (rulestatus) {
unsigned skip;
if (!proxy_available) {
return HTTP_FORBIDDEN;
r->filename);
return OK;
if (r->args) {
? r->args
NULL);
n = r->status;
return HTTP_FORBIDDEN;
return HTTP_GONE;
return DECLINED;
#if APR_HAS_USER
return HTTP_BAD_REQUEST;
(r->filename +
r->filename);
return OK;
return DECLINED;
if (t == NULL) {
return DECLINED;
r->filename, t);
ap_set_content_type(r, t);
return OK;
char *cp;
char *cp2;
const char *ccp;
char *prefix;
int rulestatus;
char *ofilename;
return DECLINED;
return DECLINED;
return DECLINED;
return DECLINED;
return HTTP_FORBIDDEN;
if (rulestatus) {
unsigned skip;
return OK;
* hostname and compare/substitute only the stuff after it.
if (r->args) {
? r->args
NULL);
n = r->status;
return HTTP_FORBIDDEN;
return HTTP_GONE;
return HTTP_BAD_REQUEST;
return OK;
r->filename+l);
return OK;
return DECLINED;
return DECLINED;
return DECLINED;
if (is_redirect_limit_exceeded(r)) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
if (!reqc) {
char *perdir)
int changed;
int rc;
changed = 0;
loop:
p = &entries[i];
if (rc) {
goto loop;
if (p->skip > 0) {
s = p->skip;
p = &entries[i];
p = &entries[i];
return changed;
char *perdir)
char *uri;
char *output;
const char *vary;
int prefixstrip;
int failed;
int rc;
prefixstrip = 0;
sizeof(regmatch));
failed = 0;
c = &conds[i];
if (rc == 0) {
c = &conds[i];
if (rc == 0) {
if (failed) {
p->forced_mimetype);
reduce_uri(r);
r->filename);
p->forced_mimetype);
int rc;
rc = 0;
#if !defined(OS2)
sizeof(regmatch));
return rc;
inp++;
goto skip;
char *endp;
goto skip;
goto skip;
if (result) {
char *var;
span = 0;
skip:
space--;
char *olduri;
if (q != NULL) {
if (qsappend) {
r->filename);
char *cp;
unsigned short port;
char *portp;
char *hostp;
char *url;
char *olduri;
apr_size_t l;
c = *cp;
*cp = c;
const char *thisserver;
char *thisport;
int port;
switch (*uri++) {
char *cp;
return NULL;
++cp;
++cp;
NULL);
#if APR_HAS_USER
char *newuri;
char *homedir;
return newuri;
rewritemap_entry *s;
char *value;
s = &entries[i];
return NULL;
if ((value =
return value;
return NULL;
return NULL;
if ((value =
return value;
return NULL;
if ((value =
return value;
return value;
return NULL;
if ((value =
return NULL;
value);
return value;
return NULL;
char *cpT;
char *curkey;
char *curval;
return NULL;
if (skip == 0) {
++cpT;
if (skip == 0) {
return value;
return value;
#ifndef NO_WRITEV
return NULL;
if (rewrite_mapr_lock_acquire) {
#ifdef NO_WRITEV
buf[i++] = c;
if (rewrite_mapr_lock_acquire) {
return NULL;
cp++) {
return value;
cp++) {
return value;
char *value;
return value;
char *value;
return value;
static int rewrite_rand_init_done = 0;
static void rewrite_rand_init(void)
if (!rewrite_rand_init_done) {
static int rewrite_rand(int l, int h)
char *buf;
return value;
return buf;
const char *fname;
if (!fname) {
!= APR_SUCCESS) {
char *str1;
const char *type;
char *ruser;
const char *rhost;
(unsigned long)(r->server), (unsigned long)r,
return APR_SUCCESS;
APR_LOCK_DEFAULT, p);
return rc;
#ifdef MOD_REWRITE_SET_MUTEX_PERMS
return rc;
return APR_SUCCESS;
return APR_SUCCESS;
return APR_SUCCESS;
return rc;
return APR_SUCCESS;
const char *desc)
!= APR_SUCCESS) ||
procattr, p);
if (fpin) {
if (fpout) {
return (rc);
const char *result;
/* XXX: wow this has gotta be slow if you actually use it for a lot, recalculates exploded time for each variable */
return (char *)result; \
return NULL;
cache *c;
return NULL;
#if APR_HAS_THREADS
return NULL;
return NULL;
return NULL;
return n % CACHE_TLB_ROWS;
char *key)
for (i=0; i < CACHE_TLB_COLS; ++i) {
return NULL;
return &elt[j];
return NULL;
cacheentry *e)
cachelist *l;
cacheentry *e;
cachetlbentry *t;
int found_list;
#if APR_HAS_THREADS
found_list = 0;
if (e != NULL) {
#if APR_HAS_THREADS
#if APR_HAS_THREADS
if (!found_list) {
sizeof(cachetlbentry));
for (i=0; i<CACHE_TLB_ROWS; ++i) {
for (j=0; j<CACHE_TLB_COLS; ++j)
#if APR_HAS_THREADS
#if APR_HAS_THREADS
cachelist *l;
cacheentry *e;
#if APR_HAS_THREADS
if (e != NULL) {
#if APR_HAS_THREADS
#if APR_HAS_THREADS
#if APR_HAS_THREADS
return NULL;
const char *subst)
char *output;
return output;
char *cp;
int isquoted;
cp++; \
isquoted = 0; \
cp++; \
cp++; \
char *cp;
char *var;
char *val;
char *domain;
char *expires;
char *path;
char *tok_cntx;
char *cookie;
if (expires) {
char *notename;
void *data;
(expires)?
r->request_time +
: NULL,
NULL);
const char *root;
const char *slash;
char *statpath;
for (i = 0; i < n1; i++) {
int depth;
else if (*s == left) {
++depth;
return NULL;
int depth;
return NULL;
else if (*s == left) {
++depth;
return NULL;
{ NULL }