mod_rewrite.c revision b0c8eb40d588cf647a0bcbccfd51644a198faed9
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.
36a72c96fc2dda27eadbae8a108fa428cc1419c1wrowe * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
1723d9ccdd3b647f5b7bae44cab9ab3eca7a4874dougm * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
59b96ad34c087942eea06884c97d12c2796a977amturk * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
70535d6421eb979ac79d8f49d31cd94d75dd8b2fjorton * URL Rewriting Module
2555a6b5da21d61804f47084d8fcc98eb4acbc42wrowe * This module uses a rule-based rewriting engine (based on a
2555a6b5da21d61804f47084d8fcc98eb4acbc42wrowe * regular-expression parser) to rewrite requested URLs on the fly.
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * It supports an unlimited number of additional rule conditions (which can
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * operate on a lot of variables, even on HTTP headers) for granular
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * matching and even external database lookups (either via plain text
2555a6b5da21d61804f47084d8fcc98eb4acbc42wrowe * tables, DBM hash files or even external processes) for advanced URL
1723d9ccdd3b647f5b7bae44cab9ab3eca7a4874dougm * substitution.
e9501b71b8a1e76384cb010b1e41e76a1e47aacctrawick * It operates on the full URLs (including the PATH_INFO part) both in
e9501b71b8a1e76384cb010b1e41e76a1e47aacctrawick * per-server context (httpd.conf) and per-dir context (.htaccess) and even
6335eb31f0f0ed54628a04ed32946360b8b77684minfrin * can generate QUERY_STRING parts on result. The rewriting result finally
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * can lead to internal subprocessing, external request redirection or even
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * to internal proxy throughput.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * This module was originally written in April 1996 and
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * gifted exclusively to the The Apache Software Foundation in July 1997 by
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * Ralf S. Engelschall
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe/* XXX: Do we really need these headers? */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturkstatic void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowe * in order to improve performance on running production systems, you
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim * may strip all rewritelog code entirely from mod_rewrite by using the
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe * -DREWRITELOG_DISABLED compiler option.
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk * responsible for answering all the mod_rewrite questions out there.
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk#else /* !REWRITELOG_DISABLED */
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe#endif /* REWRITELOG_DISABLED */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* remembered mime-type for [T=...] */
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
9621e4c4056383e4a2b844b14687bae500b33a82wrowe#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler"
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* return code of the rewrite rule
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowe * the result may be escaped - or not
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* max cookie size in rfc 2109 */
b842b65e0618c5535233b197f03dc917d184adb3jim/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk/* max line length (incl.\n) in text rewrite maps */
eee895b02dd7867622afd0a8a94f2efc7de9c618wrowe/* buffer length for prg rewrite maps */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* for better readbility */
4e0b7720fce1e872ea6afd2421b8b6addacf9bd0jim * expansion result items on the stack to save some cycles
f73a8fabbdc4ec11c8b475e9f48539de0c4f82ebmturk * (5 == about 2 variables like "foo%{var}bar%{var}baz")
f73a8fabbdc4ec11c8b475e9f48539de0c4f82ebmturk * check that a subrequest won't cause infinite recursion
f73a8fabbdc4ec11c8b475e9f48539de0c4f82ebmturk * either not in a subrequest, or in a subrequest
e8f95a682820a599fe41b22977010636be5c2717jim * and URIs aren't NULL and sub/main URIs differ
4e0b7720fce1e872ea6afd2421b8b6addacf9bd0jim (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
07ac837c886b356dc96e83cf82fb348eb56406d9jim * +-------------------------------------------------------+
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim * | Types and Structures
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim * +-------------------------------------------------------+
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jimtypedef struct {
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim const char *dbmtype; /* dbm type for dbm map data files */
07ac837c886b356dc96e83cf82fb348eb56406d9jim const char *checkfile; /* filename to check for map existence */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim const char *checkfile2; /* as above, NULL if only one file */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim const char *cachename; /* for cached maps (txt/rnd/dbm) */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim apr_file_t *fpin; /* in file pointer for program maps */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim apr_file_t *fpout; /* out file pointer for program maps */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim apr_file_t *fperr; /* err file pointer for program maps */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim char *(*func)(request_rec *, /* function pointer for internal maps */
188befd3a49e3a126bd801d7dc5a7f6e63ad4332mturk const char *dbdq; /* SQL SELECT statement for rewritemap */
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem/* special pattern types for RewriteCond */
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluemtypedef enum {
f921cd430a2ea23fcaedfdfc7439163f63c8472arpluemtypedef struct {
f921cd430a2ea23fcaedfdfc7439163f63c8472arpluem/* single linked list for env vars and cookies */
bf3f3b52289ee37f40842fc836ff92dd202742afpquernatypedef struct data_item {
4415d997ac73262e513c0a571bd5be4f609040bawrowetypedef struct {
65efbf0826de766a90d745cc44427bfa4e2447b6mturk apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
4415d997ac73262e513c0a571bd5be4f609040bawrowe ap_regex_t *regexp; /* the RegExp pattern compilation */
1febae173a82bc2a71c3c0ba4105cf674000791bjim char *forced_mimetype; /* forced MIME type of substitution */
1febae173a82bc2a71c3c0ba4105cf674000791bjim char *forced_handler; /* forced content handler of subst. */
eee895b02dd7867622afd0a8a94f2efc7de9c618wrowe int forced_responsecode; /* forced HTTP response status */
4415d997ac73262e513c0a571bd5be4f609040bawrowetypedef struct {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk const char *rewritelogfile; /* the RewriteLog filename */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk apr_file_t *rewritelogfp; /* the RewriteLog open filepointer */
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim int rewriteloglevel; /* the RewriteLog level of verbosity */
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim apr_array_header_t *rewriterules; /* the RewriteRule entries */
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim server_rec *server; /* the corresponding server indicator */
c3dc78855363fa6e8ecfc2bb8e2927efcd31d31djfcleretypedef struct {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk apr_array_header_t *rewriterules; /* the RewriteRule entries */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk const char *baseurl; /* the base-URL where it applies */
4415d997ac73262e513c0a571bd5be4f609040bawrowe/* the (per-child) cache structures.
4415d997ac73262e513c0a571bd5be4f609040bawrowetypedef struct cache {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk/* cached maps contain an mtime for the whole map and live in a subpool
4415d997ac73262e513c0a571bd5be4f609040bawrowe * of the cachep->pool. That makes it easy to forget them if necessary.
eee895b02dd7867622afd0a8a94f2efc7de9c618wrowetypedef struct {
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk/* the regex structure for the
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk * substitution of backreferences
00b70ae978854b5eb51722cbeda99c9067b5faf2mturktypedef struct backrefinfo {
361051c5d96d51ff2f707777b8f629a56799ef02jorton/* single linked list used for
361051c5d96d51ff2f707777b8f629a56799ef02jorton * variable expansion
c332befc1519a1016d8de07608f0b859e6fab580jimtypedef struct result_list {
9e3209bc06ddf32f23e4b254faa45914bc323cc9jim const char *string;
ff4ec92f5bb8d43b3ba1979ccda94f07961bf323jim/* context structure for variable lookup and expansion
ff4ec92f5bb8d43b3ba1979ccda94f07961bf323jimtypedef struct {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim const char *uri;
ff4ec92f5bb8d43b3ba1979ccda94f07961bf323jim const char *vary_this;
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim const char *vary;
d46dfdce9351f52a971777948d9b02f8fc668ff8niq * +-------------------------------------------------------+
9f22e9ea026e74271ddced44f6d54fa846ddc9bcwrowe * | static module data
d46dfdce9351f52a971777948d9b02f8fc668ff8niq * +-------------------------------------------------------+
d46dfdce9351f52a971777948d9b02f8fc668ff8niq/* the global module structure */
3a49a6c98ef80c71830e66e7f8f46083001b494ctrawick/* rewritemap int: handler function registry */
b92a868b537899a51efd8c200c396fa51c63839dtrawick/* the cache */
d46dfdce9351f52a971777948d9b02f8fc668ff8niq/* whether proxy module is available or not */
a652b68dea502131f70084ead7981d5fc754cd34jim/* whether random seed can be reaped */
a652b68dea502131f70084ead7981d5fc754cd34jimstatic const char *lockname;
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jimstatic apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
a652b68dea502131f70084ead7981d5fc754cd34jim/* Optional functions imported from mod_ssl when loaded: */
a652b68dea502131f70084ead7981d5fc754cd34jimstatic APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL;
a652b68dea502131f70084ead7981d5fc754cd34jimstatic APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL;
a652b68dea502131f70084ead7981d5fc754cd34jim * +-------------------------------------------------------+
a652b68dea502131f70084ead7981d5fc754cd34jim * | rewriting logfile support
65efbf0826de766a90d745cc44427bfa4e2447b6mturk * +-------------------------------------------------------+
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard apr_strftime(tstr, &len, sizeof(tstr), "[%d/%b/%Y:%H:%M:%S ", &t);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard apr_snprintf(tstr+len, sizeof(tstr)-len, "%c%.2d%.2d]",
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic int open_rewritelog(server_rec *s, apr_pool_t *p)
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard const char *fname;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard conf = ap_get_module_config(s->module_config, &rewrite_module);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* - no logfile configured
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * - logfilename empty
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * - virtual log shared w/ main server
00211b036b78699ace57a6d800a52e6c2d57652fnd if (!conf->rewritelogfile || !*conf->rewritelogfile || conf->rewritelogfp) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding fname = ap_server_root_relative(p, conf->rewritelogfile+1);
50b2d068ddf98cf75622a0020cd143d379d1b235jfclere ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
50b2d068ddf98cf75622a0020cd143d379d1b235jfclere "mod_rewrite: Invalid RewriteLog "
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard "mod_rewrite: could not open reliable pipe "
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding fname = ap_server_root_relative(p, conf->rewritelogfile);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mod_rewrite: Invalid RewriteLog "
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if ((rc = apr_file_open(&conf->rewritelogfp, fname,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mod_rewrite: could not open RewriteLog "
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic void do_rewritelog(request_rec *r, int level, char *perdir,
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard const char *fmt, ...)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding conf = ap_get_module_config(r->server->module_config, &rewrite_module);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard if (!conf->rewritelogfp || level > conf->rewriteloglevel) {
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard rhost = ap_get_remote_host(r->connection, r->per_dir_config,
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq logline = apr_psprintf(r->pool, "%s %s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] "
188befd3a49e3a126bd801d7dc5a7f6e63ad4332mturk (void *)(r->server),
9df9016759ec9327e256d7fff1af56ddfadb721cniq (void *)r,
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq "apr_global_mutex_lock(rewrite_log_lock) failed");
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* XXX: Maybe this should be fatal? */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq "apr_global_mutex_unlock(rewrite_log_lock) failed");
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* XXX: Maybe this should be fatal? */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq#endif /* !REWRITELOG_DISABLED */
997023faa943892aae20d092044aa983c2936982niq * +-------------------------------------------------------+
997023faa943892aae20d092044aa983c2936982niq * | URI and path functions
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * +-------------------------------------------------------+
df419be6d7d4b68823efa05722375552af49c2b6minfrin/* return number of chars of the scheme (incl. '://')
df419be6d7d4b68823efa05722375552af49c2b6minfrin * if the URI is absolute (includes a scheme etc.)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * otherwise 0.
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * NOTE: If you add new schemes here, please have a
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * look at escape_absolute_uri and splitout_queryargs.
dcda744296f197717c5105fd197e94ceba7880d7jim * Not every scheme takes query strings and some schemes
28fe44817329b1183f64e878c258962f90423a8dniq * may be handled in a special way.
28fe44817329b1183f64e878c258962f90423a8dniq * XXX: we may consider a scheme registry, perhaps with
df419be6d7d4b68823efa05722375552af49c2b6minfrin * appropriate escape callbacks to allow other modules
df419be6d7d4b68823efa05722375552af49c2b6minfrin * to extend mod_rewrite at runtime.
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* fast exit */
df419be6d7d4b68823efa05722375552af49c2b6minfrin switch (*uri++) {
df419be6d7d4b68823efa05722375552af49c2b6minfrin if (!strncasecmp(uri, "alancer://", 10)) { /* balancer:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrin if (!strncasecmp(uri, "opher://", 8)) { /* gopher:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrin else if (!strncasecmp(uri, "ttps://", 7)) { /* https:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrin else if (!strncasecmp(uri, "ntp://", 6)) { /* nntp:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrin * escape absolute uri, which may or may not be path oriented.
df419be6d7d4b68823efa05722375552af49c2b6minfrin * So let's handle them differently.
df419be6d7d4b68823efa05722375552af49c2b6minfrinstatic char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* be safe.
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * NULL should indicate elsewhere, that something's wrong
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* scheme with authority part? */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* skip host part */
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* nothing after the hostpart. ready! */
28fe44817329b1183f64e878c258962f90423a8dniq /* remember the hostname stuff */
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* special thing for ldap.
df419be6d7d4b68823efa05722375552af49c2b6minfrin * The parts are separated by question marks. From RFC 2255:
df419be6d7d4b68823efa05722375552af49c2b6minfrin * ldapurl = scheme "://" [hostport] ["/"
df419be6d7d4b68823efa05722375552af49c2b6minfrin * [dn ["?" [attributes] ["?" [scope]
df419be6d7d4b68823efa05722375552af49c2b6minfrin * ["?" [filter] ["?" extensions]]]]]]
28fe44817329b1183f64e878c258962f90423a8dniq int c = 0;
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe /* Nothing special here. Apply normal escaping. */
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * split out a QUERY_STRING part from
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * the current URI string
e8f95a682820a599fe41b22977010636be5c2717jimstatic void splitout_queryargs(request_rec *r, int qsappend)
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe /* don't touch, unless it's an http or mailto URL.
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * See RFC 1738 and RFC 2368.
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe r->args = NULL; /* forget the query that's still flying around */
8632261c895a84c88ae6ade6ea4c62b27bd22b3ebrianp if (q != NULL) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe *q++ = '\0';
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe r->args = apr_pstrcat(r->pool, q, "&", r->args, NULL);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * strip 'http[s]://ourhost/' from URI
68ce856106f153813339db8670f6cd0ab8dea484minfrin unsigned short port;
68ce856106f153813339db8670f6cd0ab8dea484minfrin scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* cut the hostname and port out of the URI */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding cp = host = scratch + l + 3; /* 3 == strlen("://") */
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm /* now check whether we could reduce it to a local path... */
b999f6ba2a266bf9a92687f31bb7e76021ac5891ianh rewritelog((r, 3, NULL, "reduce %s -> %s", r->filename, url));
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * add 'http[s]://ourhost[:ourport]/' to URI
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * if URI is still not fully qualified
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s",
35c9e4d2c0a6465746a98958ef756114834461e6minfrin * stat() only the first segment of a path
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic int prefix_stat(const char *path, apr_pool_t *pool)
35c9e4d2c0a6465746a98958ef756114834461e6minfrin const char *root;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard const char *slash;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* let's recognize slashes only, the mod_rewrite semantics are opaque
35c9e4d2c0a6465746a98958ef756114834461e6minfrin if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) {
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
d1b3d9a6f29078146ee970791123a8720bf38c39wrowestatic char *subst_prefix_path(request_rec *r, char *input, char *match,
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe const char *subst)
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe if (!strncmp(input, match, len) && input[len++] == '/') {
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe rewritelog((r, 5, NULL, "strip matching prefix: %s -> %s", input,
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe output = apr_palloc(r->pool, outlen + 1); /* don't forget the \0 */
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe rewritelog((r, 4, NULL, "add subst prefix: %s -> %s", input+len,
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk /* prefix didn't match */
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk * +-------------------------------------------------------+
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk * | caching support
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk * +-------------------------------------------------------+
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturkstatic void set_cache_value(const char *name, apr_time_t t, char *key,
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
6a4043fd04bbd44ca0d084c096318df035abc46djfclere /* Now we should have a valid map->entries hash, where we
14c8c18e6caab1bdeb0f26b2b031e000fef58ef9jim * can store our value.
6a4043fd04bbd44ca0d084c096318df035abc46djfclere * We need to copy the key and the value into OUR pool,
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim * so that we don't leave it during the r->pool cleanup.
00b70ae978854b5eb51722cbeda99c9067b5faf2mturkstatic char *get_cache_value(const char *name, apr_time_t t, char *key,
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem /* if this map is outdated, forget it. */
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING);
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem /* copy the cached value into the supplied pool,
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem * where it belongs (r->pool usually)
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem if (apr_pool_create(&cachep->pool, p) != APR_SUCCESS) {
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk (void)apr_thread_mutex_create(&(cachep->lock), APR_THREAD_MUTEX_DEFAULT, p);
a42b70fa75429d73ef00d6ae212676f8a652f51cpquerna * +-------------------------------------------------------+
a8523e2451f03f4a30030b7bda643a23a75d91demturk * | Map Functions
a8523e2451f03f4a30030b7bda643a23a75d91demturk * +-------------------------------------------------------+
a8523e2451f03f4a30030b7bda643a23a75d91demturk * General Note: key is already a fresh string, created (expanded) just
a8523e2451f03f4a30030b7bda643a23a75d91demturk * for the purpose to be passed in here. So one can modify key itself.
12d1342bd29aa7b15080f28cd8ed65d33ce00328rpluemstatic char *rewrite_mapfunc_toupper(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturk for (p = key; *p; ++p) {
12d1342bd29aa7b15080f28cd8ed65d33ce00328rpluemstatic char *rewrite_mapfunc_tolower(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturkstatic char *rewrite_mapfunc_escape(request_rec *r, char *key)
e8f95a682820a599fe41b22977010636be5c2717jimstatic char *rewrite_mapfunc_unescape(request_rec *r, char *key)
6bdb2c094666367615890147775bb18761216c8dminfrinstatic char *select_random_value_part(request_rec *r, char *value)
0280926d9c3e5deb48961117a60a817d6905dd3dniq char *p = value;
c1c0628ca9788908a5fc7502d04a89c348b75ee6wrowe unsigned n = 1;
c1c0628ca9788908a5fc7502d04a89c348b75ee6wrowe /* count number of distinct values */
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin if (n > 1) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* initialize random generator
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * XXX: Probably this should be wrapped into a thread mutex,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * shouldn't it? Is it worth the effort?
9b4c60b689b8a3f2d48d19c55d857b276d405f85wrowe /* select a random subvalue */
9b4c60b689b8a3f2d48d19c55d857b276d405f85wrowe n = (int)(((double)(rand() % RAND_MAX) / RAND_MAX) * n + 1);
9b4c60b689b8a3f2d48d19c55d857b276d405f85wrowe /* extract it from the whole string */
9b4c60b689b8a3f2d48d19c55d857b276d405f85wrowe while (--n && (value = ap_strchr(value, '|')) != NULL) {
d993198d8a18c11aa4e80bc647587df10e663f88jim *p = '\0';
af952917c05e56874069e1e5f64e6473bb478b68minfrin/* child process code */
af952917c05e56874069e1e5f64e6473bb478b68minfrinstatic void rewrite_child_errfn(apr_pool_t *p, apr_status_t err,
af952917c05e56874069e1e5f64e6473bb478b68minfrin const char *desc)
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, "%s", desc);
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jimstatic apr_status_t rewritemap_program_child(apr_pool_t *p,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if ( APR_SUCCESS == (rc=apr_procattr_create(&procattr, p))
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding && APR_SUCCESS == (rc=apr_procattr_io_set(procattr, APR_FULL_BLOCK,
af952917c05e56874069e1e5f64e6473bb478b68minfrin && APR_SUCCESS == (rc=apr_procattr_dir_set(procattr,
af952917c05e56874069e1e5f64e6473bb478b68minfrin && APR_SUCCESS == (rc=apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
af952917c05e56874069e1e5f64e6473bb478b68minfrin && APR_SUCCESS == (rc=apr_procattr_child_errfn_set(procattr,
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL,
609ecc54ccbd06a9286e1aaf891f80f62450b1aamturk apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
35630e8756729a29273ef1a5c879b90df3594d66rjungstatic apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p)
b2b9b7f0644773b50aee41956a841ac884086250niq conf = ap_get_module_config(s->module_config, &rewrite_module);
af952917c05e56874069e1e5f64e6473bb478b68minfrin /* If the engine isn't turned on,
af952917c05e56874069e1e5f64e6473bb478b68minfrin * don't even try to do anything.
b2b9b7f0644773b50aee41956a841ac884086250niq for (hi = apr_hash_first(p, conf->rewritemaps); hi; hi = apr_hash_next(hi)){
b2b9b7f0644773b50aee41956a841ac884086250niq if (!(map->argv[0]) || !*(map->argv[0]) || map->fpin || map->fpout) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe if (!lock_warning_issued && (!lockname || !*lockname)) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe "mod_rewrite: Running external rewrite maps "
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe "without defining a RewriteLock is DANGEROUS!");
f86b4df17588d404f3da535a8054f43b0642f92aniq rc = rewritemap_program_child(p, map->argv[0], map->argv,
f86b4df17588d404f3da535a8054f43b0642f92aniq if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) {
3709b26f3370ae89c5324a3c03fab56a93b09ecdsf "mod_rewrite: could not start RewriteMap "
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * +-------------------------------------------------------+
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * | Lookup functions
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * +-------------------------------------------------------+
f86b4df17588d404f3da535a8054f43b0642f92aniqstatic char *lookup_map_txtfile(request_rec *r, const char *file, char *key)
0b4d5285b176f443fd6c0fb07bef76f6b5eb81c7niq char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
f86b4df17588d404f3da535a8054f43b0642f92aniq if (apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
40b22d3b20454959fe51fdc89907908d77701078minfrin while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
40b22d3b20454959fe51fdc89907908d77701078minfrin char *p, *c;
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* ignore comments and lines starting with whitespaces */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding while (c < keylast && *p == *c && !apr_isspace(*p)) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* key doesn't match - ignore. */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* jump to the value */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding while (*p && apr_isspace(*p)) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* no value? ignore */
9379749d811388a7d0e3410940ddd6743a33d330jim /* extract the value and return. */
3c59b18ce62f97468aaa5951d4e21a5478ef36ecminfrin while (*p && !apr_isspace(*p)) {
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic char *lookup_map_dbmfile(request_rec *r, const char *file,
9379749d811388a7d0e3410940ddd6743a33d330jim if (apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY, APR_OS_DEFAULT,
24efed0910118b762a4eb84830875d4714b8d315ianh if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize);
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic char *lookup_map_dbd(request_rec *r, char *key, const char *label)
9379749d811388a7d0e3410940ddd6743a33d330jim const char *errmsg;
9379749d811388a7d0e3410940ddd6743a33d330jim int n = 0;
9379749d811388a7d0e3410940ddd6743a33d330jim stmt = apr_hash_get(db->prepared, label, APR_HASH_KEY_STRING);
9379749d811388a7d0e3410940ddd6743a33d330jim rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res,
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim if (rv != 0) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding errmsg = apr_dbd_error(db->driver, db->handle, rv);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "rewritemap: error %s querying for %s", errmsg, key);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding while (rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1), rv == 0) {
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* randomise crudely amongst multiple results */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe switch (n) {
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim /* what's a fair rewritelog level for this? */
dcda744296f197717c5105fd197e94ceba7880d7jim rewritelog((r, 3, NULL, "Multiple values found for %s", key));
28fe44817329b1183f64e878c258962f90423a8dniqstatic char *lookup_map_program(request_rec *r, apr_file_t *fpin,
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem /* when `RewriteEngine off' was used in the per-server
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim * context then the rewritemap-programs were not spawned.
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem * In this case using such a map (usually in per-dir context)
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem * is useless because it is not available.
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem * newlines in the key leave bytes in the pipe and cause
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim * bad things to happen (next map lookup will use the chars
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem * after the \n instead of the new key etc etc - in other words,
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem * the Rewritemap falls out of sync with the requests).
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) {
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* take the lock */
df419be6d7d4b68823efa05722375552af49c2b6minfrin rv = apr_global_mutex_lock(rewrite_mapr_lock_acquire);
df419be6d7d4b68823efa05722375552af49c2b6minfrin "apr_global_mutex_lock(rewrite_mapr_lock_acquire) "
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* write out the request key */
dcda744296f197717c5105fd197e94ceba7880d7jim /* read in the response value */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* remove eol from the buffer */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* only partial (invalid) eol sequence -> reset the counter */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe else if (eolc) {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim /* catch binary mode, e.g. on Win32 */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe else if (c == '\n') {
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* well, if there wasn't a newline yet, we need to read further */
06cdc8d205f0982074ab9b032d0b6d74c58f54carjung curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist));
06cdc8d205f0982074ab9b032d0b6d74c58f54carjung else if (i) {
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim } while (1);
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim /* concat the stuff */
997023faa943892aae20d092044aa983c2936982niq p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */
997023faa943892aae20d092044aa983c2936982niq /* give the lock back */
997023faa943892aae20d092044aa983c2936982niq rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire);
ea64665b1de60d55641104e018f696c4a310d2f1jim "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) "
997023faa943892aae20d092044aa983c2936982niq "failed");
ea64665b1de60d55641104e018f696c4a310d2f1jim /* catch the "failed" case */
997023faa943892aae20d092044aa983c2936982niq * generic map lookup
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic char *lookup_map(request_rec *r, char *name, char *key)
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq /* get map configuration */
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq conf = ap_get_module_config(r->server->module_config, &rewrite_module);
997023faa943892aae20d092044aa983c2936982niq s = apr_hash_get(conf->rewritemaps, name, APR_HASH_KEY_STRING);
a812b025d139f465a31c76fc02ed162ed5271b03nd /* map doesn't exist */
f86b4df17588d404f3da535a8054f43b0642f92aniq switch (s->type) {
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq * Text file map (perhaps random)
997023faa943892aae20d092044aa983c2936982niq rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mod_rewrite: can't access text RewriteMap file %s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "can't open RewriteMap file, see error log"));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = get_cache_value(s->cachename, st.mtime, key, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "cache lookup FAILED, forcing new map lookup"));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[txt] key=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL,"map lookup OK: map=%s[txt] key=%s -> val=%s",
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard set_cache_value(s->cachename, st.mtime, key, value);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard rewritelog((r,5,NULL,"cache lookup OK: map=%s[txt] key=%s -> val=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "randomly chosen the subvalue `%s'",value));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * DBM file map
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mod_rewrite: can't access DBM RewriteMap file %s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rv = apr_stat(&st2, s->checkfile2, APR_FINFO_MIN, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mod_rewrite: can't access DBM RewriteMap "
b9b3cd7661ec354e0eee71116faa1e4afdf3fcdcmturk "can't open DBM RewriteMap file, see error log"));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = get_cache_value(s->cachename, st.mtime, key, r->pool);
b9b3cd7661ec354e0eee71116faa1e4afdf3fcdcmturk "cache lookup FAILED, forcing new map lookup"));
b9b3cd7661ec354e0eee71116faa1e4afdf3fcdcmturk value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s",
407cde44becba3694e7c3d81ac99b5d86f4b03a9rbb rewritelog((r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> "
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard set_cache_value(s->cachename, st.mtime, key, value);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * SQL map without cache
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * SQL map with cache
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = get_cache_value(s->cachename, 0, key, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "cache lookup FAILED, forcing new map lookup"));
40b22d3b20454959fe51fdc89907908d77701078minfrin rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
ff1234e45aca1b8171d711ecb87f58b9d9100a99ianh rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
93cf7fc650197b941ae31a7c7e51e901b129e954igalic rewritelog((r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s",
3709b26f3370ae89c5324a3c03fab56a93b09ecdsf * Program file map
fd3fa792f04fc9c4e8f5f83dceb0fc34e71f8570ianh rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * Internal Map
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin * lookup a HTTP header and set VARY note
35c9e4d2c0a6465746a98958ef756114834461e6minfrinstatic const char *lookup_header(const char *name, rewrite_ctx *ctx)
35c9e4d2c0a6465746a98958ef756114834461e6minfrin const char *val = apr_table_get(ctx->r->headers_in, name);
4224d5789080ea5586d49420da1e1996f5653bb5ianh * lookahead helper function
4224d5789080ea5586d49420da1e1996f5653bb5ianh * Determine the correct URI path in perdir context
4224d5789080ea5586d49420da1e1996f5653bb5ianh conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * generic variable lookup
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic char *lookup_variable(char *var, rewrite_ctx *ctx)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const char *result;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* fast exit */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* fast tests for variable length variables (sic) first */
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim else if (var[4] && !strncasecmp(var, "SSL", 3) && rewrite_ssl_lookup) {
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim result = rewrite_ssl_lookup(r->pool, r->server, r->connection, r,
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim const char *path;
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe return (char *)result;
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe /* sigh, the user wants a file based subrequest, but
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe * we can't do one, since we don't know what the file
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe * path is! In this case behave like LA-U.
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere return (char *)result;
433dc2d5dae74ed067db6175010ff973d02511e9jerenkrantz /* well, do it the hard way */
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe /* can't do this above, because of the getenv call */
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe for (p = var; *p; ++p) {
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim return (char *)result;
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe int flag = rewrite_is_https && rewrite_is_https(r->connection);
fef95ce9cdb638443693c21f181dd2494b467469mturk return apr_psprintf(r->pool, "%04d", tm.tm_year+1900);
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe return apr_psprintf(r->pool, "%u", ap_get_server_port(r));
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe result = ap_get_remote_host(r->connection,r->per_dir_config,
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe return apr_itoa(r->pool, r->connection->remote_addr->port);
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim result = r->filename; /* same as request_filename (16) */
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk result = r->filename; /* same as script_filename (15) */
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * +-------------------------------------------------------+
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * | Expansion functions
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * +-------------------------------------------------------+
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * Bracketed expression handling
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * s points after the opening bracket
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb else if (*s == LEFT_CURLY) {
e99dfd55d29a7b4209b814efc7270d0b74ccee74niqstatic APR_INLINE char *find_char_in_curlies(char *s, int c)
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb else if (*s == LEFT_CURLY) {
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb/* perform all the expansions on the input string
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb * putting the result into a new string
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb * for security reasons this expansion must be performed in a
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb * single pass, otherwise an attacker can arrange for the result
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb * of an earlier expansion to include expansion specifiers that
40b22d3b20454959fe51fdc89907908d77701078minfrin * are interpreted by a later expansion, producing results that
4ecd546edd89824908c2a9ad2e07339d89368f9cmartin * were not intended by the administrator.
4ecd546edd89824908c2a9ad2e07339d89368f9cmartinstatic char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim unsigned spc = 0;
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe char *p, *c;
b2b9b7f0644773b50aee41956a841ac884086250niq /* fast exit */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* well, actually something to do */
e8f95a682820a599fe41b22977010636be5c2717jim /* loop for specials */
1723d9ccdd3b647f5b7bae44cab9ab3eca7a4874dougm /* prepare next entry */
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm /* escaped character */
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm if (*p == '\\') {
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm if (!p[1]) {
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk /* variable or map lookup */
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk /* variable lookup */
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk else if (*p == '%') {
e1d00c81dc34e2c644cc936b9dea75ec9ccfca2adougm p = lookup_variable(apr_pstrmemdup(pool, p+2, endp-p-2), ctx);
e1d00c81dc34e2c644cc936b9dea75ec9ccfca2adougm /* map lookup */
e1d00c81dc34e2c644cc936b9dea75ec9ccfca2adougm else { /* *p == '$' */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * To make rewrite maps useful, the lookup key and
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * default values must be expanded, so we make
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * recursive calls to do the work. For security
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * reasons we must never expand a string that includes
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * verbatim data from the network. The recursion here
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * isn't a problem because the result of expansion is
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * only passed to lookup_map() so it cannot be
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * re-expanded, only re-looked-up. Another way of
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere * looking at it is that the recursion is entirely
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * driven by the syntax of the nested curly brackets.
cd42921af450e87bc797da0ff899348b75732645jfclere /* reuse of key variable as result */
c64fb33e0c4634fd352c4a6c143cd1a087c09b13nd key = lookup_map(ctx->r, map, do_expand(key, ctx, entry));
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere /* backreference */
67d13599fd4a5f73a2d3fc2b566a9aad60b1c892mturk backrefinfo *bri = (*p == '$') ? &ctx->briRR : &ctx->briRC;
67d13599fd4a5f73a2d3fc2b566a9aad60b1c892mturk /* see ap_pregsub() in server/util.c */
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere span = bri->regmatch[n].rm_eo - bri->regmatch[n].rm_so;
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere if (entry && (entry->flags & RULEFLAG_ESCAPEBACKREF)) {
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere /* escape the backreference */
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere tmp = apr_pstrndup(pool, bri->source + bri->regmatch[n].rm_so, span);
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'",
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe current->string = bri->source + bri->regmatch[n].rm_so;
67d13599fd4a5f73a2d3fc2b566a9aad60b1c892mturk /* not for us, just copy it */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe /* check the remainder */
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim /* assemble result */
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim c = p = apr_palloc(pool, outlen + 1); /* don't forget the \0 */
748859d4eeb90f2a64b8b8318bc45c3a61dcd968jim ap_assert(c+result->len <= p+outlen); /* XXX: can be removed after
748859d4eeb90f2a64b8b8318bc45c3a61dcd968jim * extensive testing and
748859d4eeb90f2a64b8b8318bc45c3a61dcd968jim * perform all the expansions on the environment variables
a865e849a3b00dc8524ecdda09a1699452876219mturkstatic void do_expand_env(data_item *env, rewrite_ctx *ctx)
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim rewritelog((ctx->r, 5, NULL, "setting env variable '%s' to '%s'",
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * perform all the expansions on the cookies
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * TODO: use cached time similar to how logging does it
fdacf528f0125e0165de42b7783b1dcbf961a189chuck notename = apr_pstrcat(rmain->pool, var, "_rewrite", NULL);
e1d00c81dc34e2c644cc936b9dea75ec9ccfca2adougm path = expires ? apr_strtok(NULL, ":", &tok_cntx) : NULL;
36ef8f77bffe75d1aa327882be1b5bdbe2ff567asf "%.2d:%.2d:%.2d GMT",
bda7a7d57377f45932c237d5aba00b189d85c2a9ianh apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie);
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin apr_pool_userdata_set("set", notename, NULL, rmain->pool);
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie));
e8f95a682820a599fe41b22977010636be5c2717jim rewritelog((rmain, 5, NULL, "skipping already set cookie '%s'",
d75bc22ab2702fa770f6935f07107efff16a76f0wrowestatic void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx)
d75bc22ab2702fa770f6935f07107efff16a76f0wrowe add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL));
67f62b7a48ff9eb8d9f31898dceaf9f89280a723dougm * Expand tilde-paths (/~user) through Unix /etc/passwd
b9b3cd7661ec354e0eee71116faa1e4afdf3fcdcmturk * database information (or other OS-specific database)
bda7a7d57377f45932c237d5aba00b189d85c2a9ianhstatic char *expand_tildepaths(request_rec *r, char *uri)
bda7a7d57377f45932c237d5aba00b189d85c2a9ianh 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 }