mod_rewrite.c revision 52f13df58bab77959915d669d156f89c3f1aa7ed
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering/* Copyright 1999-2004 Apache Software Foundation
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * Licensed under the Apache License, Version 2.0 (the "License");
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * you may not use this file except in compliance with the License.
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * You may obtain a copy of the License at
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * http://www.apache.org/licenses/LICENSE-2.0
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * Unless required by applicable law or agreed to in writing, software
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * distributed under the License is distributed on an "AS IS" BASIS,
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * See the License for the specific language governing permissions and
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * limitations under the License.
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * URL Rewriting Module
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * This module uses a rule-based rewriting engine (based on a
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * regular-expression parser) to rewrite requested URLs on the fly.
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * It supports an unlimited number of additional rule conditions (which can
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * operate on a lot of variables, even on HTTP headers) for granular
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * matching and even external database lookups (either via plain text
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * tables, DBM hash files or even external processes) for advanced URL
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * substitution.
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * It operates on the full URLs (including the PATH_INFO part) both in
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * per-server context (httpd.conf) and per-dir context (.htaccess) and even
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * can generate QUERY_STRING parts on result. The rewriting result finally
c75f27ea2b483f91d437ebaf8494457dc76f3fd6Lennart Poettering * can lead to internal subprocessing, external request redirection or even
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering * to internal proxy throughput.
c75f27ea2b483f91d437ebaf8494457dc76f3fd6Lennart Poettering * This module was originally written in April 1996 and
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * gifted exclusively to the The Apache Software Foundation in July 1997 by
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering * Ralf S. Engelschall
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poettering/* XXX: Do we really need these headers? */
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering#define MOD_REWRITE_SET_MUTEX_PERMS /* XXX Apache should define something */
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * in order to improve performance on running production systems, you
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * may strip all rewritelog code entirely from mod_rewrite by using the
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * -DREWRITELOG_DISABLED compiler option.
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
d7c7c334f56edab8cfc102b657366277a65738cfLennart Poettering * responsible for answering all the mod_rewrite questions out there.
#ifndef REWRITELOG_DISABLED
#define rewritelog(x)
#ifndef RAND_MAX
#ifndef REWRITE_MAX_TXT_MAP_LINE
#ifndef REWRITE_MAX_PRG_MAP_LINE
CONDPAT_REGEX = 0,
} pattern_type;
typedef struct data_item {
char *data;
} data_item;
#ifndef REWRITELOG_DISABLED
typedef struct cache {
#if APR_HAS_THREADS
} 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;
for (p = key; *p; ++p) {
*p = apr_tolower(*p);
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;
apr_size_t i;
int eolc = 0;
#ifndef NO_WRITEV
return NULL;
if (rewrite_mapr_lock_acquire) {
#ifdef NO_WRITEV
i -= --eolc;
else if (eolc) {
eolc = 0;
buf[i++] = c;
if (rewrite_mapr_lock_acquire) {
return NULL;
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_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 *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 MOD_REWRITE_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;
while (*option) {
if (limit <= 0) {
return NULL;
#ifndef REWRITELOG_DISABLED
return NULL;
const char *a1)
return NULL;
const char *a2)
const char *fname;
const char *ignored_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) {
? REG_ICASE : 0));
if (!regexp) {
return NULL;
int error = 0;
switch (*key++) {
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;
? 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;
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 MOD_REWRITE_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;
return DECLINED;
return DECLINED;
* URL: http://localhost/foo and .htaccess is located in foo directory
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);
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;
if (!reqc) {
return DECLINED;
return DECLINED;
if (is_redirect_limit_exceeded(r)) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
#ifdef REWRITELOG_DISABLED
#ifndef REWRITELOG_DISABLED
{ NULL }