rse 97/11/12 02:46:57
Modified: src CHANGES src/modules/standard mod_rewrite.c mod_rewrite.h Log: The mod_rewrite ``Break Its Heart'' patch: - the rewriting engine now is really very well documented, so all Apache developers should be able to understand it now ;_) Try it out: look at the new apply_rewrite_list and apply_rewrite_rule functions and try to understand it... - the rewriting engine no longer contains any redundant stuff for the proxy-throughput, redirection and per-dir cases. This makes it even more clear to understand and avoid future bugs like the following: - fixed the query string bug recently discovered by Mark: | RewriteRule ^foo /bar?query [R] | RewriteRule ^foo http://host/bar?query [R] where the second rule's query string was escaped. (fixed by avoiding this as an extra case ;_) - fixed the nasty redirection bug recently discovered in c.i.w.s.u | RewriteRule ^(.*[^/])(.*) ${vhost:$1|$1}$2 [R,L] where the expansion of ${vhost} to http://... was too late for the rewriting engine to accept it as a redirect. (fixed by new and more accurate evaluation order) - split out the fully-qualification of URLs into own named function fully_qualify_uri(r). This avoid redundancy, too. Also incorporate updated APACHE_SSL #ifdef'edchanges to mod_rewrite.c from Ben's latest 1.2.4+ssl_1.11 patch - make cmd_rewriterule static (this was left over from old days of the mod_rewrite_compat which never found its way into the core distribution) Submitted by: Ralf S. Engelschall Reviewed by: Roy T. Fielding, Jim Jagielski, Ralf S. Engelschall Revision Changes Path 1.504 +12 -0 apachen/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apachen/src/CHANGES,v retrieving revision 1.503 retrieving revision 1.504 diff -u -r1.503 -r1.504 --- CHANGES 1997/11/12 00:05:55 1.503 +++ CHANGES 1997/11/12 10:46:52 1.504 @@ -1,5 +1,17 @@ Changes with Apache 1.3b3 + *) Complete rewrite ;_) of mod_rewrite's URL rewriting engine: + Now the rewriting engine (the heart of mod_rewrite) is organized more + straight-forward, first time well documented and reduced to the really + essential parts. All redundant cases were stripped off and processing now + is the same for both per-server and per-directory context with only a + minimum difference (the prefix stripping in per-dir context). As a + side-effect some subtle restrictions and two recently discovered problems + are gone: Wrong escaping of QUERY_STRING on redirects in per-directory + context and restrictions on the substitution URL on redirects. + Additionally some minor source cleanups were done. + [Ralf S. Engelschall] + *) Lars Eilebrecht wrote a whole new set of Apache Vhost Internals documentation, examples, explanations and caveats. They live in a new subdirectory htdocs/manual/vhost/. [Lars Eilebrecht <[EMAIL PROTECTED]>] 1.56 +380 -244 apachen/src/modules/standard/mod_rewrite.c Index: mod_rewrite.c =================================================================== RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.c,v retrieving revision 1.55 retrieving revision 1.56 diff -u -r1.55 -r1.56 --- mod_rewrite.c 1997/10/27 19:15:35 1.55 +++ mod_rewrite.c 1997/11/12 10:46:55 1.56 @@ -235,7 +235,7 @@ static int proxy_available; /* maximum nmatch parameter for regexec */ -#define MAX_NMATCH (10) +#define MAX_NMATCH (10) /* the txt mapfile parsing stuff */ #define MAPFILE_PATTERN "^([^ \t]+)[ \t]+([^ \t]+).*$" @@ -637,9 +637,8 @@ return NULL; } -/* NON static */ -const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, - char *str) +static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *str) { rewrite_server_conf *sconf; rewriterule_entry *new; @@ -1447,11 +1446,15 @@ /* ** +-------------------------------------------------------+ ** | | -** | rewriting engine +** | the rewriting engine ** | | ** +-------------------------------------------------------+ */ +/* + * Apply a complete rule set, + * i.e. a list of rewrite rules + */ static int apply_rewrite_list(request_rec *r, array_header *rewriterules, char *perdir) { @@ -1462,14 +1465,19 @@ int rc; int s; + /* + * Iterate over all existing rules + */ entries = (rewriterule_entry *)rewriterules->elts; changed = 0; loop: for (i = 0; i < rewriterules->nelts; i++) { p = &entries[i]; - /* ignore this rule on subrequests if we are explicitly asked to do so - * or this is a proxy throughput or a forced redirect rule + /* + * Ignore this rule on subrequests if we are explicitly + * asked to do so or this is a proxy-throughput or a + * forced redirect rule. */ if (r->main != NULL && (p->flags & RULEFLAG_IGNOREONSUBREQ || @@ -1477,40 +1485,73 @@ p->flags & RULEFLAG_FORCEREDIRECT )) continue; - /* apply the current rule */ + /* + * Apply the current rule. + */ rc = apply_rewrite_rule(r, p, perdir); if (rc) { - if (rc != 2) /* not a match-only rule */ + /* + * Indicate a change if this was not a match-only rule. + */ + if (rc != 2) changed = 1; + + /* + * Pass-Through Feature (`RewriteRule .. .. [PT]'): + * Because the Apache 1.x API is very limited we + * need this hack to pass the rewritten URL to other + * modules like mod_alias, mod_userdir, etc. + */ if (p->flags & RULEFLAG_PASSTHROUGH) { - rewritelog(r, 2, - "forcing '%s' to get passed through to next URI-to-filename handler", - r->filename); - r->filename = pstrcat(r->pool, "passthrough:", r->filename, - NULL); + rewritelog(r, 2, "forcing '%s' to get passed through " + "to next API URI-to-filename handler", r->filename); + r->filename = pstrcat(r->pool, "passthrough:", + r->filename, NULL); changed = 1; break; } + + /* + * Rule has the "forbidden" flag set which means that + * we stop processing and indicate this to the caller. + */ if (p->flags & RULEFLAG_FORBIDDEN) { rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename); r->filename = pstrcat(r->pool, "forbidden:", r->filename, NULL); changed = 1; break; } + + /* + * Rule has the "gone" flag set which means that + * we stop processing and indicate this to the caller. + */ if (p->flags & RULEFLAG_GONE) { rewritelog(r, 2, "forcing '%s' to be gone", r->filename); r->filename = pstrcat(r->pool, "gone:", r->filename, NULL); changed = 1; break; } + + /* + * Stop processing also on proxy pass-through and + * last-rule and new-round flags. + */ if (p->flags & RULEFLAG_PROXY) break; if (p->flags & RULEFLAG_LASTRULE) break; + + /* + * On "new-round" flag we just start from the top of + * the rewriting ruleset again. + */ if (p->flags & RULEFLAG_NEWROUND) goto loop; - /* if we are forced to skip N next rules, do it now */ + /* + * If we are forced to skip N next rules, do it now. + */ if (p->skip > 0) { s = p->skip; while ( i < rewriterules->nelts @@ -1522,8 +1563,9 @@ } } else { - /* if current rule is chained with next rule(s), - * skip all this next rule(s) + /* + * If current rule is chained with next rule(s), + * skip all this next rule(s) */ while ( i < rewriterules->nelts && p->flags & RULEFLAG_CHAIN) { @@ -1535,6 +1577,9 @@ return changed; } +/* + * Apply a single(!) rewrite rule + */ static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir) { @@ -1543,7 +1588,6 @@ int flags; char newuri[MAX_STRING_LEN]; char env[MAX_STRING_LEN]; - char port[32]; regex_t *regexp; regmatch_t regmatch[MAX_NMATCH]; backrefinfo *briRR = NULL; @@ -1556,20 +1600,32 @@ int i; int rc; + /* + * Initialisation + */ uri = r->filename; regexp = p->regexp; output = p->output; flags = p->flags; + /* + * Add (perhaps splitted away) PATH_INFO postfix to URL to + * make sure we really match against the complete URL. + */ if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') { rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s", perdir, uri, uri, r->path_info); uri = pstrcat(r->pool, uri, r->path_info, NULL); } + /* + * On per-directory context (.htaccess) strip the location + * prefix from the URL to make sure patterns apply only to + * the local part. Additionally indicate this special + * threatment in the logfile. + */ prefixstrip = 0; if (perdir != NULL) { - /* this is a per-directory match */ if ( strlen(uri) >= strlen(perdir) && strncmp(uri, perdir, strlen(perdir)) == 0) { rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s", @@ -1579,162 +1635,105 @@ } } - if (perdir != NULL) + /* + * Try to match the URI against the RewriteRule pattern + * and exit immeddiately if it didn't apply. + */ + if (perdir == NULL) + rewritelog(r, 3, "applying pattern '%s' to uri '%s'", + p->pattern, uri); + else rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'", perdir, p->pattern, uri); - - /* try to match the pattern */ rc = (regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0); - if (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || - (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) { - - /* create the RewriteRule regsubinfo */ - briRR = (backrefinfo *)palloc(r->pool, sizeof(backrefinfo)); - if (!rc && (p->flags & RULEFLAG_NOTMATCH)) { - briRR->source = ""; - briRR->nsub = 0; - } - else { - briRR->source = pstrdup(r->pool, uri); - briRR->nsub = regexp->re_nsub; - memcpy((void *)(briRR->regmatch), (void *)(regmatch), - sizeof(regmatch)); - } + if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) + return 0; + + /* + * Else create the RewriteRule `regsubinfo' structure which + * holds the substitution information. + */ + briRR = (backrefinfo *)palloc(r->pool, sizeof(backrefinfo)); + if (!rc && (p->flags & RULEFLAG_NOTMATCH)) { + /* empty info on negative patterns */ + briRR->source = ""; + briRR->nsub = 0; + } + else { + briRR->source = pstrdup(r->pool, uri); + briRR->nsub = regexp->re_nsub; + memcpy((void *)(briRR->regmatch), (void *)(regmatch), + sizeof(regmatch)); + } - /* create the RewriteCond backrefinfo, but - * initialized as empty backrefinfo, i.e. not subst - */ - briRC = (backrefinfo *)pcalloc(r->pool, sizeof(backrefinfo)); - briRC->source = ""; - briRC->nsub = 0; - - /* ok, the pattern matched, but we now additionally have to check - * for any preconditions which have to be also true. We do this - * at this very late stage to avoid unnessesary checks which - * slow down the rewriting engine!! - */ - rewriteconds = p->rewriteconds; - conds = (rewritecond_entry *)rewriteconds->elts; - failed = 0; - for (i = 0; i < rewriteconds->nelts; i++) { - c = &conds[i]; - rc = apply_rewrite_cond(r, c, perdir, briRR, briRC); - if (c->flags & CONDFLAG_ORNEXT) { - /* there is a "or" flag */ - if (rc == 0) { - /* one cond is false, but another can be true... */ - continue; - } - else { - /* one true cond is enough, so skip the other conds - * of the "ornext" chained conds - */ - while ( i < rewriteconds->nelts - && c->flags & CONDFLAG_ORNEXT) { - i++; - c = &conds[i]; - } - continue; - } + /* + * Initiallally create the RewriteCond backrefinfo with + * empty backrefinfo, i.e. not subst parts + * (this one is adjusted inside apply_rewrite_cond() later!!) + */ + briRC = (backrefinfo *)pcalloc(r->pool, sizeof(backrefinfo)); + briRC->source = ""; + briRC->nsub = 0; + + /* + * Ok, we already know the pattern has matched, but we now + * additionally have to check for all existing preconditions + * (RewriteCond) which have to be also true. We do this at + * this very late stage to avoid unnessesary checks which + * would slow down the rewriting engine!! + */ + rewriteconds = p->rewriteconds; + conds = (rewritecond_entry *)rewriteconds->elts; + failed = 0; + for (i = 0; i < rewriteconds->nelts; i++) { + c = &conds[i]; + rc = apply_rewrite_cond(r, c, perdir, briRR, briRC); + if (c->flags & CONDFLAG_ORNEXT) { + /* + * The "OR" case + */ + if (rc == 0) { + /* One condition is false, but another can be + * still true, so we have to continue... + */ + continue; } else { - /* no "or" flag, so a single fail means total fail */ - if (rc == 0) { /* failed */ - failed = 1; - break; + /* One true condition is enough in "or" case, so + * skip the other conditions which are "ornext" + * chained + */ + while ( i < rewriteconds->nelts + && c->flags & CONDFLAG_ORNEXT) { + i++; + c = &conds[i]; } + continue; } } - if (failed) - return 0; /* if any condition fails this complete rule fails */ - - /* if this is a pure matching rule we return immediately */ - if (strcmp(output, "-") == 0) { - /* but before we set the env variables... */ - for (i = 0; p->env[i] != NULL; i++) { - strncpy(env, p->env[i], sizeof(env)-1); - EOS_PARANOIA(env); - expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$'); - expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); - add_env_variable(r, env); - } - return 2; - } - - /* if this is a forced proxy request ... */ - if (p->flags & RULEFLAG_PROXY) { - output = pstrcat(r->pool, "proxy:", output, NULL); - strncpy(newuri, output, sizeof(newuri)-1); - EOS_PARANOIA(newuri); - /* expand $N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR,'$'); - /* expand %N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC,'%'); - /* expand %{...} */ - expand_variables_inbuffer(r, newuri, sizeof(newuri)); - /* expand ${...} */ - expand_map_lookups(r, newuri, sizeof(newuri)); - if (perdir == NULL) - rewritelog(r, 2, "rewrite %s -> %s", r->filename, newuri); - else - rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, - r->filename, newuri); - r->filename = pstrdup(r->pool, newuri); - return 1; - } - - /* if this is an implicit redirect in a per-dir rule */ - i = strlen(output); - if (perdir != NULL - && ( (i > 7 && strncmp(output, "http://", 7) == 0) - || (i > 8 && strncmp(output, "https://", 8) == 0) - || (i > 9 && strncmp(output, "gopher://", 9) == 0) - || (i > 6 && strncmp(output, "ftp://", 6) == 0) ) ) { - strncpy(newuri, output, sizeof(newuri)-1); - EOS_PARANOIA(newuri); - /* expand $N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR,'$'); - /* expand %N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC,'%'); - /* expand %{...} */ - expand_variables_inbuffer(r, newuri, sizeof(newuri)); - /* expand ${...} */ - expand_map_lookups(r, newuri, sizeof(newuri)); - for (i = 0; p->env[i] != NULL; i++) { - strncpy(env, p->env[i], sizeof(env)-1); - EOS_PARANOIA(env); - expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$'); - expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); - add_env_variable(r, env); - } - rewritelog(r, 2, "[per-dir %s] redirect %s -> %s", perdir, - r->filename, newuri); - r->filename = pstrdup(r->pool, newuri); - r->status = p->forced_responsecode; - return 1; - } - - /* add again the previously stripped perdir prefix if the new - * URI is not a new one (i.e. prefixed by a slash which means - * that it is not for this per-dir context) - */ - if (prefixstrip && output[0] != '/') { - rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", - perdir, output, perdir, output); - output = pstrcat(r->pool, perdir, output, NULL); - } - - /* standard case: create the substitution string */ - strncpy(newuri, output, sizeof(newuri)-1); - EOS_PARANOIA(newuri); - /* expand $N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$'); - /* expand %N */ - expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%'); - /* expand %{...} */ - expand_variables_inbuffer(r, newuri, sizeof(newuri)); - /* expand ${...} */ - expand_map_lookups(r, newuri, sizeof(newuri)); + else { + /* + * The "AND" case, i.e. no "or" flag, + * so a single failure means total failure. + */ + if (rc == 0) { + failed = 1; + break; + } + } + } + /* if any condition fails the complete rule fails */ + if (failed) + return 0; + + /* + * If this is a pure matching rule (`RewriteRule <pat> -') + * we stop processing and return immediately. The only thing + * we have not to forget are the environment variables + * (`RewriteRule <pat> - [E=...]') + */ + if (strcmp(output, "-") == 0) { for (i = 0; p->env[i] != NULL; i++) { strncpy(env, p->env[i], sizeof(env)-1); EOS_PARANOIA(env); @@ -1742,93 +1741,180 @@ expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); add_env_variable(r, env); } + return 2; + } - if (perdir == NULL) - rewritelog(r, 2, "rewrite %s -> %s", uri, newuri); - else - rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, - newuri); - - r->filename = pstrdup(r->pool, newuri); + /* + * Ok, now we finally know all patterns have matched and + * that there is something to replace, so we create the + * substitution URL string in `newuri'. + */ + /* 1. take the output string */ + strncpy(newuri, output, sizeof(newuri)-1); + EOS_PARANOIA(newuri); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%'); + /* 4. expand %{...} (i.e. variables) */ + expand_variables_inbuffer(r, newuri, sizeof(newuri)); + /* 5. expand ${...} (RewriteMap lookups) */ + expand_map_lookups(r, newuri, sizeof(newuri)); + /* and log the result... */ + if (perdir == NULL) + rewritelog(r, 2, "rewrite %s -> %s", uri, newuri); + else + rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri); - /* reduce http[s]://<ourhost>[:<port>] */ - reduce_uri(r); + /* + * Additionally do expansion for the environment variable + * strings (`RewriteCond .. .. [E=<string>]'). + */ + for (i = 0; p->env[i] != NULL; i++) { + /* 1. take the string */ + strncpy(env, p->env[i], sizeof(env)-1); + EOS_PARANOIA(env); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); + /* and add the variable to Apache's structures */ + add_env_variable(r, env); + } - /* split out on-the-fly generated QUERY_STRING '....?xxxxx&xxxx...' */ - splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + /* + * Now replace API's knowledge of the current URI: + * Replace r->filename with the new URI string and split out + * an on-the-fly generated QUERY_STRING part into r->args + */ + r->filename = pstrdup(r->pool, newuri); + splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + + /* + * Again add the previously stripped per-directory location + * prefix if the new URI is not a new one for this + * location, i.e. if it's not starting with either a slash + * or a fully qualified URL scheme. + */ + i = strlen(r->filename); + if ( prefixstrip + && !( r->filename[0] == '/' + || ( (i > 7 && strncmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0)))) { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", + perdir, r->filename, perdir, r->filename); + r->filename = pstrcat(r->pool, perdir, r->filename, NULL); + } + + /* + * If this rule is forced for proxy throughput + * (`RewriteRule ... ... [P]') then emulate mod_proxy's + * URL-to-filename handler to be sure mod_proxy is triggered + * for this URL later in the Apache API. But make sure it is + * a fully-qualified URL. (If not it is qualified with + * ourself). + */ + if (p->flags & RULEFLAG_PROXY) { + fully_qualify_uri(r); + if (perdir == NULL) + rewritelog(r, 2, "forcing proxy-throughput with %s", r->filename); + else + rewritelog(r, 2, "[per-dir %s] forcing proxy-throughput with %s", + perdir, r->filename); + r->filename = pstrcat(r->pool, "proxy:", r->filename, NULL); + return 1; + } - /* remember if a MIME-type should be later forced for this URL */ - if (p->forced_mimetype != NULL) { - table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, - p->forced_mimetype); - if (perdir == NULL) - rewritelog(r, 2, "remember %s to have MIME-type '%s'", - r->filename, p->forced_mimetype); - else - rewritelog(r, 2, - "[per-dir %s] remember %s to have MIME-type '%s'", - perdir, r->filename, p->forced_mimetype); - } + /* + * If this rule is explicitly forced for HTTP redirection + * (`RewriteRule .. .. [R]') then force an external HTTP + * redirect. But make sure it is a fully-qualified URL. (If + * not it is qualified with ourself). + */ + if (p->flags & RULEFLAG_FORCEREDIRECT) { + fully_qualify_uri(r); + if (perdir == NULL) + rewritelog(r, 2, + "explicitly forcing redirect with %s", r->filename); + else + rewritelog(r, 2, + "[per-dir %s] explicitly forcing redirect with %s", + perdir, r->filename); + r->status = p->forced_responsecode; + return 1; + } - /* if we are forced to do a explicit redirect by [R] flag - * and the current URL still is not a fully qualified one we - * finally prefix it with http[s]://<ourname> explicitly - */ - if (flags & RULEFLAG_FORCEREDIRECT) { - r->status = p->forced_responsecode; - if ( !(strlen(r->filename) > 7 && - strncmp(r->filename, "http://", 7) == 0) - && !(strlen(r->filename) > 8 && - strncmp(r->filename, "https://", 8) == 0) - && !(strlen(r->filename) > 9 && - strncmp(r->filename, "gopher://", 9) == 0) - && !(strlen(r->filename) > 6 && - strncmp(r->filename, "ftp://", 6) == 0) ) { + /* + * Special Rewriting Feature: Self-Reduction + * We reduce the URL by stripping a possible + * http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which + * corresponds to ourself. This is to simplify rewrite maps + * and to avoid recursion, etc. When this prefix is not a + * coincidence then the user has to use [R] explicitly (see + * above). + */ + reduce_uri(r); -#ifdef APACHE_SSL - if ((!r->connection->client->ssl - && r->server->port == DEFAULT_PORT) || - ( r->connection->client->ssl && r->server->port == 443) ) -#else - if (r->server->port == DEFAULT_PORT) -#endif - port[0] = '\0'; - else - ap_snprintf(port, sizeof(port), ":%u", r->server->port); - if (r->filename[0] == '/') -#ifdef APACHE_SSL - ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s", - http_method(r), r->server->server_hostname, - port, r->filename); -#else - ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s", - r->server->server_hostname, port, r->filename); -#endif - else -#ifdef APACHE_SSL - ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s", - http_method(r), r->server->server_hostname, - port, r->filename); -#else - ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s", - r->server->server_hostname, port, r->filename); -#endif - if (perdir == NULL) - rewritelog(r, 2, "prepare forced redirect %s -> %s", - r->filename, newuri); - else - rewritelog(r, 2, - "[per-dir %s] prepare forced redirect %s -> %s", - perdir, r->filename, newuri); + /* + * If this rule is still implicitly forced for HTTP + * redirection (`RewriteRule .. <scheme>://...') then + * directly force an external HTTP redirect. + */ + i = strlen(r->filename); + if ( (i > 7 && strncmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0)) { + if (perdir == NULL) + rewritelog(r, 2, + "implicitly forcing redirect (rc=%d) with %s", + p->forced_responsecode, r->filename); + else + rewritelog(r, 2, "[per-dir %s] implicitly forcing redirect " + "(rc=%d) with %s", perdir, p->forced_responsecode, + r->filename); + r->status = p->forced_responsecode; + return 1; + } - r->filename = pstrdup(r->pool, newuri); - return 1; - } - } + /* + * Now we are sure it is not a fully qualified URL. But + * there is still one special case left: A local rewrite in + * per-directory context, i.e. a substitution URL which does + * not start with a slash. Here we add again the initially + * stripped per-directory prefix. + */ + if (prefixstrip && r->filename[0] != '/') { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", + perdir, r->filename, perdir, r->filename); + r->filename = pstrcat(r->pool, perdir, r->filename, NULL); + } - return 1; + /* + * Finally we had to remember if a MIME-type should be + * forced for this URL (`RewriteRule .. .. [T=<type>]') + * Later in the API processing phase this is forced by our + * MIME API-hook function. + */ + if (p->forced_mimetype != NULL) { + table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + p->forced_mimetype); + if (perdir == NULL) + rewritelog(r, 2, "remember %s to have MIME-type '%s'", + r->filename, p->forced_mimetype); + else + rewritelog(r, 2, + "[per-dir %s] remember %s to have MIME-type '%s'", + perdir, r->filename, p->forced_mimetype); } - return 0; + + /* + * Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_) + * But now we're done for this particular rule. + */ + return 1; } static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, @@ -2103,6 +2189,56 @@ } } return; +} + + +/* +** +** add 'http[s]://ourhost[:ourport]/' to URI +** if URI is still not fully qualified +** +*/ + +static void fully_qualify_uri(request_rec *r) +{ + int i; + char newuri[MAX_STRING_LEN]; + char port[32]; + + i = strlen(r->filename); + if (!( (i > 7 && strncmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0))) { +#ifdef APACHE_SSL + if (is_default_port(r->server->port,r)) +#else + if (r->server->port == DEFAULT_PORT) +#endif + port[0] = '\0'; + else + ap_snprintf(port, sizeof(port), ":%u", r->server->port); + if (r->filename[0] == '/') +#ifdef APACHE_SSL + ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s", + http_method(r), r->server->server_hostname, + port, r->filename); +#else + ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s", + r->server->server_hostname, port, r->filename); +#endif + else +#ifdef APACHE_SSL + ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s", + http_method(r), r->server->server_hostname, + port, r->filename); +#else + ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s", + r->server->server_hostname, port, r->filename); +#endif + r->filename = pstrdup(r->pool, newuri); + } + return; } 1.35 +2 -1 apachen/src/modules/standard/mod_rewrite.h Index: mod_rewrite.h =================================================================== RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.h,v retrieving revision 1.34 retrieving revision 1.35 diff -u -r1.34 -r1.35 --- mod_rewrite.h 1997/10/22 20:30:28 1.34 +++ mod_rewrite.h 1997/11/12 10:46:56 1.35 @@ -348,7 +348,7 @@ static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg, char *key, char *val); -extern const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, +static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str); static const char *cmd_rewriterule_parseflagfield(pool *p, @@ -378,6 +378,7 @@ /* URI transformation function */ static void splitout_queryargs(request_rec *r, int qsappend); +static void fully_qualify_uri(request_rec *r); static void reduce_uri(request_rec *r); static void expand_backref_inbuffer(pool *p, char *buf, int nbuf, backrefinfo *bri, char c);