mod_rewrite.c revision 4fca72b7b53b5419325e06837c98266148111b52
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye/* Licensed to the Apache Software Foundation (ASF) under one or more
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * contributor license agreements. See the NOTICE file distributed with
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * this work for additional information regarding copyright ownership.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * The ASF licenses this file to You under the Apache License, Version 2.0
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * (the "License"); you may not use this file except in compliance with
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * the License. You may obtain a copy of the License at
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * Unless required by applicable law or agreed to in writing, software
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * distributed under the License is distributed on an "AS IS" BASIS,
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * See the License for the specific language governing permissions and
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * limitations under the License.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * URL Rewriting Module
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * This module uses a rule-based rewriting engine (based on a
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * regular-expression parser) to rewrite requested URLs on the fly.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * It supports an unlimited number of additional rule conditions (which can
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * operate on a lot of variables, even on HTTP headers) for granular
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * matching and even external database lookups (either via plain text
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * tables, DBM hash files or even external processes) for advanced URL
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * substitution.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * It operates on the full URLs (including the PATH_INFO part) both in
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * per-server context (httpd.conf) and per-dir context (.htaccess) and even
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * can generate QUERY_STRING parts on result. The rewriting result finally
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * can lead to internal subprocessing, external request redirection or even
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * to internal proxy throughput.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * This module was originally written in April 1996 and
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * gifted exclusively to the The Apache Software Foundation in July 1997 by
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * Ralf S. Engelschall
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye/* XXX: Do we really need these headers? */
64b763950bf11e9357facbd2b5666631a895c085Trond Norbyestatic ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL;
225d5411e0f1f0e690e3553aad7a97c648d566a1HemangLavanastatic void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
225d5411e0f1f0e690e3553aad7a97c648d566a1HemangLavanastatic const char* really_last_key = "rewrite_really_last";
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * in order to improve performance on running production systems, you
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * may strip all rewritelog code entirely from mod_rewrite by using the
75640e2b0da81c240758d747e76d30acd1ed194dKnut Anders Hatlen * -DREWRITELOG_DISABLED compiler option.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye * responsible for answering all the mod_rewrite questions out there.
64b763950bf11e9357facbd2b5666631a895c085Trond Norbye/* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#else /* !REWRITELOG_DISABLED */
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#endif /* REWRITELOG_DISABLED */
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye/* remembered mime-type for [T=...] */
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler"
7b046969a1b2565787df8ae3a8126359e8cd6fafTrond Norbye#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
2dbc1835e0ae88ad102e2b9a85e5c7b5298b14b6Knut Anders Hatlen/* return code of the rewrite rule
2dbc1835e0ae88ad102e2b9a85e5c7b5298b14b6Knut Anders Hatlen * the result may be escaped - or not
9661674ed58ba62a40e43d1a4b38d5e77c3c6545Knut Anders Hatlen/* max cookie size in rfc 2109 */
9661674ed58ba62a40e43d1a4b38d5e77c3c6545Knut Anders Hatlen/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
9cf297d9a579835e9336d587eaee187ca0954767Knut Anders Hatlen/* max line length (incl.\n) in text rewrite maps */
c0550b01024b910b8c1468811c0ea663b10b1372Trond Norbye/* buffer length for prg rewrite maps */
523201f786f6b12b7cf54091c6e5be167878cbeeTrond Norbye/* for better readbility */
523201f786f6b12b7cf54091c6e5be167878cbeeTrond Norbye * expansion result items on the stack to save some cycles
523201f786f6b12b7cf54091c6e5be167878cbeeTrond Norbye * (5 == about 2 variables like "foo%{var}bar%{var}baz")
0a0811923cbbd2976425db6f4c78eed811c2825bKnut Anders Hatlen * check that a subrequest won't cause infinite recursion
0a0811923cbbd2976425db6f4c78eed811c2825bKnut Anders Hatlen * either not in a subrequest, or in a subrequest
0a0811923cbbd2976425db6f4c78eed811c2825bKnut Anders Hatlen * and URIs aren't NULL and sub/main URIs differ
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen * +-------------------------------------------------------+
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen * | Types and Structures
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen * +-------------------------------------------------------+
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlentypedef struct {
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *datafile; /* filename for map data files */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *dbmtype; /* dbm type for dbm map data files */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *checkfile; /* filename to check for map existence */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *cachename; /* for cached maps (txt/rnd/dbm) */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen apr_file_t *fpin; /* in file pointer for program maps */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen apr_file_t *fpout; /* out file pointer for program maps */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen apr_file_t *fperr; /* err file pointer for program maps */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen char *(*func)(request_rec *, /* function pointer for internal maps */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen char **argv; /* argv of the external rewrite map */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *dbdq; /* SQL SELECT statement for rewritemap */
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen const char *checkfile2; /* filename to check for map existence
a07b2874263e3c5f0cd2e83441719415d53059c2Knut Anders Hatlen NULL if only one file */
edcb01bf549171673fd0bb4239f2edfc7a810397Knut Anders Hatlen/* special pattern types for RewriteCond */
} pattern_type;
typedef struct data_item {
char *data;
} data_item;
typedef struct cache {
#if APR_HAS_THREADS
} cache;
} cachedmap;
typedef struct backrefinfo {
const char *source;
} backrefinfo;
typedef struct result_list {
const char *string;
} result_list;
request_rec *r;
const char *uri;
const char *vary_this;
const char *vary;
char *perdir;
} rewrite_ctx;
static int proxy_available;
static int rewrite_rand_init_done = 0;
#ifndef REWRITELOG_DISABLED
const char *fmt, ...)
const char *fmt, ...)
int redir;
++redir;
(void *)(r->server),
text);
switch (*uri++) {
unsigned char *where)
return where;
const unsigned char *s = (const unsigned char *)path;
unsigned char *d = (unsigned char *)copy;
return copy;
char *cp;
return NULL;
++cp;
++cp;
NULL);
if ( qsdiscard ) {
if (q != NULL) {
char *olduri;
if (qsappend) {
if (!len) {
char *cp;
apr_size_t l;
unsigned short port;
++cp;
++cp;
if (!*url) {
const char *thisserver;
char *thisport;
int port;
r->filename);
const char *root;
const char *slash;
char *statpath;
const char *subst)
--len;
char *output;
++slen;
output));
return output;
return input;
char *val)
if (cachep) {
#if APR_HAS_THREADS
if (!map) {
apr_pool_t *p;
#if APR_HAS_THREADS
#if APR_HAS_THREADS
apr_pool_t *p)
if (cachep) {
#if APR_HAS_THREADS
if (map) {
if (val) {
#if APR_HAS_THREADS
return val;
#if APR_HAS_THREADS
for (p = key; *p; ++p) {
*p = apr_toupper(*p);
return key;
return key;
return key;
char *p = value;
if (!rewrite_rand_init_done) {
++value;
return value;
const char *desc)
procattr, p);
if (fpin) {
if (fpout) {
return (rc);
return APR_SUCCESS;
void *val;
return rc;
return APR_SUCCESS;
return NULL;
p = line;
c = key;
while (*p && apr_isspace(*p)) {
while (*p && !apr_isspace(*p)) {
return value;
char *value;
return NULL;
return value;
const char *errmsg;
if (rv != 0) {
return NULL;
return NULL;
char *buf;
int found_nl = 0;
#ifndef NO_WRITEV
return NULL;
if (rewrite_mapr_lock_acquire) {
#ifdef NO_WRITEV
--eolc;
if (i < eolc) {
i -= eolc;
++found_nl;
else if (eolc) {
eolc = 0;
++found_nl;
buf[i++] = c;
if (!buflist) {
combined_len += i;
if (buflist) {
while (buflist) {
i = combined_len;
if (rewrite_mapr_lock_acquire) {
return NULL;
return buf;
rewritemap_entry *s;
char *value;
return NULL;
switch (s->type) {
case MAPTYPE_RND:
case MAPTYPE_TXT:
s->checkfile);
return NULL;
if (!value) {
if (!value) {
return NULL;
case MAPTYPE_DBM:
s->checkfile);
return NULL;
if (!value) {
if (!value) {
return NULL;
return value;
case MAPTYPE_DBD:
if (!value) {
return NULL;
return value;
case MAPTYPE_DBD_CACHE:
if (!value) {
if (!value) {
return NULL;
return value;
case MAPTYPE_PRG:
if (!value) {
key));
return NULL;
return value;
case MAPTYPE_INT:
if (!value) {
key));
return NULL;
return value;
return NULL;
if (val) {
return val;
const char *result;
if (!result) {
if (!result) {
const char *path;
ctx->r = r;
return (char *)result;
NULL);
ctx->r = r;
return (char *)result;
for (p = var; *p; ++p) {
*p = apr_toupper(*p);
switch (varlen) {
return (char *)result;
#if APR_HAVE_IPV6
r->pool);
r->pool);
unsigned depth;
else if (*s == LEFT_CURLY) {
++depth;
return NULL;
unsigned depth;
return NULL;
else if (*s == LEFT_CURLY) {
++depth;
return NULL;
unsigned spc = 0;
sizeof(result_list));
++outlen;
char *endp;
if (!endp) {
char *key;
if (!key) {
if (dflt) {
if (key) {
++outlen;
sizeof(result_list));
p += span;
} while (result);
while (env) {
name++;
char *var;
char *val;
char *domain;
char *expires;
char *path;
char *secure;
char *httponly;
char *tok_cntx;
char *cookie;
char *notename;
void *data;
if (!data) {
if (expires) {
long exp_min;
if (exp_min) {
: NULL,
: NULL,
NULL);
var));
while (cookie) {
#if APR_HAS_USER
char *p, *user;
if (p > user) {
char *homedir;
return homedir;
return uri;
return rc;
return APR_SUCCESS;
if (rewrite_mapr_lock_acquire) {
char quote;
++str;
++str;
if (!*str) {
++str;
++str;
if (!*str) {
++str;
if (!*str) {
++str;
a->server = s;
sizeof(rewrite_server_conf));
sizeof(rewrite_perdir_conf));
return NULL;
int options = 0;
while (*option) {
return NULL;
const char *a2)
const char *fname;
if (colon) {
if (!fname) {
return NULL;
const char *a1)
return NULL;
const char *err;
++key;
while (*key) {
++key;
--endp;
if (val) {
if (err) {
return err;
return NULL;
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
const char *err;
++a2;
switch (*a2) {
? AP_REG_ICASE : 0));
if (!regexp) {
if (err)
return NULL;
int error = 0;
switch (*key++) {
++error;
if (!cp) {
++error;
if (!cp) {
++error;
++error;
++error;
++error;
++error;
++error;
++error;
++error;
int status = 0;
if (*val) {
int idx =
val);
++error;
++error;
++error;
++error;
if (error) {
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
const char *err;
++a1;
? AP_REG_ICASE : 0));
if (!regexp) {
sizeof(rewritecond_entry));
sizeof(rewritecond_entry));
return NULL;
for (i = 0; i < lena; ++i) {
int rc = 0;
int basis;
switch (p->ptype) {
case CONDPAT_FILE_EXISTS:
case CONDPAT_FILE_SIZE:
case CONDPAT_FILE_LINK:
#if !defined(OS2)
case CONDPAT_FILE_DIR:
case CONDPAT_FILE_XBIT:
case CONDPAT_LU_URL:
case CONDPAT_LU_FILE:
case CONDPAT_STR_GE:
basis = 0;
goto test_str_g;
case CONDPAT_STR_GT:
case CONDPAT_STR_LE:
basis = 0;
goto test_str_l;
case CONDPAT_STR_LT:
case CONDPAT_STR_EQ:
case CONDPAT_AP_EXPR:
rc = 0;
return rc;
char *expanded;
if (p->forced_mimetype) {
if (*expanded) {
expanded);
if (p->forced_handler) {
if (*expanded) {
expanded);
int i, rc;
int is_proxyreq = 0;
if (!rc) {
c = &conds[++i];
else if (!rc) {
NULL)
newuri));
r->filename));
r->filename));
reduce_uri(r);
char *perdir)
int changed;
int rc;
ctx->r = r;
changed = 0;
loop:
p = &entries[i];
if (rc) {
return ACTION_STATUS;
goto loop;
if (p->skip > 0) {
s = p->skip;
p = &entries[i];
return changed;
if (map_pfn_register) {
return OK;
server_rec *s)
return HTTP_INTERNAL_SERVER_ERROR;
for (; s; s = s->next) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
if (rewrite_mapr_lock_acquire) {
if (!init_cache(p)) {
const char *saved_rulestatus;
const char *var;
const char *thisserver;
char *thisport;
const char *thisurl;
unsigned int port;
int rulestatus;
void *skipdata;
return DECLINED;
return DECLINED;
return DECLINED;
r->filename));
if (rulestatus) {
unsigned skip;
int n = r->status;
if (!proxy_available) {
return HTTP_FORBIDDEN;
r->filename));
return OK;
r->filename));
if (r->args) {
? r->args
NULL);
n = r->status;
return DECLINED;
#if APR_HAS_USER
return HTTP_BAD_REQUEST;
int res;
return res;
r->filename));
return OK;
return DECLINED;
char *cp;
char *cp2;
const char *ccp;
apr_size_t l;
int rulestatus;
int is_proxyreq;
void *skipdata;
return DECLINED;
return DECLINED;
return DECLINED;
* URL: http://localhost/foo and .htaccess is located in foo directory
if (!is_proxyreq) {
return DECLINED;
return DECLINED;
return HTTP_FORBIDDEN;
if (rulestatus) {
unsigned skip;
int n = r->status;
return OK;
* hostname and compare/substitute only the stuff after it.
r->filename));
if (r->args) {
? r->args
NULL);
r->args ,
n = r->status;
r->filename, n));
return HTTP_BAD_REQUEST;
return OK;
r->filename+l));
return OK;
return DECLINED;
r->filename, t));
ap_set_content_type(r, t);
r->handler = t;
return OK;
return DECLINED;
return DECLINED;
return OK;
{ NULL }