mod_rewrite.c revision 06d3a46db9574ad55b65fed36b856fee72bd71a7
842ae4bd224140319ae7feec1872b93dfd491143fielding/* Licensed to the Apache Software Foundation (ASF) under one or more
842ae4bd224140319ae7feec1872b93dfd491143fielding * contributor license agreements. See the NOTICE file distributed with
842ae4bd224140319ae7feec1872b93dfd491143fielding * this work for additional information regarding copyright ownership.
842ae4bd224140319ae7feec1872b93dfd491143fielding * The ASF licenses this file to You under the Apache License, Version 2.0
842ae4bd224140319ae7feec1872b93dfd491143fielding * (the "License"); you may not use this file except in compliance with
842ae4bd224140319ae7feec1872b93dfd491143fielding * the License. You may obtain a copy of the License at
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * Unless required by applicable law or agreed to in writing, software
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * distributed under the License is distributed on an "AS IS" BASIS,
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * See the License for the specific language governing permissions and
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * limitations under the License.
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * URL Rewriting Module
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This module uses a rule-based rewriting engine (based on a
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * regular-expression parser) to rewrite requested URLs on the fly.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * It supports an unlimited number of additional rule conditions (which can
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * operate on a lot of variables, even on HTTP headers) for granular
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * matching and even external database lookups (either via plain text
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * tables, DBM hash files or even external processes) for advanced URL
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * substitution.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * It operates on the full URLs (including the PATH_INFO part) both in
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * per-server context (httpd.conf) and per-dir context (.htaccess) and even
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * can generate QUERY_STRING parts on result. The rewriting result finally
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * can lead to internal subprocessing, external request redirection or even
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * to internal proxy throughput.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This module was originally written in April 1996 and
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * gifted exclusively to the The Apache Software Foundation in July 1997 by
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Ralf S. Engelschall
1860b2b5f1de31f8cf9d95f1b394fe98c8dbfab7rbb/* XXX: Do we really need these headers? */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * in order to improve performance on running production systems, you
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * may strip all rewritelog code entirely from mod_rewrite by using the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * -DREWRITELOG_DISABLED compiler option.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * responsible for answering all the mod_rewrite questions out there.
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm#else /* !REWRITELOG_DISABLED */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif /* REWRITELOG_DISABLED */
e8f95a682820a599fe41b22977010636be5c2717jim/* remembered mime-type for [T=...] */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler"
2b672ae3a6d190fb62d04f4f47bbdc0a2bde151fcolm#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/* return code of the rewrite rule
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the result may be escaped - or not
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* max cookie size in rfc 2109 */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* max line length (incl.\n) in text rewrite maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* buffer length for prg rewrite maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* for better readbility */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * expansion result items on the stack to save some cycles
db848a70422b56cf0f15a47c37e17ebe05e2ce04stoddard * (5 == about 2 variables like "foo%{var}bar%{var}baz")
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * check that a subrequest won't cause infinite recursion
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * either not in a subrequest, or in a subrequest
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * and URIs aren't NULL and sub/main URIs differ
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * +-------------------------------------------------------+
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * | Types and Structures
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm * +-------------------------------------------------------+
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *datafile; /* filename for map data files */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *dbmtype; /* dbm type for dbm map data files */
2b672ae3a6d190fb62d04f4f47bbdc0a2bde151fcolm const char *checkfile; /* filename to check for map existence */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *cachename; /* for cached maps (txt/rnd/dbm) */
43c3e6a4b559b76b750c245ee95e2782c15b4296jim apr_file_t *fpin; /* in file pointer for program maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_file_t *fpout; /* out file pointer for program maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_file_t *fperr; /* err file pointer for program maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm char *(*func)(request_rec *, /* function pointer for internal maps */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *dbdq; /* SQL SELECT statement for rewritemap */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *checkfile2; /* filename to check for map existence
43c3e6a4b559b76b750c245ee95e2782c15b4296jim NULL if only one file */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* special pattern types for RewriteCond */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef enum {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm/* single linked list for env vars and cookies */
e8f95a682820a599fe41b22977010636be5c2717jimtypedef struct data_item {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm ap_regex_t *regexp; /* the RegExp pattern compilation */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm char *forced_mimetype; /* forced MIME type of substitution */
e8f95a682820a599fe41b22977010636be5c2717jim char *forced_handler; /* forced content handler of subst. */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm int forced_responsecode; /* forced HTTP response status */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm const char *rewritelogfile; /* the RewriteLog filename */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_file_t *rewritelogfp; /* the RewriteLog open filepointer */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm int rewriteloglevel; /* the RewriteLog level of verbosity */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_array_header_t *rewriterules; /* the RewriteRule entries */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm server_rec *server; /* the corresponding server indicator */
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct {
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colm apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
e8f95a682820a599fe41b22977010636be5c2717jim apr_array_header_t *rewriterules; /* the RewriteRule entries */
e8f95a682820a599fe41b22977010636be5c2717jim const char *baseurl; /* the base-URL where it applies */
e8f95a682820a599fe41b22977010636be5c2717jim/* the (per-child) cache structures.
0b4b04d8621478ba59f0a6ba2950ddc02ab92b58colmtypedef struct cache {
} cachedmap;
typedef struct backrefinfo {
char *source;
int nsub;
} 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;
static const char *lockname;
#ifndef REWRITELOG_DISABLED
#ifndef REWRITELOG_DISABLED
const char *fname;
if (!fname) {
if (!fname) {
!= APR_SUCCESS) {
const char *fmt, ...)
int redir;
++redir;
current_logtime(r),
(void *)(r->server),
text);
switch (*uri++) {
char *cp;
return NULL;
++cp;
++cp;
NULL);
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);
int lock_warning_issued = 0;
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;
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) {
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) {
NULL);
var));
while (cookie) {
#if APR_HAS_USER
char *p, *user;
if (p > user) {
char *homedir;
return homedir;
return uri;
return APR_SUCCESS;
APR_LOCK_DEFAULT, p);
return rc;
#ifdef AP_NEED_SET_MUTEX_PERMS
return rc;
return APR_SUCCESS;
return APR_SUCCESS;
char quote;
++str;
++str;
if (!*str) {
++str;
++str;
if (!*str) {
++str;
if (!*str) {
++str;
#ifndef REWRITELOG_DISABLED
a->rewriteloglevel = 0;
a->server = s;
sizeof(rewrite_server_conf));
#ifndef REWRITELOG_DISABLED
#ifndef REWRITELOG_DISABLED
sizeof(rewrite_perdir_conf));
return NULL;
int options = 0;
while (*option) {
return NULL;
#ifndef REWRITELOG_DISABLED
return NULL;
const char *a1)
return NULL;
const char *a2)
const char *fname;
if (colon) {
if (!fname) {
return NULL;
const char *error;
return error;
if (!lockname) {
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;
return err;
++a2;
switch (*a2) {
? AP_REG_ICASE : 0));
if (!regexp) {
return NULL;
int error = 0;
switch (*key++) {
++error;
if (!cp) {
++error;
if (!cp) {
++error;
++error;
++error;
++error;
++error;
++error;
++error;
++error;
int status = 0;
int idx =
val);
++error;
++error;
++error;
++error;
if (error) {
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
const char *err;
return 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;
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_GT:
case CONDPAT_STR_LT:
case CONDPAT_STR_EQ:
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];
p = &entries[i];
return changed;
if (map_pfn_register) {
return OK;
server_rec *s)
void *data;
int first_time = 0;
if (!data) {
#ifndef REWRITELOG_DISABLED
return HTTP_INTERNAL_SERVER_ERROR;
#ifdef AP_NEED_SET_MUTEX_PERMS
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
for (; s; s = s->next) {
#ifndef REWRITELOG_DISABLED
if (!open_rewritelog(s, p)) {
return HTTP_INTERNAL_SERVER_ERROR;
if (!first_time) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
lockname, p);
#ifndef REWRITELOG_DISABLED
if (!init_cache(p)) {
const char *saved_rulestatus;
const char *var;
const char *thisserver;
char *thisport;
const char *thisurl;
unsigned int port;
int rulestatus;
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;
char *ofilename;
int is_proxyreq;
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;
r->filename));
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);
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;
#ifdef REWRITELOG_DISABLED
#ifndef REWRITELOG_DISABLED
{ NULL }