mod_rewrite.c revision 6f2fbf354b34981f398cf0313aa44702ea2a7066
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
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* XXX: Do we really need these headers? */
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowestatic void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowestatic const char* really_last_key = "rewrite_really_last";
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe * in order to improve performance on running production systems, you
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe * may strip all rewritelog code entirely from mod_rewrite by using the
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk * -DREWRITELOG_DISABLED compiler option.
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe * responsible for answering all the mod_rewrite questions out there.
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowe/* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
4c67ef499845a08771e81254ce6eb2324a160bc7wrowe#else /* !REWRITELOG_DISABLED */
4c67ef499845a08771e81254ce6eb2324a160bc7wrowe#endif /* REWRITELOG_DISABLED */
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowe/* remembered mime-type for [T=...] */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler"
1dac466bcc84f8ebf410016dcf2a4cd4312e8611wrowe#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* return code of the rewrite rule
b842b65e0618c5535233b197f03dc917d184adb3jim * the result may be escaped - or not
f73a8fabbdc4ec11c8b475e9f48539de0c4f82ebmturk/* max cookie size in rfc 2109 */
e8f95a682820a599fe41b22977010636be5c2717jim/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
f73a8fabbdc4ec11c8b475e9f48539de0c4f82ebmturk/* max line length (incl.\n) in text rewrite maps */
7ce17736e4802a923eed275812647a7c3e9ad76ejim/* buffer length for prg rewrite maps */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* for better readbility */
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim * expansion result items on the stack to save some cycles
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim * (5 == about 2 variables like "foo%{var}bar%{var}baz")
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim * check that a subrequest won't cause infinite recursion
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim * either not in a subrequest, or in a subrequest
5d392744e2077f71f34ce098ab49d2c0ddcf4ea3jim * and URIs aren't NULL and sub/main URIs differ
188befd3a49e3a126bd801d7dc5a7f6e63ad4332mturk (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem * +-------------------------------------------------------+
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem * | Types and Structures
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim * +-------------------------------------------------------+
f185ce14f5dd540ae54659f764989c017c619485jimtypedef struct {
f185ce14f5dd540ae54659f764989c017c619485jim const char *dbmtype; /* dbm type for dbm map data files */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim const char *checkfile; /* filename to check for map existence */
f185ce14f5dd540ae54659f764989c017c619485jim const char *cachename; /* for cached maps (txt/rnd/dbm) */
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem apr_file_t *fpin; /* in file pointer for program maps */
f921cd430a2ea23fcaedfdfc7439163f63c8472arpluem apr_file_t *fpout; /* out file pointer for program maps */
f921cd430a2ea23fcaedfdfc7439163f63c8472arpluem apr_file_t *fperr; /* err file pointer for program maps */
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem char *(*func)(request_rec *, /* function pointer for internal maps */
90f18725dbb9bdfba94da22aa60f94dfb759a8ferpluem const char *dbdq; /* SQL SELECT statement for rewritemap */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim const char *checkfile2; /* filename to check for map existence
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim NULL if only one file */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjim/* special pattern types for RewriteCond */
2f1146e06a1bfa371573a3f3fb0379448e18aaedjimtypedef enum {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturktypedef struct {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim/* single linked list for env vars and cookies */
c3dc78855363fa6e8ecfc2bb8e2927efcd31d31djfcleretypedef struct data_item {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturktypedef struct {
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturk apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim ap_regex_t *regexp; /* the RegExp pattern compilation */
4415d997ac73262e513c0a571bd5be4f609040bawrowe char *forced_mimetype; /* forced MIME type of substitution */
4415d997ac73262e513c0a571bd5be4f609040bawrowe char *forced_handler; /* forced content handler of subst. */
4415d997ac73262e513c0a571bd5be4f609040bawrowe int forced_responsecode; /* forced HTTP response status */
01261c7d9578aadd1891f94c8ee03f32ba51db3dmturktypedef struct {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
4415d997ac73262e513c0a571bd5be4f609040bawrowe apr_array_header_t *rewriterules; /* the RewriteRule entries */
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk server_rec *server; /* the corresponding server indicator */
00b70ae978854b5eb51722cbeda99c9067b5faf2mturktypedef struct {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk apr_array_header_t *rewriterules; /* the RewriteRule entries */
c332befc1519a1016d8de07608f0b859e6fab580jim const char *baseurl; /* the base-URL where it applies */
5b439d3976ab9363288cf5132902fad230777523jim/* the (per-child) cache structures.
1febae173a82bc2a71c3c0ba4105cf674000791bjimtypedef struct cache {
ff4ec92f5bb8d43b3ba1979ccda94f07961bf323jim/* cached maps contain an mtime for the whole map and live in a subpool
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim * of the cachep->pool. That makes it easy to forget them if necessary.
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jimtypedef struct {
d46dfdce9351f52a971777948d9b02f8fc668ff8niq/* the regex structure for the
d46dfdce9351f52a971777948d9b02f8fc668ff8niq * substitution of backreferences
9f22e9ea026e74271ddced44f6d54fa846ddc9bcwrowetypedef struct backrefinfo {
d46dfdce9351f52a971777948d9b02f8fc668ff8niq/* single linked list used for
d46dfdce9351f52a971777948d9b02f8fc668ff8niq * variable expansion
d46dfdce9351f52a971777948d9b02f8fc668ff8niqtypedef struct result_list {
d46dfdce9351f52a971777948d9b02f8fc668ff8niq const char *string;
d46dfdce9351f52a971777948d9b02f8fc668ff8niq/* context structure for variable lookup and expansion
a652b68dea502131f70084ead7981d5fc754cd34jimtypedef struct {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim const char *uri;
a652b68dea502131f70084ead7981d5fc754cd34jim const char *vary_this;
a652b68dea502131f70084ead7981d5fc754cd34jim const char *vary;
a652b68dea502131f70084ead7981d5fc754cd34jim * +-------------------------------------------------------+
a652b68dea502131f70084ead7981d5fc754cd34jim * | static module data
a652b68dea502131f70084ead7981d5fc754cd34jim * +-------------------------------------------------------+
a652b68dea502131f70084ead7981d5fc754cd34jim/* the global module structure */
4415d997ac73262e513c0a571bd5be4f609040bawrowe/* rewritemap int: handler function registry */
38fd849bd99e2765ee633b6dc576b5f17acdc455wrowe/* the cache */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding/* whether proxy module is available or not */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding/* whether random seed can be reaped */
00211b036b78699ace57a6d800a52e6c2d57652fndstatic int rewrite_rand_init_done = 0;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard/* Optional functions imported from mod_ssl when loaded: */
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL;
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic char *escape_uri(apr_pool_t *p, const char *path);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * +-------------------------------------------------------+
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * | rewriting logfile support
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * +-------------------------------------------------------+
00211b036b78699ace57a6d800a52e6c2d57652fndstatic void do_rewritelog(request_rec *r, int level, char *perdir,
00211b036b78699ace57a6d800a52e6c2d57652fnd const char *fmt, ...)
00211b036b78699ace57a6d800a52e6c2d57652fndstatic void do_rewritelog(request_rec *r, int level, char *perdir,
00211b036b78699ace57a6d800a52e6c2d57652fnd const char *fmt, ...)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rhost = ap_get_remote_host(r->connection, r->per_dir_config,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding logline = apr_psprintf(r->pool, "%s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] "
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "%s%s%s%s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding (void *)(r->server),
16d38ac65d7e54cd44eeda7b23f84ee68b35094ewrowe AP_REWRITE_LOG((uintptr_t)r, level, r->main ? 0 : 1, (char *)ap_get_server_name(r), logline);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ap_log_rerror(APLOG_MARK, APLOG_DEBUG + level, 0, r, "%s", logline);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard#endif /* !REWRITELOG_DISABLED */
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * +-------------------------------------------------------+
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * | URI and path functions
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * +-------------------------------------------------------+
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard/* return number of chars of the scheme (incl. '://')
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * if the URI is absolute (includes a scheme etc.)
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * otherwise 0.
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * NOTE: If you add new schemes here, please have a
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * look at escape_absolute_uri and splitout_queryargs.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * Not every scheme takes query strings and some schemes
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * may be handled in a special way.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * XXX: we may consider a scheme registry, perhaps with
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * appropriate escape callbacks to allow other modules
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * to extend mod_rewrite at runtime.
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* fast exit */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq switch (*uri++) {
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq if (!strncasecmp(uri, "alancer://", 10)) { /* balancer:// */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq return 11;
997023faa943892aae20d092044aa983c2936982niq else if (!strncasecmp(uri, "ttps://", 7)) { /* https:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrin else if (!strncasecmp(uri, "ntp://", 6)) { /* nntp:// */
df419be6d7d4b68823efa05722375552af49c2b6minfrinstatic APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
df419be6d7d4b68823efa05722375552af49c2b6minfrin unsigned char *where)
df419be6d7d4b68823efa05722375552af49c2b6minfrin what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
df419be6d7d4b68823efa05722375552af49c2b6minfrin#endif /*APR_CHARSET_EBCDIC*/
df419be6d7d4b68823efa05722375552af49c2b6minfrin * Escapes a uri in a similar way as php's urlencode does.
df419be6d7d4b68823efa05722375552af49c2b6minfrin * Based on ap_os_escape_path in server/util.c
df419be6d7d4b68823efa05722375552af49c2b6minfrinstatic char *escape_uri(apr_pool_t *p, const char *path) {
df419be6d7d4b68823efa05722375552af49c2b6minfrin const unsigned char *s = (const unsigned char *)path;
df419be6d7d4b68823efa05722375552af49c2b6minfrin unsigned char *d = (unsigned char *)copy;
df419be6d7d4b68823efa05722375552af49c2b6minfrin unsigned c;
df419be6d7d4b68823efa05722375552af49c2b6minfrin while ((c = *s)) {
df419be6d7d4b68823efa05722375552af49c2b6minfrin else if (c == ' ') {
df419be6d7d4b68823efa05722375552af49c2b6minfrin *d++ = '+';
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)
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* be safe.
df419be6d7d4b68823efa05722375552af49c2b6minfrin * NULL should indicate elsewhere, that something's wrong
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* scheme with authority part? */
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* skip host part */
a812b025d139f465a31c76fc02ed162ed5271b03nd /* nothing after the hostpart. ready! */
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* remember the hostname stuff */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* special thing for ldap.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * The parts are separated by question marks. From RFC 2255:
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * ldapurl = scheme "://" [hostport] ["/"
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * [dn ["?" [attributes] ["?" [scope]
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * ["?" [filter] ["?" extensions]]]]]]
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe /* Nothing special here. Apply normal escaping. */
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * split out a QUERY_STRING part from
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard * the current URI string
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard)
e8f95a682820a599fe41b22977010636be5c2717jim /* 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 */
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe if (q != NULL) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe *q++ = '\0';
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe r->args = apr_pstrcat(r->pool, q, "&", r->args, NULL);
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * strip 'http[s]://ourhost/' from URI
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding unsigned short port;
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* cut the hostname and port out of the URI */
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* now check whether we could reduce it to a local path... */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 3, NULL, "reduce %s -> %s", r->filename, url));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * add 'http[s]://ourhost[:ourport]/' to URI
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * if URI is still not fully qualified
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s",
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin * stat() only the first segment of a path
35c9e4d2c0a6465746a98958ef756114834461e6minfrinstatic int prefix_stat(const char *path, apr_pool_t *pool)
d75bc22ab2702fa770f6935f07107efff16a76f0wrowe const char *root;
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk const char *slash;
45dac0729754e413ff7c673481b219e9ab1a11f1bnicholes rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* let's recognize slashes only, the mod_rewrite semantics are opaque
e2b2e15108eb7cb566b1d70ce4e479276d951de5minfrin if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) {
35c9e4d2c0a6465746a98958ef756114834461e6minfrin * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddardstatic char *subst_prefix_path(request_rec *r, char *input, char *match,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const char *subst)
7184de27ec1d62a83c41cdeac0953ca9fd661e8csf 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,
d1b3d9a6f29078146ee970791123a8720bf38c39wrowe /* prefix didn't match */
a8523e2451f03f4a30030b7bda643a23a75d91demturk * +-------------------------------------------------------+
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk * | caching support
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk * +-------------------------------------------------------+
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic 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);
e2458a81ee951feeff648c2ca5cad2c5a744d8e5mturk if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem /* Now we should have a valid map->entries hash, where we
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem * can store our value.
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem * We need to copy the key and the value into OUR pool,
8cdc2e8b6f46a3f239a70184e9f785fc25463487rpluem * 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,
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk /* 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) {
5fbd1e97905738791e7359ccbc9b02e913948d2erpluem (void)apr_thread_mutex_create(&(cachep->lock), APR_THREAD_MUTEX_DEFAULT, p);
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk * +-------------------------------------------------------+
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk * | Map Functions
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk * +-------------------------------------------------------+
00b70ae978854b5eb51722cbeda99c9067b5faf2mturk * General Note: key is already a fresh string, created (expanded) just
a42b70fa75429d73ef00d6ae212676f8a652f51cpquerna * for the purpose to be passed in here. So one can modify key itself.
a8523e2451f03f4a30030b7bda643a23a75d91demturkstatic char *rewrite_mapfunc_toupper(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturk for (p = key; *p; ++p) {
00b70ae978854b5eb51722cbeda99c9067b5faf2mturkstatic char *rewrite_mapfunc_tolower(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturkstatic char *rewrite_mapfunc_escape(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturkstatic char *rewrite_mapfunc_unescape(request_rec *r, char *key)
a8523e2451f03f4a30030b7bda643a23a75d91demturkstatic char *select_random_value_part(request_rec *r, char *value)
e8f95a682820a599fe41b22977010636be5c2717jim char *p = value;
67d13599fd4a5f73a2d3fc2b566a9aad60b1c892mturk unsigned n = 1;
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin /* count number of distinct values */
0280926d9c3e5deb48961117a60a817d6905dd3dniq if (n > 1) {
c1c0628ca9788908a5fc7502d04a89c348b75ee6wrowe /* initialize random generator
c1c0628ca9788908a5fc7502d04a89c348b75ee6wrowe * XXX: Probably this should be wrapped into a thread mutex,
d9efe39afed3db4bbdc32e40ddb67075c56e689djim * shouldn't it? Is it worth the effort?
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* select a random subvalue */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding n = (int)(((double)(rand() % RAND_MAX) / RAND_MAX) * n + 1);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* extract it from the whole string */
11c3b5180e1de6776035320b012a28bb146e7b46chuck while (--n && (value = ap_strchr(value, '|')) != NULL) {
fd61dbd57eb4ee63c3c647c178979ed817d13be3jim/* child process code */
d993198d8a18c11aa4e80bc647587df10e663f88jimstatic void rewrite_child_errfn(apr_pool_t *p, apr_status_t err,
fd61dbd57eb4ee63c3c647c178979ed817d13be3jim const char *desc)
fd61dbd57eb4ee63c3c647c178979ed817d13be3jim ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, "%s", desc);
af952917c05e56874069e1e5f64e6473bb478b68minfrinstatic apr_status_t rewritemap_program_child(apr_pool_t *p,
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin if ( APR_SUCCESS == (rc=apr_procattr_create(&procattr, p))
35c9e4d2c0a6465746a98958ef756114834461e6minfrin && APR_SUCCESS == (rc=apr_procattr_io_set(procattr, APR_FULL_BLOCK,
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim && APR_SUCCESS == (rc=apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim && APR_SUCCESS == (rc=apr_procattr_child_errfn_set(procattr,
b2b9b7f0644773b50aee41956a841ac884086250niq && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL,
af952917c05e56874069e1e5f64e6473bb478b68minfrin apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
af952917c05e56874069e1e5f64e6473bb478b68minfrinstatic apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p)
7ce40500bfead8781bab964eb6e01944acbf3915jim conf = ap_get_module_config(s->module_config, &rewrite_module);
7ce40500bfead8781bab964eb6e01944acbf3915jim /* If the engine isn't turned on,
af952917c05e56874069e1e5f64e6473bb478b68minfrin * don't even try to do anything.
35630e8756729a29273ef1a5c879b90df3594d66rjung 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) {
b2b9b7f0644773b50aee41956a841ac884086250niq rc = rewritemap_program_child(p, map->argv[0], map->argv,
b2b9b7f0644773b50aee41956a841ac884086250niq if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) {
b2b9b7f0644773b50aee41956a841ac884086250niq "mod_rewrite: could not start RewriteMap "
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * +-------------------------------------------------------+
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe * | Lookup functions
f86b4df17588d404f3da535a8054f43b0642f92aniq * +-------------------------------------------------------+
f86b4df17588d404f3da535a8054f43b0642f92aniqstatic char *lookup_map_txtfile(request_rec *r, const char *file, char *key)
40b22d3b20454959fe51fdc89907908d77701078minfrin if ((rv = apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe "mod_rewrite: can't open text RewriteMap file %s", file);
0b4d5285b176f443fd6c0fb07bef76f6b5eb81c7niq while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe char *p, *c;
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe /* ignore comments and lines starting with whitespaces */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq /* key doesn't match - ignore. */
40b22d3b20454959fe51fdc89907908d77701078minfrin /* jump to the value */
40b22d3b20454959fe51fdc89907908d77701078minfrin while (*p && apr_isspace(*p)) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe /* no value? ignore */
9379749d811388a7d0e3410940ddd6743a33d330jim /* extract the value and return. */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding while (*p && !apr_isspace(*p)) {
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbbstatic char *lookup_map_dbmfile(request_rec *r, const char *file,
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard if ((rv = apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize);
65efbf0826de766a90d745cc44427bfa4e2447b6mturkstatic char *lookup_map_dbd(request_rec *r, char *key, const char *label)
24efed0910118b762a4eb84830875d4714b8d315ianh const char *errmsg;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding stmt = apr_hash_get(db->prepared, label, APR_HASH_KEY_STRING);
9379749d811388a7d0e3410940ddd6743a33d330jim rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (rv != 0) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding errmsg = apr_dbd_error(db->driver, db->handle, rv);
9379749d811388a7d0e3410940ddd6743a33d330jim while (rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1), rv == 0) {
9379749d811388a7d0e3410940ddd6743a33d330jim /* randomise crudely amongst multiple results */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding errmsg = apr_dbd_error(db->driver, db->handle, rv);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "rewritemap: error %s looking up %s", errmsg, key);
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe switch (n) {
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim /* what's a fair rewritelog level for this? */
28fe44817329b1183f64e878c258962f90423a8dniq rewritelog((r, 3, NULL, "Multiple values found for %s", key));
b588214d6e6fe09abe709e83e894921fbc7e25c8covenerstatic char *lookup_map_program(request_rec *r, apr_file_t *fpin,
28fe44817329b1183f64e878c258962f90423a8dniq /* when `RewriteEngine off' was used in the per-server
28fe44817329b1183f64e878c258962f90423a8dniq * context then the rewritemap-programs were not spawned.
28fe44817329b1183f64e878c258962f90423a8dniq * In this case using such a map (usually in per-dir context)
28fe44817329b1183f64e878c258962f90423a8dniq * is useless because it is not available.
997023faa943892aae20d092044aa983c2936982niq * newlines in the key leave bytes in the pipe and cause
997023faa943892aae20d092044aa983c2936982niq * bad things to happen (next map lookup will use the chars
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe * after the \n instead of the new key etc etc - in other words,
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe * the Rewritemap falls out of sync with the requests).
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) {
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem /* take the lock */
c7eeb0a294d58c75aee6ed86f73c6e1e8cf600a3rpluem "apr_global_mutex_lock(rewrite_mapr_lock_acquire) "
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* write out the request key */
df419be6d7d4b68823efa05722375552af49c2b6minfrin /* read in the response value */
28fe44817329b1183f64e878c258962f90423a8dniq /* remove eol from the buffer */
dcda744296f197717c5105fd197e94ceba7880d7jim if (i < eolc) {
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* only partial (invalid) eol sequence -> reset the counter */
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq else if (eolc) {
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim /* catch binary mode, e.g. on Win32 */
4415d997ac73262e513c0a571bd5be4f609040bawrowe 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));
cb7cf74a315df272e2ec329ce2ef1d50b82b8384jim else if (i) {
06cdc8d205f0982074ab9b032d0b6d74c58f54carjung } while (1);
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* concat the stuff */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim *p = '\0';
997023faa943892aae20d092044aa983c2936982niq /* give the lock back */
f86b4df17588d404f3da535a8054f43b0642f92aniq rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire);
997023faa943892aae20d092044aa983c2936982niq "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) "
997023faa943892aae20d092044aa983c2936982niq "failed");
b588214d6e6fe09abe709e83e894921fbc7e25c8covener /* catch the "failed" case */
ea64665b1de60d55641104e018f696c4a310d2f1jim * generic map lookup
997023faa943892aae20d092044aa983c2936982niqstatic char *lookup_map(request_rec *r, char *name, char *key)
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard /* get map configuration */
997023faa943892aae20d092044aa983c2936982niq conf = ap_get_module_config(r->server->module_config, &rewrite_module);
997023faa943892aae20d092044aa983c2936982niq s = apr_hash_get(conf->rewritemaps, name, APR_HASH_KEY_STRING);
997023faa943892aae20d092044aa983c2936982niq /* map doesn't exist */
997023faa943892aae20d092044aa983c2936982niq switch (s->type) {
f86b4df17588d404f3da535a8054f43b0642f92aniq * Text file map (perhaps random)
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
a812b025d139f465a31c76fc02ed162ed5271b03nd "mod_rewrite: can't access text RewriteMap file %s",
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq value = get_cache_value(s->cachename, st.mtime, key, r->pool);
33cb45dc8c5106018b7c2f6ae42478b109423e0eniq "cache lookup FAILED, forcing new map lookup"));
997023faa943892aae20d092044aa983c2936982niq 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",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding set_cache_value(s->cachename, st.mtime, key, value);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding 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
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard "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 "
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding value = get_cache_value(s->cachename, st.mtime, key, r->pool);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "cache lookup FAILED, forcing new map lookup"));
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s",
65efbf0826de766a90d745cc44427bfa4e2447b6mturk rewritelog((r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> "
65efbf0826de766a90d745cc44427bfa4e2447b6mturk rewritelog((r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s",
b9b3cd7661ec354e0eee71116faa1e4afdf3fcdcmturk * SQL map without cache
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard 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"));
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 rewritelog((r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s",
ff1234e45aca1b8171d711ecb87f58b9d9100a99ianh * Program file map
40b22d3b20454959fe51fdc89907908d77701078minfrin value = lookup_map_program(r, s->fpin, s->fpout, key);
40b22d3b20454959fe51fdc89907908d77701078minfrin rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
93cf7fc650197b941ae31a7c7e51e901b129e954igalic rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
93cf7fc650197b941ae31a7c7e51e901b129e954igalic * Internal 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 * lookup a HTTP header and set VARY note
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic const char *lookup_header(const char *name, rewrite_ctx *ctx)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const char *val = apr_table_get(ctx->r->headers_in, name);
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin * lookahead helper function
afef080e47ef499a5cbceb7ad7fadbb3abca0b48minfrin * Determine the correct URI path in perdir context
35c9e4d2c0a6465746a98958ef756114834461e6minfrin conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module);
4224d5789080ea5586d49420da1e1996f5653bb5ianh * generic variable lookup
4224d5789080ea5586d49420da1e1996f5653bb5ianhstatic char *lookup_variable(char *var, rewrite_ctx *ctx)
4224d5789080ea5586d49420da1e1996f5653bb5ianh const char *result;
35630e8756729a29273ef1a5c879b90df3594d66rjung /* fast exit */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* fast tests for variable length variables (sic) first */
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard else if (var[4] && !strncasecmp(var, "SSL", 3) && rewrite_ssl_lookup) {
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard result = rewrite_ssl_lookup(r->pool, r->server, r->connection, r,
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim const char *path;
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim return (char *)result;
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe /* sigh, the user wants a file based subrequest, but
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * we can't do one, since we don't know what the file
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe * path is! In this case behave like LA-U.
7ce40500bfead8781bab964eb6e01944acbf3915jim result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
7ce40500bfead8781bab964eb6e01944acbf3915jim rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
7ce40500bfead8781bab964eb6e01944acbf3915jim return (char *)result;
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe /* well, do it the hard way */
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe /* can't do this above, because of the getenv call */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe for (p = var; *p; ++p) {
23a31b10f869a72c9197b5f153f4f3e1a4c68f28jim result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
d52b01101efdd3a37493d1090f20eb23c4dd1521wrowe rewritelog((r, 1, ctx->perdir, "RESULT='%s'", result));
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere return (char *)result;
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr));
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere rewritelog((r, 1, ctx->perdir, "IPV6='%s'", flag ? "on" : "off"));
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere rewritelog((r, 1, ctx->perdir, "IPV6='off' (IPv6 is not enabled)"));
a2d1780b346e147ca4d5c1cdeb0fd8a57fd93899jfclere int flag = rewrite_is_https && rewrite_is_https(r->connection);
06cdc8d205f0982074ab9b032d0b6d74c58f54carjung return apr_psprintf(r->pool, "%04d", tm.tm_year+1900);
65efbf0826de766a90d745cc44427bfa4e2447b6mturk return apr_psprintf(r->pool, "%u", ap_get_server_port(r));
65efbf0826de766a90d745cc44427bfa4e2447b6mturk else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) {
65efbf0826de766a90d745cc44427bfa4e2447b6mturk result = ap_get_remote_host(r->connection,r->per_dir_config,
65efbf0826de766a90d745cc44427bfa4e2447b6mturk return apr_itoa(r->pool, r->connection->remote_addr->port);
e6366481b8fe06a24337f0b30b7da66cf64d6062stoddard if (*var == 'H' && !strcmp(var, "HTTP_FORWARDED")) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe result = r->filename; /* same as request_filename (16) */
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk result = r->filename; /* same as script_filename (15) */
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * +-------------------------------------------------------+
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * | Expansion functions
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * +-------------------------------------------------------+
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * Bracketed expression handling
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk * s points after the opening bracket
144f8db798e3b92e4e4a3f51904e8f21ad160680mturk else if (*s == LEFT_CURLY) {
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowestatic APR_INLINE char *find_char_in_curlies(char *s, int c)
2ceedfca3a2fdfdb5ff60ca17f030ce91f6331cbwrowe else if (*s == LEFT_CURLY) {
9379749d811388a7d0e3410940ddd6743a33d330jim/* perform all the expansions on the input string
b0ac1e83f8582a9b5a72bff798ffb31a419c8adesf * putting the result into a new string
e99dfd55d29a7b4209b814efc7270d0b74ccee74niq * for security reasons this expansion must be performed in a
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim * single pass, otherwise an attacker can arrange for the result
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim * of an earlier expansion to include expansion specifiers that
d27b1ee5dae8f52cdde42e0ac1939cc2397c1be5jim * are interpreted by a later expansion, producing results that
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb * were not intended by the administrator.
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbbstatic char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
a812b025d139f465a31c76fc02ed162ed5271b03nd unsigned spc = 0;
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb char *p, *c;
35c9e4d2c0a6465746a98958ef756114834461e6minfrin /* fast exit */
a2f9f38db0931e6edf7b71378dd680c3c5fa5841rbb /* well, actually something to do */
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim /* loop for specials */
5d5c9e862c3d4e7f15c12d293de4111f52404760wrowe /* prepare next entry */
93cf7fc650197b941ae31a7c7e51e901b129e954igalic /* escaped character */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (*p == '\\') {
1723d9ccdd3b647f5b7bae44cab9ab3eca7a4874dougm if (!p[1]) {
1723d9ccdd3b647f5b7bae44cab9ab3eca7a4874dougm /* variable or map lookup */
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm /* variable lookup */
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm else if (*p == '%') {
621bd763d2e4d32f19013ac8b76b375b5a01851fdougm p = lookup_variable(apr_pstrmemdup(pool, p+2, endp-p-2), ctx);
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk /* map lookup */
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk else { /* *p == '$' */
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * To make rewrite maps useful, the lookup key and
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * default values must be expanded, so we make
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * recursive calls to do the work. For security
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * reasons we must never expand a string that includes
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * verbatim data from the network. The recursion here
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * isn't a problem because the result of expansion is
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * only passed to lookup_map() so it cannot be
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * re-expanded, only re-looked-up. Another way of
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * looking at it is that the recursion is entirely
79d97c2c5e2d3f8bb2a92cd21b3b4900d7bf04d6mturk * driven by the syntax of the nested curly brackets.
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe /* reuse of key variable as result */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe key = lookup_map(ctx->r, map, do_expand(key, ctx, entry));
c64fb33e0c4634fd352c4a6c143cd1a087c09b13nd /* backreference */
c64fb33e0c4634fd352c4a6c143cd1a087c09b13nd backrefinfo *bri = (*p == '$') ? &ctx->briRR : &ctx->briRC;
cd42921af450e87bc797da0ff899348b75732645jfclere /* see ap_pregsub() in server/util.c */
cd42921af450e87bc797da0ff899348b75732645jfclere && bri->regmatch[n].rm_eo > bri->regmatch[n].rm_so) {
9e3209bc06ddf32f23e4b254faa45914bc323cc9jim /* escape the backreference */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span);
67d13599fd4a5f73a2d3fc2b566a9aad60b1c892mturk rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'",
166a9c7964cffc8193764d877edce5bb9b509551mturk current->string = bri->source + bri->regmatch[n].rm_so;
834fc281be8e0f7f2614961f12d8bbf603382a17jfclere /* not for us, just copy it */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe /* check the remainder */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe /* assemble result */
c0a549c3f6e8edc87e921cf76fac95d04feba72bwrowe c = p = apr_palloc(pool, outlen + 1); /* don't forget the \0 */
1d2ff7570139286b0f0d16f92187a16ed5932291mturk ap_assert(c+result->len <= p+outlen); /* XXX: can be removed after
1d2ff7570139286b0f0d16f92187a16ed5932291mturk * extensive testing and
748859d4eeb90f2a64b8b8318bc45c3a61dcd968jim * perform all the expansions on the environment variables
748859d4eeb90f2a64b8b8318bc45c3a61dcd968jimstatic void do_expand_env(data_item *env, rewrite_ctx *ctx)
888c31d26ec2735b6e744697dcaec1ab12877b01mturk rewritelog((ctx->r, 5, NULL, "setting env variable '%s' to '%s'",
14c8c18e6caab1bdeb0f26b2b031e000fef58ef9jim * perform all the expansions on the cookies
a865e849a3b00dc8524ecdda09a1699452876219mturk * TODO: use cached time similar to how logging does it
65aeb4185d6a108b19c27b89f311dc57dab62239nd notename = apr_pstrcat(rmain->pool, var, "_rewrite", NULL);
4b155326ef6781820619c10e518dce1a44df7f91rjung path = expires ? apr_strtok(NULL, ":", &tok_cntx) : NULL;
e8f95a682820a599fe41b22977010636be5c2717jim secure = path ? apr_strtok(NULL, ":", &tok_cntx) : NULL;
fdacf528f0125e0165de42b7783b1dcbf961a189chuck httponly = secure ? apr_strtok(NULL, ":", &tok_cntx) : NULL;
e8f95a682820a599fe41b22977010636be5c2717jim "%.2d:%.2d:%.2d GMT",
65efbf0826de766a90d745cc44427bfa4e2447b6mturk "secure"))) ?
2e41eca72bcc4167d1871b0941ee79845540d58eminfrin "HttpOnly"))) ?
e8f95a682820a599fe41b22977010636be5c2717jim apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie);
e8f95a682820a599fe41b22977010636be5c2717jim apr_pool_userdata_set("set", notename, NULL, rmain->pool);
446c6a9a1e1073798258f1237f8c848b5f917b66wrowe rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie));
446c6a9a1e1073798258f1237f8c848b5f917b66wrowe rewritelog((rmain, 5, NULL, "skipping already set cookie '%s'",
d75bc22ab2702fa770f6935f07107efff16a76f0wrowestatic void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx)
5a5a6c22260854843c973e2ea9a14bec64362ab5wrowe add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL));
bda7a7d57377f45932c237d5aba00b189d85c2a9ianh * Expand tilde-paths (/~user) through Unix /etc/passwd
bda7a7d57377f45932c237d5aba00b189d85c2a9ianh * database information (or other OS-specific database)
char *p, *user;
if (p > user) {
char *homedir;
return homedir;
return uri;
return rc;
return APR_SUCCESS;
if (rewrite_mapr_lock_acquire) {
char quote;
++str;
++str;
if (!*str) {
++str;
++str;
if (!*str) {
++str;
if (!*str) {
++str;
a->server = s;
sizeof(rewrite_server_conf));
sizeof(rewrite_perdir_conf));
return NULL;
int options = 0;
while (*option) {
return NULL;
const char *a2)
const char *fname;
if (colon) {
if (!fname) {
return NULL;
const char *a1)
return NULL;
const char *err;
++key;
while (*key) {
++key;
--endp;
if (val) {
if (err) {
return err;
return NULL;
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
const char *err;
++a2;
switch (*a2) {
? AP_REG_ICASE : 0));
if (!regexp) {
return NULL;
int error = 0;
switch (*key++) {
++error;
if (!cp) {
++error;
if (!cp) {
++error;
++error;
++error;
++error;
++error;
++error;
++error;
++error;
int status = 0;
if (*val) {
int idx =
val);
++error;
++error;
++error;
++error;
if (error) {
return NULL;
const char *in_str)
char *a1;
char *a2;
char *a3;
const char *err;
++a1;
? AP_REG_ICASE : 0));
if (!regexp) {
sizeof(rewritecond_entry));
sizeof(rewritecond_entry));
return NULL;
for (i = 0; i < lena; ++i) {
int rc = 0;
int basis;
switch (p->ptype) {
case CONDPAT_FILE_EXISTS:
case CONDPAT_FILE_SIZE:
case CONDPAT_FILE_LINK:
#if !defined(OS2)
case CONDPAT_FILE_DIR:
case CONDPAT_FILE_XBIT:
case CONDPAT_LU_URL:
case CONDPAT_LU_FILE:
case CONDPAT_STR_GE:
basis = 0;
goto test_str_g;
case CONDPAT_STR_GT:
case CONDPAT_STR_LE:
basis = 0;
goto test_str_l;
case CONDPAT_STR_LT:
case CONDPAT_STR_EQ:
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) {
return HTTP_INTERNAL_SERVER_ERROR;
for (; s; s = s->next) {
if (!first_time) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
if (rewrite_mapr_lock_acquire) {
if (!init_cache(p)) {
const char *saved_rulestatus;
const char *var;
const char *thisserver;
char *thisport;
const char *thisurl;
unsigned int port;
int rulestatus;
void *skipdata;
return DECLINED;
return DECLINED;
return DECLINED;
r->filename));
if (rulestatus) {
unsigned skip;
int n = r->status;
if (!proxy_available) {
return HTTP_FORBIDDEN;
r->filename));
return OK;
r->filename));
if (r->args) {
? r->args
NULL);
n = r->status;
return DECLINED;
#if APR_HAS_USER
return HTTP_BAD_REQUEST;
int res;
return res;
r->filename));
return OK;
return DECLINED;
char *cp;
char *cp2;
const char *ccp;
apr_size_t l;
int rulestatus;
char *ofilename;
int is_proxyreq;
void *skipdata;
return DECLINED;
return DECLINED;
* URL: http://localhost/foo and .htaccess is located in foo directory
if (!is_proxyreq) {
return DECLINED;
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;
return DECLINED;
return DECLINED;
return OK;
{ NULL }