/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* _ _ _ * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| * |_____| * * URL Rewriting Module * * This module uses a rule-based rewriting engine (based on a * regular-expression parser) to rewrite requested URLs on the fly. * * It supports an unlimited number of additional rule conditions (which can * operate on a lot of variables, even on HTTP headers) for granular * matching and even external database lookups (either via plain text * tables, DBM hash files or even external processes) for advanced URL * substitution. * * It operates on the full URLs (including the PATH_INFO part) both in * per-server context (httpd.conf) and per-dir context (.htaccess) and even * can generate QUERY_STRING parts on result. The rewriting result finally * can lead to internal subprocessing, external request redirection or even * to internal proxy throughput. * * This module was originally written in April 1996 and * gifted exclusively to the The Apache Software Foundation in July 1997 by * * Ralf S. Engelschall * rse engelschall.com * www.engelschall.com
*/
/* * in order to improve performance on running production systems, you * may strip all rewritelog code entirely from mod_rewrite by using the * -DREWRITELOG_DISABLED compiler option. * * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are * responsible for answering all the mod_rewrite questions out there.
*/ /* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */ #ifdef APLOG_MAX_LOGLEVEL #if APLOG_MAX_LOGLEVEL < APLOG_TRACE1 #ifndef REWRITELOG_DISABLED #define REWRITELOG_DISABLED #endif #endif #endif
/* return code of the rewrite rule * the result may be escaped - or not
*/ #define ACTION_NORMAL (1<<0) #define ACTION_NOESCAPE (1<<1) #define ACTION_STATUS (1<<2) #define ACTION_STATUS_SET (1<<3)
/* * expansion result items on the stack to save some cycles * * (5 == about 2 variables like "foo%{var}bar%{var}baz")
*/ #define SMALL_EXPANSION 5
/* * check that a subrequest won't cause infinite recursion * * either not in a subrequest, or in a subrequest * and URIs aren't NULL and sub/main URIs differ
*/ #define subreq_ok(r) (!r->main || \
(r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
typedefstruct { constchar *datafile; /* filename for map data files */ constchar *dbmtype; /* dbm type for dbm map data files */ constchar *checkfile; /* filename to check for map existence */ constchar *cachename; /* for cached maps (txt/rnd/dbm) */ int type; /* the type of the map */
apr_file_t *fpin; /* in file pointer for program maps */
apr_file_t *fpout; /* out file pointer for program maps */
apr_file_t *fperr; /* err file pointer for program maps */ char *(*func)(request_rec *, /* function pointer for internal maps */ char *); char **argv; /* argv of the external rewrite map */ constchar *dbdq; /* SQL SELECT statement for rewritemap */ constchar *checkfile2; /* filename to check for map existence
NULL if only one file */ constchar *user; /* run RewriteMap program as this user */ constchar *group; /* run RewriteMap program as this group */
} rewritemap_entry;
typedefenum {
RULE_RC_NOMATCH = 0, /* the rule didn't match */
RULE_RC_MATCH = 1, /* a matching rule w/ substitution */
RULE_RC_NOSUB = 2, /* a matching rule w/ no substitution */
RULE_RC_STATUS_SET = 3 /* a matching rule that has set an HTTP error
to be returned in r->status */
} rule_return_type;
typedefenum {
COND_RC_NOMATCH = 0, /* the cond didn't match */
COND_RC_MATCH = 1, /* the cond matched */
COND_RC_STATUS_SET = 3 /* The condition eval set a final r->status */
} cond_return_type;
typedefstruct { char *input; /* Input string of RewriteCond */ char *pattern; /* the RegExp pattern string */
ap_regex_t *regexp; /* the precompiled regexp */
ap_expr_info_t *expr; /* the compiled ap_expr */ int flags; /* Flags which control the match */
pattern_type ptype; /* pattern type */ int pskip; /* back-index to display pattern */
} rewritecond_entry;
/* single linked list for env vars and cookies */ typedefstruct data_item { struct data_item *next; char *data;
} data_item;
typedefstruct {
apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */ char *pattern; /* the RegExp pattern string */
ap_regex_t *regexp; /* the RegExp pattern compilation */ char *output; /* the Substitution string */ int flags; /* Flags which control the substitution */ char *forced_mimetype; /* forced MIME type of substitution */ char *forced_handler; /* forced content handler of subst. */ int forced_responsecode; /* forced HTTP response status */
data_item *env; /* added environment variables */
data_item *cookie; /* added cookies */ int skip; /* number of next rules to skip */ int maxrounds; /* limit on number of loops with N flag */ constchar *escapes; /* specific backref escapes */ constchar *noescapes; /* specific backref chars not to escape */
} rewriterule_entry;
typedefstruct { int state; /* the RewriteEngine state */ int options; /* the RewriteOption state */
apr_hash_t *rewritemaps; /* the RewriteMap entries */
apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
apr_array_header_t *rewriterules; /* the RewriteRule entries */
server_rec *server; /* the corresponding server indicator */ unsignedint state_set:1; unsignedint options_set:1;
} rewrite_server_conf;
typedefstruct { int state; /* the RewriteEngine state */ int options; /* the RewriteOption state */
apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
apr_array_header_t *rewriterules; /* the RewriteRule entries */ char *directory; /* the directory where it applies */ constchar *baseurl; /* the base-URL where it applies */ unsignedint state_set:1; unsignedint options_set:1; unsignedint baseurl_set:1;
} rewrite_perdir_conf;
/* cached maps contain an mtime for the whole map and live in a subpool * of the cachep->pool. That makes it easy to forget them if necessary.
*/ typedefstruct {
apr_time_t mtime;
apr_pool_t *pool;
apr_hash_t *entries;
} cachedmap;
/* the regex structure for the * substitution of backreferences
*/ typedefstruct backrefinfo { constchar *source;
ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
} backrefinfo;
/* single linked list used for * variable expansion
*/ typedefstruct result_list { struct result_list *next;
apr_size_t len; constchar *string;
} result_list;
/* Slightly complicated definitions follow here to support * compile-time choice of enabled/disabled of rewritelog both with or * without C99 support, where varargs macros are only available with * C99. * * do_rewritelog is defined for (rewritelog enabled) or (rewritelog * disabled AND no C99 support) but is an noop for the latter case and * should get optimized away. * * For the (rewritelog disabled) case the function is defined * differently for C99/non-C99. For C99, the rewritelog() macro passes * __LINE__, allowing accurate logging of line numbers. For non-C99 * the line number used for rewritelog() tracing is always
* constant. */
/* * +-------------------------------------------------------+ * | | * | URI and path functions * | | * +-------------------------------------------------------+
*/
/* return number of chars of the scheme (incl. '://') * if the URI is absolute (includes a scheme etc.) * otherwise 0. * If supportqs is not NULL, we return a whether or not * the scheme supports a query string or not. * * NOTE: If you add new schemes here, please have a * look at escape_absolute_uri and splitout_queryargs. * Not every scheme takes query strings and some schemes * may be handled in a special way. * * XXX: we may consider a scheme registry, perhaps with * appropriate escape callbacks to allow other modules * to extend mod_rewrite at runtime.
*/ staticunsigned is_absolute_uri(char *uri, int *supportsqs)
{ int dummy, *sqs;
/* * escape absolute uri, which may or may not be path oriented. * So let's handle them differently.
*/ staticchar *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
{ char *cp;
/* be safe. * NULL should indicate elsewhere, that something's wrong
*/ if (!scheme || strlen(uri) < scheme) { return NULL;
}
cp = uri + scheme;
/* scheme with authority part? */ if (cp[-1] == '/') { /* skip host part */ while (*cp && *cp != '/') {
++cp;
}
/* nothing after the hostpart. ready! */ if (!*cp || !*++cp) { return apr_pstrdup(p, uri);
}
/* special thing for ldap. * The parts are separated by question marks. From RFC 2255: * ldapurl = scheme "://" [hostport] ["/" * [dn ["?" [attributes] ["?" [scope] * ["?" [filter] ["?" extensions]]]]]]
*/ if (!ap_cstr_casecmpn(uri, "ldap", 4)) { char *token[5]; int c = 0;
token[0] = cp = apr_pstrdup(p, cp); while (*cp && c < 4) { if (*cp == '?') {
token[++c] = cp + 1;
*cp = '\0';
}
++cp;
}
/* Nothing special here. Apply normal escaping. */ return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
ap_escape_uri(p, cp), NULL);
}
/* * split out a QUERY_STRING part from * the current URI string
*/ staticvoid splitout_queryargs(request_rec *r, int flags)
{ char *q; int split, skip; int qsappend = flags & RULEFLAG_QSAPPEND; int qsdiscard = flags & RULEFLAG_QSDISCARD; int qslast = flags & RULEFLAG_QSLAST;
if (flags & RULEFLAG_QSNONE) {
rewritelog(r, 2, NULL, "discarding query string, no parse from substitution");
r->args = NULL; return;
}
/* don't touch, unless it's a scheme for which a query string makes sense. * See RFC 1738 and RFC 2368.
*/ if ((skip = is_absolute_uri(r->filename, &split))
&& !split) {
r->args = NULL; /* forget the query that's still flying around */ return;
}
/* cut the hostname and port out of the URI */
cp = host = scratch + l + 3; /* 3 == strlen("://") */ while (*cp && *cp != '/' && *cp != ':') {
++cp;
}
if (*cp == ':') { /* additional port given */
*cp++ = '\0';
portp = cp; while (*cp && *cp != '/') {
++cp;
}
*cp = '\0';
port = atoi(portp);
url = r->filename + (cp - scratch); if (!*url) {
url = "/";
}
} elseif (*cp == '/') { /* default port */
*cp = '\0';
port = ap_default_port(r);
url = r->filename + (cp - scratch);
} else {
port = ap_default_port(r);
url = "/";
}
/* now check whether we could reduce it to a local path... */ if (ap_matches_request_vhost(r, host, port)) {
rewrite_server_conf *conf =
ap_get_module_config(r->server->module_config, &rewrite_module);
rewritelog(r, 3, NULL, "reduce %s -> %s", r->filename, url);
r->filename = apr_pstrdup(r->pool, url);
/* remember that the uri was reduced */ if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
apr_table_setn(r->notes, "mod_rewrite_uri_reduced", "true");
}
}
}
return;
}
/* * add 'http[s]://ourhost[:ourport]/' to URI * if URI is still not fully qualified
*/ staticvoid fully_qualify_uri(request_rec *r)
{ if (r->method_number == M_CONNECT) { return;
} elseif (!is_absolute_uri(r->filename, NULL)) { constchar *thisserver; char *thisport; int port;
if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) { if (!lastsub) {
rewritelog(r, 3, NULL, "prefix_stat no lastsub subst prefix %s", statpath); return 1;
}
rewritelog(r, 3, NULL, "prefix_stat compare statpath %s and lastsub output %s STATOK %d ",
statpath, lastsub->output, lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT); if (lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT) { return 1;
} else { constchar *docroot = ap_document_root(r); constchar *context_docroot = ap_context_document_root(r); /* * As an example, path (r->filename) is /var/foo/bar/baz.html * even if the flag is not set, we can accept a rule that * began with a literal /var (stapath), or if the entire path * starts with the docroot or context document root
*/ if (startsWith(r, lastsub->output, statpath) ||
startsWith(r, path, docroot) ||
((docroot != context_docroot) &&
startsWith(r, path, context_docroot))) { return 1;
}
}
}
}
/* prefix will be added */ return 0;
}
/* * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
*/ staticchar *subst_prefix_path(request_rec *r, char *input, constchar *match, constchar *subst)
{
apr_size_t len = strlen(match);
/* Now we should have a valid map->entries hash, where we * can store our value. * * We need to copy the key and the value into OUR pool, * so that we don't leave it during the r->pool cleanup.
*/
apr_hash_set(map->entries,
apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING,
apr_pstrdup(map->pool, val));
if (map) { /* if this map is outdated, forget it. */ if (map->mtime != t) {
apr_pool_clear(map->pool);
map->entries = apr_hash_make(map->pool);
map->mtime = t;
} else {
val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING); if (val) { /* copy the cached value into the supplied pool, * where it belongs (r->pool usually)
*/
val = apr_pstrdup(p, val);
}
}
}
if ((rv = apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
r->pool)) != APR_SUCCESS)
{
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00655) "mod_rewrite: can't open text RewriteMap file %s", file); return NULL;
}
keylast = key + strlen(key);
value = NULL; while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) { char *p, *c;
/* ignore comments and lines starting with whitespaces */ if (*line == '#' || apr_isspace(*line)) { continue;
}
p = line;
c = key; while (c < keylast && *p == *c && !apr_isspace(*p)) {
++p;
++c;
}
/* key doesn't match - ignore. */ if (c != keylast || !apr_isspace(*p)) { continue;
}
/* jump to the value */ while (apr_isspace(*p)) {
++p;
}
/* no value? ignore */ if (!*p) { continue;
}
/* extract the value and return. */
c = p; while (*p && !apr_isspace(*p)) {
++p;
}
value = apr_pstrmemdup(r->pool, c, p - c); break;
}
apr_file_close(fp);
/* when `RewriteEngine off' was used in the per-server * context then the rewritemap-programs were not spawned. * In this case using such a map (usually in per-dir context) * is useless because it is not available. * * newlines in the key leave bytes in the pipe and cause * bad things to happen (next map lookup will use the chars * after the \n instead of the new key etc etc - in other words, * the Rewritemap falls out of sync with the requests).
*/ if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) { return NULL;
}
/* take the lock */ if (rewrite_mapr_lock_acquire) {
rv = apr_global_mutex_lock(rewrite_mapr_lock_acquire); if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00659) "apr_global_mutex_lock(rewrite_mapr_lock_acquire) " "failed"); return NULL; /* Maybe this should be fatal? */
}
}
/* read in the response value */
nbytes = 1;
apr_file_read(fpout, &c, &nbytes); do {
i = 0; while (nbytes == 1 && (i < REWRITE_PRG_MAP_BUF)) { if (c == eol[eolc]) { if (!eol[++eolc]) { /* remove eol from the buffer */
--eolc; if (i < eolc) {
curbuf->len -= eolc-i;
i = 0;
} else {
i -= eolc;
}
++found_nl; break;
}
}
/* only partial (invalid) eol sequence -> reset the counter */ elseif (eolc) {
eolc = 0;
}
/* catch binary mode, e.g. on Win32 */ elseif (c == '\n') {
++found_nl; break;
}
/* well, if there wasn't a newline yet, we need to read further */ if (buflist || (nbytes == 1 && !found_nl)) { if (!buflist) {
curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist));
} elseif (i) {
curbuf->next = apr_palloc(r->pool, sizeof(*buflist));
curbuf = curbuf->next;
p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */ while (buflist) { if (buflist->len) {
memcpy(p, buflist->string, buflist->len);
p += buflist->len;
}
buflist = buflist->next;
}
*p = '\0';
i = combined_len;
} else {
buf[i] = '\0';
}
/* give the lock back */ if (rewrite_mapr_lock_acquire) {
rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire); if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00660) "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) " "failed"); return NULL; /* Maybe this should be fatal? */
}
}
/* catch the "failed" case */ if (i == 4 && !strcasecmp(buf, "NULL")) { return NULL;
}
/* fast tests for variable length variables (sic) first */ if (var[3] == ':') { if (var[4] && !strncasecmp(var, "ENV", 3)) {
var += 4;
result = apr_table_get(r->notes, var);
if (!result) {
result = apr_table_get(r->subprocess_env, var);
} if (!result) {
result = getenv(var);
}
} elseif (var[4] && !strncasecmp(var, "SSL", 3)) {
result = ap_ssl_var_lookup(r->pool, r->server, r->connection, r,
var + 4);
}
} elseif (var[4] == ':') { if (var[5]) {
request_rec *rr; constchar *path;
return (char *)result;
}
} elseif (!strncasecmp(var, "LA-F", 4)) { if (ctx->uri && subreq_ok(r)) {
path = ctx->uri; if (ctx->perdir && *path == '/') { /* sigh, the user wants a file based subrequest, but * we can't do one, since we don't know what the file * path is! In this case behave like LA-U.
*/
rr = ap_sub_req_lookup_uri(path, r, NULL);
} else { if (ctx->perdir) {
rewrite_perdir_conf *conf;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.