Apache HTTPD
mod_rewrite.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* _ _ _
18 * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
19 * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
20 * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
21 * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
22 * |_____|
23 *
24 * URL Rewriting Module
25 *
26 * This module uses a rule-based rewriting engine (based on a
27 * regular-expression parser) to rewrite requested URLs on the fly.
28 *
29 * It supports an unlimited number of additional rule conditions (which can
30 * operate on a lot of variables, even on HTTP headers) for granular
31 * matching and even external database lookups (either via plain text
32 * tables, DBM hash files or even external processes) for advanced URL
33 * substitution.
34 *
35 * It operates on the full URLs (including the PATH_INFO part) both in
36 * per-server context (httpd.conf) and per-dir context (.htaccess) and even
37 * can generate QUERY_STRING parts on result. The rewriting result finally
38 * can lead to internal subprocessing, external request redirection or even
39 * to internal proxy throughput.
40 *
41 * This module was originally written in April 1996 and
42 * gifted exclusively to the The Apache Software Foundation in July 1997 by
43 *
44 * Ralf S. Engelschall
45 * rse engelschall.com
46 * www.engelschall.com
47 */
48
49#include "apr.h"
50#include "apr_strings.h"
51#include "apr_hash.h"
52#include "apr_user.h"
53#include "apr_lib.h"
54#include "apr_signal.h"
55#include "apr_global_mutex.h"
56#include "apr_dbm.h"
57#include "apr_dbd.h"
58
59#include "apr_version.h"
60#if !APR_VERSION_AT_LEAST(2,0,0)
61#include "apu_version.h"
62#endif
63
64#include "mod_dbd.h"
65
66#if APR_HAS_THREADS
67#include "apr_thread_mutex.h"
68#endif
69
70#define APR_WANT_MEMFUNC
71#define APR_WANT_STRFUNC
72#define APR_WANT_IOVEC
73#include "apr_want.h"
74
75/* XXX: Do we really need these headers? */
76#if APR_HAVE_UNISTD_H
77#include <unistd.h>
78#endif
79#if APR_HAVE_SYS_TYPES_H
80#include <sys/types.h>
81#endif
82#if APR_HAVE_STDARG_H
83#include <stdarg.h>
84#endif
85#if APR_HAVE_STDLIB_H
86#include <stdlib.h>
87#endif
88#if APR_HAVE_CTYPE_H
89#include <ctype.h>
90#endif
91#if APR_HAVE_NETINET_IN_H
92#include <netinet/in.h>
93#endif
94
95#include "ap_config.h"
96#include "httpd.h"
97#include "http_config.h"
98#include "http_request.h"
99#include "http_core.h"
100#include "http_log.h"
101#include "http_protocol.h"
102#include "http_ssl.h"
103#include "http_vhost.h"
104#include "util_mutex.h"
105
106#include "mod_rewrite.h"
107#include "ap_expr.h"
108
109#include "test_char.h"
110
111static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL;
112static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
113static const char* really_last_key = "rewrite_really_last";
114
115/*
116 * in order to improve performance on running production systems, you
117 * may strip all rewritelog code entirely from mod_rewrite by using the
118 * -DREWRITELOG_DISABLED compiler option.
119 *
120 * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
121 * responsible for answering all the mod_rewrite questions out there.
122 */
123/* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */
124#ifdef APLOG_MAX_LOGLEVEL
125#if APLOG_MAX_LOGLEVEL < APLOG_TRACE1
126#ifndef REWRITELOG_DISABLED
127#define REWRITELOG_DISABLED
128#endif
129#endif
130#endif
131
132#ifndef REWRITELOG_DISABLED
133
134#define rewritelog(x) do_rewritelog x
135#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
136#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
137
138#else /* !REWRITELOG_DISABLED */
139
140#define rewritelog(x)
141
142#endif /* REWRITELOG_DISABLED */
143
144/* remembered mime-type for [T=...] */
145#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
146#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler"
147
148#define ENVVAR_SCRIPT_URL "SCRIPT_URL"
149#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
150#define ENVVAR_SCRIPT_URI "SCRIPT_URI"
151
152#define CONDFLAG_NONE (1<<0)
153#define CONDFLAG_NOCASE (1<<1)
154#define CONDFLAG_NOTMATCH (1<<2)
155#define CONDFLAG_ORNEXT (1<<3)
156#define CONDFLAG_NOVARY (1<<4)
157
158#define RULEFLAG_NONE (1<<0)
159#define RULEFLAG_FORCEREDIRECT (1<<1)
160#define RULEFLAG_LASTRULE (1<<2)
161#define RULEFLAG_NEWROUND (1<<3)
162#define RULEFLAG_CHAIN (1<<4)
163#define RULEFLAG_IGNOREONSUBREQ (1<<5)
164#define RULEFLAG_NOTMATCH (1<<6)
165#define RULEFLAG_PROXY (1<<7)
166#define RULEFLAG_PASSTHROUGH (1<<8)
167#define RULEFLAG_QSAPPEND (1<<9)
168#define RULEFLAG_NOCASE (1<<10)
169#define RULEFLAG_NOESCAPE (1<<11)
170#define RULEFLAG_NOSUB (1<<12)
171#define RULEFLAG_STATUS (1<<13)
172#define RULEFLAG_ESCAPEBACKREF (1<<14)
173#define RULEFLAG_DISCARDPATHINFO (1<<15)
174#define RULEFLAG_QSDISCARD (1<<16)
175#define RULEFLAG_END (1<<17)
176#define RULEFLAG_ESCAPENOPLUS (1<<18)
177#define RULEFLAG_QSLAST (1<<19)
178#define RULEFLAG_QSNONE (1<<20) /* programattic only */
179#define RULEFLAG_ESCAPECTLS (1<<21)
180#define RULEFLAG_UNSAFE_PREFIX_STAT (1<<22)
181#define RULEFLAG_UNSAFE_ALLOW3F (1<<23)
182#define RULEFLAG_UNC (1<<24)
183
184/* return code of the rewrite rule
185 * the result may be escaped - or not
186 */
187#define ACTION_NORMAL (1<<0)
188#define ACTION_NOESCAPE (1<<1)
189#define ACTION_STATUS (1<<2)
190#define ACTION_STATUS_SET (1<<3)
191
192#define MAPTYPE_TXT (1<<0)
193#define MAPTYPE_DBM (1<<1)
194#define MAPTYPE_PRG (1<<2)
195#define MAPTYPE_INT (1<<3)
196#define MAPTYPE_RND (1<<4)
197#define MAPTYPE_DBD (1<<5)
198#define MAPTYPE_DBD_CACHE (1<<6)
199
200#define ENGINE_DISABLED (1<<0)
201#define ENGINE_ENABLED (1<<1)
202
203#define OPTION_NONE (1<<0)
204#define OPTION_INHERIT (1<<1)
205#define OPTION_INHERIT_BEFORE (1<<2)
206#define OPTION_NOSLASH (1<<3)
207#define OPTION_ANYURI (1<<4)
208#define OPTION_MERGEBASE (1<<5)
209#define OPTION_INHERIT_DOWN (1<<6)
210#define OPTION_INHERIT_DOWN_BEFORE (1<<7)
211#define OPTION_IGNORE_INHERIT (1<<8)
212#define OPTION_IGNORE_CONTEXT_INFO (1<<9)
213#define OPTION_LEGACY_PREFIX_DOCROOT (1<<10)
214#define OPTION_UNSAFE_PREFIX_STAT (1<<12)
215
216#ifndef RAND_MAX
217#define RAND_MAX 32767
218#endif
219
220/* max cookie size in rfc 2109 */
221/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
222#define MAX_COOKIE_LEN 4096
223
224/* max line length (incl.\n) in text rewrite maps */
225#ifndef REWRITE_MAX_TXT_MAP_LINE
226#define REWRITE_MAX_TXT_MAP_LINE 1024
227#endif
228
229/* buffer length for prg rewrite maps */
230#ifndef REWRITE_PRG_MAP_BUF
231#define REWRITE_PRG_MAP_BUF 1024
232#endif
233
234/* for better readbility */
235#define LEFT_CURLY '{'
236#define RIGHT_CURLY '}'
237
238/*
239 * expansion result items on the stack to save some cycles
240 *
241 * (5 == about 2 variables like "foo%{var}bar%{var}baz")
242 */
243#define SMALL_EXPANSION 5
244
245/*
246 * check that a subrequest won't cause infinite recursion
247 *
248 * either not in a subrequest, or in a subrequest
249 * and URIs aren't NULL and sub/main URIs differ
250 */
251#define subreq_ok(r) (!r->main || \
252 (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
253
254#ifndef REWRITE_MAX_ROUNDS
255#define REWRITE_MAX_ROUNDS 32000
256#endif
257
258/*
259 * +-------------------------------------------------------+
260 * | |
261 * | Types and Structures
262 * | |
263 * +-------------------------------------------------------+
264 */
265
266typedef struct {
267 const char *datafile; /* filename for map data files */
268 const char *dbmtype; /* dbm type for dbm map data files */
269 const char *checkfile; /* filename to check for map existence */
270 const char *cachename; /* for cached maps (txt/rnd/dbm) */
271 int type; /* the type of the map */
272 apr_file_t *fpin; /* in file pointer for program maps */
273 apr_file_t *fpout; /* out file pointer for program maps */
274 apr_file_t *fperr; /* err file pointer for program maps */
275 char *(*func)(request_rec *, /* function pointer for internal maps */
276 char *);
277 char **argv; /* argv of the external rewrite map */
278 const char *dbdq; /* SQL SELECT statement for rewritemap */
279 const char *checkfile2; /* filename to check for map existence
280 NULL if only one file */
281 const char *user; /* run RewriteMap program as this user */
282 const char *group; /* run RewriteMap program as this group */
284
285/* special pattern types for RewriteCond */
307
308typedef enum {
309 RULE_RC_NOMATCH = 0, /* the rule didn't match */
310 RULE_RC_MATCH = 1, /* a matching rule w/ substitution */
311 RULE_RC_NOSUB = 2, /* a matching rule w/ no substitution */
312 RULE_RC_STATUS_SET = 3 /* a matching rule that has set an HTTP error
313 to be returned in r->status */
315
316typedef struct {
317 char *input; /* Input string of RewriteCond */
318 char *pattern; /* the RegExp pattern string */
319 ap_regex_t *regexp; /* the precompiled regexp */
320 ap_expr_info_t *expr; /* the compiled ap_expr */
321 int flags; /* Flags which control the match */
322 pattern_type ptype; /* pattern type */
323 int pskip; /* back-index to display pattern */
325
326/* single linked list for env vars and cookies */
327typedef struct data_item {
329 char *data;
331
332typedef struct {
333 apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
334 char *pattern; /* the RegExp pattern string */
335 ap_regex_t *regexp; /* the RegExp pattern compilation */
336 char *output; /* the Substitution string */
337 int flags; /* Flags which control the substitution */
338 char *forced_mimetype; /* forced MIME type of substitution */
339 char *forced_handler; /* forced content handler of subst. */
340 int forced_responsecode; /* forced HTTP response status */
341 data_item *env; /* added environment variables */
342 data_item *cookie; /* added cookies */
343 int skip; /* number of next rules to skip */
344 int maxrounds; /* limit on number of loops with N flag */
345 const char *escapes; /* specific backref escapes */
346 const char *noescapes; /* specific backref chars not to escape */
348
349typedef struct {
350 int state; /* the RewriteEngine state */
351 int options; /* the RewriteOption state */
352 apr_hash_t *rewritemaps; /* the RewriteMap entries */
353 apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
354 apr_array_header_t *rewriterules; /* the RewriteRule entries */
355 server_rec *server; /* the corresponding server indicator */
356 unsigned int state_set:1;
357 unsigned int options_set:1;
359
360typedef struct {
361 int state; /* the RewriteEngine state */
362 int options; /* the RewriteOption state */
363 apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */
364 apr_array_header_t *rewriterules; /* the RewriteRule entries */
365 char *directory; /* the directory where it applies */
366 const char *baseurl; /* the base-URL where it applies */
367 unsigned int state_set:1;
368 unsigned int options_set:1;
369 unsigned int baseurl_set:1;
371
372/* the (per-child) cache structures.
373 */
374typedef struct cache {
377#if APR_HAS_THREADS
379#endif
381
382/* cached maps contain an mtime for the whole map and live in a subpool
383 * of the cachep->pool. That makes it easy to forget them if necessary.
384 */
390
391/* the regex structure for the
392 * substitution of backreferences
393 */
398
399/* single linked list used for
400 * variable expansion
401 */
407
408/* context structure for variable lookup and expansion
409 */
410typedef struct {
412 const char *uri;
413 const char *vary_this;
414 const char *vary;
415 char *perdir;
419
420/*
421 * +-------------------------------------------------------+
422 * | |
423 * | static module data
424 * | |
425 * +-------------------------------------------------------+
426 */
427
428/* the global module structure */
429module AP_MODULE_DECLARE_DATA rewrite_module;
430
431/* rewritemap int: handler function registry */
433
434/* the cache */
435static cache *cachep;
436
437/* whether proxy module is available or not */
439
440/* Locks/Mutexes */
441static int rewrite_lock_needed = 0;
443static const char *rewritemap_mutex_type = "rewrite-map";
444
445/* Optional functions imported from mod_ssl when loaded: */
446static char *escape_backref(apr_pool_t *p, const char *path,
447 const char *escapeme, const char *noescapeme,
448 int flags);
449
450/*
451 * +-------------------------------------------------------+
452 * | |
453 * | rewriting logfile support
454 * | |
455 * +-------------------------------------------------------+
456 */
457
458#ifndef REWRITELOG_DISABLED
459static void do_rewritelog(request_rec *r, int level, char *perdir,
460 const char *fmt, ...)
462
463static void do_rewritelog(request_rec *r, int level, char *perdir,
464 const char *fmt, ...)
465{
466 char *logline, *text;
467 const char *rhost, *rname;
468 int redir;
469 request_rec *req;
470 va_list ap;
471
472 if (!APLOG_R_IS_LEVEL(r, APLOG_DEBUG + level))
473 return;
474
477
478 for (redir=0, req=r; req->prev; req = req->prev) {
479 ++redir;
480 }
481
482 va_start(ap, fmt);
484 va_end(ap);
485
486 logline = apr_psprintf(r->pool, "%s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] "
487 "%s%s%s%s",
488 rhost ? rhost : "UNKNOWN-HOST",
489 rname ? rname : "-",
490 r->user ? (*r->user ? r->user : "\"\"") : "-",
492 (void *)(r->server),
493 (void *)r,
494 r->main ? "subreq" : "initial",
495 redir ? "/redir#" : "",
496 redir ? apr_itoa(r->pool, redir) : "",
497 perdir ? "[perdir " : "",
498 perdir ? perdir : "",
499 perdir ? "] ": "",
500 text);
501
502 AP_REWRITE_LOG((uintptr_t)r, level, r->main ? 0 : 1, (char *)ap_get_server_name(r), logline);
503
504 /* Intentional no APLOGNO */
505 ap_log_rerror(APLOG_MARK, APLOG_DEBUG + level, 0, r, "%s", logline);
506
507 return;
508}
509#endif /* !REWRITELOG_DISABLED */
510
511
512/*
513 * +-------------------------------------------------------+
514 * | |
515 * | URI and path functions
516 * | |
517 * +-------------------------------------------------------+
518 */
519
520/* return number of chars of the scheme (incl. '://')
521 * if the URI is absolute (includes a scheme etc.)
522 * otherwise 0.
523 * If supportqs is not NULL, we return a whether or not
524 * the scheme supports a query string or not.
525 *
526 * NOTE: If you add new schemes here, please have a
527 * look at escape_absolute_uri and splitout_queryargs.
528 * Not every scheme takes query strings and some schemes
529 * may be handled in a special way.
530 *
531 * XXX: we may consider a scheme registry, perhaps with
532 * appropriate escape callbacks to allow other modules
533 * to extend mod_rewrite at runtime.
534 */
535static unsigned is_absolute_uri(char *uri, int *supportsqs)
536{
537 int dummy, *sqs;
538
540 *sqs = 0;
541 /* fast exit */
542 if (*uri == '/' || strlen(uri) <= 5) {
543 return 0;
544 }
545
546 switch (*uri++) {
547 case 'a':
548 case 'A':
549 if (!ap_cstr_casecmpn(uri, "jp://", 5)) { /* ajp:// */
550 *sqs = 1;
551 return 6;
552 }
553 break;
554
555 case 'b':
556 case 'B':
557 if (!ap_cstr_casecmpn(uri, "alancer://", 10)) { /* balancer:// */
558 *sqs = 1;
559 return 11;
560 }
561 break;
562
563 case 'f':
564 case 'F':
565 if (!ap_cstr_casecmpn(uri, "tp://", 5)) { /* ftp:// */
566 return 6;
567 }
568 if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* fcgi:// */
569 *sqs = 1;
570 return 7;
571 }
572 break;
573
574 case 'g':
575 case 'G':
576 if (!ap_cstr_casecmpn(uri, "opher://", 8)) { /* gopher:// */
577 return 9;
578 }
579 break;
580
581 case 'h':
582 case 'H':
583 if (!ap_cstr_casecmpn(uri, "ttp://", 6)) { /* http:// */
584 *sqs = 1;
585 return 7;
586 }
587 else if (!ap_cstr_casecmpn(uri, "ttps://", 7)) { /* https:// */
588 *sqs = 1;
589 return 8;
590 }
591 else if (!ap_cstr_casecmpn(uri, "2://", 4)) { /* h2:// */
592 *sqs = 1;
593 return 5;
594 }
595 else if (!ap_cstr_casecmpn(uri, "2c://", 5)) { /* h2c:// */
596 *sqs = 1;
597 return 6;
598 }
599 break;
600
601 case 'l':
602 case 'L':
603 if (!ap_cstr_casecmpn(uri, "dap://", 6)) { /* ldap:// */
604 return 7;
605 }
606 break;
607
608 case 'm':
609 case 'M':
610 if (!ap_cstr_casecmpn(uri, "ailto:", 6)) { /* mailto: */
611 *sqs = 1;
612 return 7;
613 }
614 break;
615
616 case 'n':
617 case 'N':
618 if (!ap_cstr_casecmpn(uri, "ews:", 4)) { /* news: */
619 return 5;
620 }
621 else if (!ap_cstr_casecmpn(uri, "ntp://", 6)) { /* nntp:// */
622 return 7;
623 }
624 break;
625
626 case 's':
627 case 'S':
628 if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* scgi:// */
629 *sqs = 1;
630 return 7;
631 }
632 break;
633
634 case 'w':
635 case 'W':
636 if (!ap_cstr_casecmpn(uri, "s://", 4)) { /* ws:// */
637 *sqs = 1;
638 return 5;
639 }
640 else if (!ap_cstr_casecmpn(uri, "ss://", 5)) { /* wss:// */
641 *sqs = 1;
642 return 6;
643 }
644 break;
645
646 case 'u':
647 case 'U':
648 if (!ap_cstr_casecmpn(uri, "nix:", 4)) { /* unix: */
649 *sqs = 1;
650 return (uri[4] == '/' && uri[5] == '/') ? 7 : 5;
651 }
652 }
653
654 return 0;
655}
656
657static int is_absolute_path(const char *path)
658{
659#ifndef CASE_BLIND_FILESYSTEM
660 return (path[0] == '/');
661#else
662 return ((AP_IS_SLASH(path[0]) && path[1] == path[0])
663 || (apr_isalpha(path[0]) && path[1] == ':' && AP_IS_SLASH(path[2])));
664#endif
665}
666
667static const char c2x_table[] = "0123456789abcdef";
668
669static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
670 unsigned char *where)
671{
672#if APR_CHARSET_EBCDIC
674#endif /*APR_CHARSET_EBCDIC*/
675 *where++ = prefix;
676 *where++ = c2x_table[what >> 4];
677 *where++ = c2x_table[what & 0xf];
678 return where;
679}
680
681/*
682 * Escapes a backreference in a similar way as php's urlencode does.
683 * Based on ap_os_escape_path in server/util.c
684 */
685static char *escape_backref(apr_pool_t *p, const char *path,
686 const char *escapeme, const char *noescapeme,
687 int flags)
688{
689 char *copy = apr_palloc(p, 3 * strlen(path) + 1);
690 const unsigned char *s = (const unsigned char *)path;
691 unsigned char *d = (unsigned char *)copy;
692 int noplus = (flags & RULEFLAG_ESCAPENOPLUS) != 0;
693 int ctls = (flags & RULEFLAG_ESCAPECTLS) != 0;
694 unsigned char c;
695
696 while ((c = *s)) {
698 || (escapeme && ap_strchr_c(escapeme, c)))
699 && (!noescapeme || !ap_strchr_c(noescapeme, c))) {
700 if (apr_isalnum(c) || c == '_') {
701 *d++ = c;
702 }
703 else if (c == ' ' && !noplus) {
704 *d++ = '+';
705 }
706 else {
707 d = c2x(c, '%', d);
708 }
709 }
710 else {
711 *d++ = c;
712 }
713 ++s;
714 }
715 *d = '\0';
716 return copy;
717}
718
719/*
720 * escape absolute uri, which may or may not be path oriented.
721 * So let's handle them differently.
722 */
723static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
724{
725 char *cp;
726
727 /* be safe.
728 * NULL should indicate elsewhere, that something's wrong
729 */
730 if (!scheme || strlen(uri) < scheme) {
731 return NULL;
732 }
733
734 cp = uri + scheme;
735
736 /* scheme with authority part? */
737 if (cp[-1] == '/') {
738 /* skip host part */
739 while (*cp && *cp != '/') {
740 ++cp;
741 }
742
743 /* nothing after the hostpart. ready! */
744 if (!*cp || !*++cp) {
745 return apr_pstrdup(p, uri);
746 }
747
748 /* remember the hostname stuff */
749 scheme = cp - uri;
750
751 /* special thing for ldap.
752 * The parts are separated by question marks. From RFC 2255:
753 * ldapurl = scheme "://" [hostport] ["/"
754 * [dn ["?" [attributes] ["?" [scope]
755 * ["?" [filter] ["?" extensions]]]]]]
756 */
757 if (!ap_cstr_casecmpn(uri, "ldap", 4)) {
758 char *token[5];
759 int c = 0;
760
761 token[0] = cp = apr_pstrdup(p, cp);
762 while (*cp && c < 4) {
763 if (*cp == '?') {
764 token[++c] = cp + 1;
765 *cp = '\0';
766 }
767 ++cp;
768 }
769
770 return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
771 ap_escape_uri(p, token[0]),
772 (c >= 1) ? "?" : NULL,
773 (c >= 1) ? ap_escape_uri(p, token[1]) : NULL,
774 (c >= 2) ? "?" : NULL,
775 (c >= 2) ? ap_escape_uri(p, token[2]) : NULL,
776 (c >= 3) ? "?" : NULL,
777 (c >= 3) ? ap_escape_uri(p, token[3]) : NULL,
778 (c >= 4) ? "?" : NULL,
779 (c >= 4) ? ap_escape_uri(p, token[4]) : NULL,
780 NULL);
781 }
782 }
783
784 /* Nothing special here. Apply normal escaping. */
785 return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
786 ap_escape_uri(p, cp), NULL);
787}
788
789/*
790 * split out a QUERY_STRING part from
791 * the current URI string
792 */
794{
795 char *q;
796 int split, skip;
800
801 if (flags & RULEFLAG_QSNONE) {
802 rewritelog((r, 2, NULL, "discarding query string, no parse from substitution"));
803 r->args = NULL;
804 return;
805 }
806
807 /* don't touch, unless it's a scheme for which a query string makes sense.
808 * See RFC 1738 and RFC 2368.
809 */
810 if ((skip = is_absolute_uri(r->filename, &split))
811 && !split) {
812 r->args = NULL; /* forget the query that's still flying around */
813 return;
814 }
815
816 if (qsdiscard) {
817 r->args = NULL; /* Discard query string */
818 rewritelog((r, 2, NULL, "discarding query string"));
819 }
820
821 q = qslast ? ap_strrchr(r->filename + skip, '?') : ap_strchr(r->filename + skip, '?');
822
823 if (q != NULL) {
824 char *olduri;
826
828 *q++ = '\0';
829 if (qsappend) {
830 if (*q) {
831 r->args = apr_pstrcat(r->pool, q, "&" , r->args, NULL);
832 }
833 }
834 else {
835 r->args = apr_pstrdup(r->pool, q);
836 }
837
838 if (r->args) {
839 len = strlen(r->args);
840
841 if (!len) {
842 r->args = NULL;
843 }
844 else if (r->args[len-1] == '&') {
845 r->args[len-1] = '\0';
846 }
847 }
848
849 rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri,
850 r->filename, r->args ? r->args : "<none>"));
851 }
852}
853
854/*
855 * strip 'http[s]://ourhost/' from URI
856 */
858{
859 char *cp;
860 apr_size_t l;
861
862 cp = (char *)ap_http_scheme(r);
863 l = strlen(cp);
864 if ( strlen(r->filename) > l+3
865 && ap_cstr_casecmpn(r->filename, cp, l) == 0
866 && r->filename[l] == ':'
867 && r->filename[l+1] == '/'
868 && r->filename[l+2] == '/' ) {
869
870 unsigned short port;
871 char *portp, *host, *url, *scratch;
872
873 scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */
874
875 /* cut the hostname and port out of the URI */
876 cp = host = scratch + l + 3; /* 3 == strlen("://") */
877 while (*cp && *cp != '/' && *cp != ':') {
878 ++cp;
879 }
880
881 if (*cp == ':') { /* additional port given */
882 *cp++ = '\0';
883 portp = cp;
884 while (*cp && *cp != '/') {
885 ++cp;
886 }
887 *cp = '\0';
888
889 port = atoi(portp);
890 url = r->filename + (cp - scratch);
891 if (!*url) {
892 url = "/";
893 }
894 }
895 else if (*cp == '/') { /* default port */
896 *cp = '\0';
897
899 url = r->filename + (cp - scratch);
900 }
901 else {
903 url = "/";
904 }
905
906 /* now check whether we could reduce it to a local path... */
908 rewrite_server_conf *conf =
909 ap_get_module_config(r->server->module_config, &rewrite_module);
910 rewritelog((r, 3, NULL, "reduce %s -> %s", r->filename, url));
912
913 /* remember that the uri was reduced */
915 apr_table_setn(r->notes, "mod_rewrite_uri_reduced", "true");
916 }
917 }
918 }
919
920 return;
921}
922
923/*
924 * add 'http[s]://ourhost[:ourport]/' to URI
925 * if URI is still not fully qualified
926 */
928{
929 if (r->method_number == M_CONNECT) {
930 return;
931 }
932 else if (!is_absolute_uri(r->filename, NULL)) {
933 const char *thisserver;
934 char *thisport;
935 int port;
936
940 ? ""
941 : apr_psprintf(r->pool, ":%u", port);
942
943 r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s",
945 (*r->filename == '/') ? "" : "/",
946 r->filename);
947 }
948
949 return;
950}
951
952static int startsWith(request_rec *r, const char *haystack, const char *needle) {
954 rewritelog((r, 5, NULL, "prefix_stat startsWith(%s, %s) %d", haystack, needle, rc));
955 return rc;
956}
957/*
958 * stat() only the first segment of a path, and only if it matches the output of the last matching rule
959 */
961{
962 const char *curpath = path;
963 const char *root;
964 const char *slash;
965 char *statpath;
966 apr_status_t rv;
967
969
970 if (rv != APR_SUCCESS) {
971 return 0;
972 }
973
974 /* let's recognize slashes only, the mod_rewrite semantics are opaque
975 * enough.
976 */
977 if ((slash = ap_strchr_c(curpath, '/')) != NULL) {
978 rv = apr_filepath_merge(&statpath, root,
983 }
984 else {
988 }
989
990 if (rv == APR_SUCCESS) {
992
994 if (!lastsub) {
995 rewritelog((r, 3, NULL, "prefix_stat no lastsub subst prefix %s", statpath));
996 return 1;
997 }
998
999 rewritelog((r, 3, NULL, "prefix_stat compare statpath %s and lastsub output %s STATOK %d ",
1001 if (lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT) {
1002 return 1;
1003 }
1004 else {
1005 const char *docroot = ap_document_root(r);
1007 /*
1008 * As an example, path (r->filename) is /var/foo/bar/baz.html
1009 * even if the flag is not set, we can accept a rule that
1010 * began with a literal /var (stapath), or if the entire path
1011 * starts with the docroot or context document root
1012 */
1013 if (startsWith(r, lastsub->output, statpath) ||
1014 startsWith(r, path, docroot) ||
1015 ((docroot != context_docroot) &&
1017 return 1;
1018 }
1019 }
1020 }
1021 }
1022
1023 /* prefix will be added */
1024 return 0;
1025}
1026
1027/*
1028 * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
1029 */
1030static char *subst_prefix_path(request_rec *r, char *input, const char *match,
1031 const char *subst)
1032{
1033 apr_size_t len = strlen(match);
1034
1035 if (len && match[len - 1] == '/') {
1036 --len;
1037 }
1038
1039 if (!strncmp(input, match, len) && input[len++] == '/') {
1041 char *output;
1042
1043 rewritelog((r, 5, NULL, "strip matching prefix: %s -> %s", input,
1044 input+len));
1045
1046 slen = strlen(subst);
1047 if (slen && subst[slen - 1] != '/') {
1048 ++slen;
1049 }
1050
1051 outlen = strlen(input) + slen - len;
1052 output = apr_palloc(r->pool, outlen + 1); /* don't forget the \0 */
1053
1054 memcpy(output, subst, slen);
1055 if (slen && !output[slen-1]) {
1056 output[slen-1] = '/';
1057 }
1058 memcpy(output+slen, input+len, outlen - slen);
1059 output[outlen] = '\0';
1060
1061 rewritelog((r, 4, NULL, "add subst prefix: %s -> %s", input+len,
1062 output));
1063
1064 return output;
1065 }
1066
1067 /* prefix didn't match */
1068 return input;
1069}
1070
1071
1072/*
1073 * +-------------------------------------------------------+
1074 * | |
1075 * | caching support
1076 * | |
1077 * +-------------------------------------------------------+
1078 */
1079
1080static void set_cache_value(const char *name, apr_time_t t, char *key,
1081 char *val)
1082{
1083 cachedmap *map;
1084
1085 if (cachep) {
1086#if APR_HAS_THREADS
1088#endif
1090
1091 if (!map) {
1092 apr_pool_t *p;
1093
1095#if APR_HAS_THREADS
1097#endif
1098 return;
1099 }
1100 apr_pool_tag(p, "rewrite_cachedmap");
1101
1102 map = apr_palloc(cachep->pool, sizeof(cachedmap));
1103 map->pool = p;
1104 map->entries = apr_hash_make(map->pool);
1105 map->mtime = t;
1106
1108 }
1109 else if (map->mtime != t) {
1110 apr_pool_clear(map->pool);
1111 map->entries = apr_hash_make(map->pool);
1112 map->mtime = t;
1113 }
1114
1115 /* Now we should have a valid map->entries hash, where we
1116 * can store our value.
1117 *
1118 * We need to copy the key and the value into OUR pool,
1119 * so that we don't leave it during the r->pool cleanup.
1120 */
1121 apr_hash_set(map->entries,
1123 apr_pstrdup(map->pool, val));
1124
1125#if APR_HAS_THREADS
1127#endif
1128 }
1129
1130 return;
1131}
1132
1133static char *get_cache_value(const char *name, apr_time_t t, char *key,
1134 apr_pool_t *p)
1135{
1136 cachedmap *map;
1137 char *val = NULL;
1138
1139 if (cachep) {
1140#if APR_HAS_THREADS
1142#endif
1144
1145 if (map) {
1146 /* if this map is outdated, forget it. */
1147 if (map->mtime != t) {
1148 apr_pool_clear(map->pool);
1149 map->entries = apr_hash_make(map->pool);
1150 map->mtime = t;
1151 }
1152 else {
1154 if (val) {
1155 /* copy the cached value into the supplied pool,
1156 * where it belongs (r->pool usually)
1157 */
1158 val = apr_pstrdup(p, val);
1159 }
1160 }
1161 }
1162
1163#if APR_HAS_THREADS
1165#endif
1166 }
1167
1168 return val;
1169}
1170
1172{
1173 cachep = apr_palloc(p, sizeof(cache));
1175 cachep = NULL; /* turns off cache */
1176 return 0;
1177 }
1178 apr_pool_tag(cachep->pool, "rewrite_cachep");
1179
1181#if APR_HAS_THREADS
1183#endif
1184
1185 return 1;
1186}
1187
1188
1189/*
1190 * +-------------------------------------------------------+
1191 * | |
1192 * | Map Functions
1193 * | |
1194 * +-------------------------------------------------------+
1195 */
1196
1197/*
1198 * General Note: key is already a fresh string, created (expanded) just
1199 * for the purpose to be passed in here. So one can modify key itself.
1200 */
1201
1203{
1205
1206 return key;
1207}
1208
1210{
1212
1213 return key;
1214}
1215
1217{
1218 return ap_escape_uri(r->pool, key);
1219}
1220
1222{
1224
1225 return key;
1226}
1227
1229{
1230 char *p = value;
1231 unsigned n = 1;
1232
1233 /* count number of distinct values */
1234 while ((p = ap_strchr(p, '|')) != NULL) {
1235 ++n;
1236 ++p;
1237 }
1238
1239 if (n > 1) {
1240 n = ap_random_pick(1, n);
1241
1242 /* extract it from the whole string */
1243 while (--n && (value = ap_strchr(value, '|')) != NULL) {
1244 ++value;
1245 }
1246
1247 if (value) { /* should not be NULL, but ... */
1248 p = ap_strchr(value, '|');
1249 if (p) {
1250 *p = '\0';
1251 }
1252 }
1253 }
1254
1255 return value;
1256}
1257
1258/* child process code */
1260 const char *desc)
1261{
1262 ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, APLOGNO(00653) "%s", desc);
1263}
1264
1266 const char *progname, char **argv,
1267 const char *user, const char *group,
1268 apr_file_t **fpout,
1269 apr_file_t **fpin)
1270{
1272 apr_procattr_t *procattr;
1274
1275 if ( APR_SUCCESS == (rc=apr_procattr_create(&procattr, p))
1278 && APR_SUCCESS == (rc=apr_procattr_dir_set(procattr,
1280 && (!user || APR_SUCCESS == (rc=apr_procattr_user_set(procattr, user, "")))
1281 && (!group || APR_SUCCESS == (rc=apr_procattr_group_set(procattr, group)))
1285 && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) {
1286
1287 procnew = apr_pcalloc(p, sizeof(*procnew));
1288 rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL,
1289 procattr, p);
1290
1291 if (rc == APR_SUCCESS) {
1293
1294 if (fpin) {
1295 (*fpin) = procnew->in;
1296 }
1297
1298 if (fpout) {
1299 (*fpout) = procnew->out;
1300 }
1301 }
1302 }
1303
1304 return (rc);
1305}
1306
1308{
1309 rewrite_server_conf *conf;
1312
1313 conf = ap_get_module_config(s->module_config, &rewrite_module);
1314
1315 /* If the engine isn't turned on,
1316 * don't even try to do anything.
1317 */
1318 if (conf->state == ENGINE_DISABLED) {
1319 return APR_SUCCESS;
1320 }
1321
1322 for (hi = apr_hash_first(p, conf->rewritemaps); hi; hi = apr_hash_next(hi)){
1323 apr_file_t *fpin = NULL;
1324 apr_file_t *fpout = NULL;
1325 rewritemap_entry *map;
1326 void *val;
1327
1329 map = val;
1330
1331 if (map->type != MAPTYPE_PRG) {
1332 continue;
1333 }
1334 if (!(map->argv[0]) || !*(map->argv[0]) || map->fpin || map->fpout) {
1335 continue;
1336 }
1337
1338 rc = rewritemap_program_child(p, map->argv[0], map->argv,
1339 map->user, map->group,
1340 &fpout, &fpin);
1341 if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) {
1343 "mod_rewrite: could not start RewriteMap "
1344 "program %s", map->checkfile);
1345 return rc;
1346 }
1347 map->fpin = fpin;
1348 map->fpout = fpout;
1349 }
1350
1351 return APR_SUCCESS;
1352}
1353
1354
1355/*
1356 * +-------------------------------------------------------+
1357 * | |
1358 * | Lookup functions
1359 * | |
1360 * +-------------------------------------------------------+
1361 */
1362
1363static char *lookup_map_txtfile(request_rec *r, const char *file, char *key)
1364{
1365 apr_file_t *fp = NULL;
1366 char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
1367 char *value, *keylast;
1368 apr_status_t rv;
1369
1371 r->pool)) != APR_SUCCESS)
1372 {
1374 "mod_rewrite: can't open text RewriteMap file %s", file);
1375 return NULL;
1376 }
1377
1378 keylast = key + strlen(key);
1379 value = NULL;
1380 while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
1381 char *p, *c;
1382
1383 /* ignore comments and lines starting with whitespaces */
1384 if (*line == '#' || apr_isspace(*line)) {
1385 continue;
1386 }
1387
1388 p = line;
1389 c = key;
1390 while (c < keylast && *p == *c && !apr_isspace(*p)) {
1391 ++p;
1392 ++c;
1393 }
1394
1395 /* key doesn't match - ignore. */
1396 if (c != keylast || !apr_isspace(*p)) {
1397 continue;
1398 }
1399
1400 /* jump to the value */
1401 while (apr_isspace(*p)) {
1402 ++p;
1403 }
1404
1405 /* no value? ignore */
1406 if (!*p) {
1407 continue;
1408 }
1409
1410 /* extract the value and return. */
1411 c = p;
1412 while (*p && !apr_isspace(*p)) {
1413 ++p;
1414 }
1415 value = apr_pstrmemdup(r->pool, c, p - c);
1416 break;
1417 }
1418 apr_file_close(fp);
1419
1420 return value;
1421}
1422
1423static char *lookup_map_dbmfile(request_rec *r, const char *file,
1424 const char *dbmtype, char *key)
1425{
1426#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
1427 const apr_dbm_driver_t *driver;
1428 const apu_err_t *err;
1429#endif
1430 apr_dbm_t *dbmfp = NULL;
1433 char *value;
1434 apr_status_t rv;
1435
1436#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
1437 if ((rv = apr_dbm_get_driver(&driver, dbmtype, &err,
1438 r->pool)) != APR_SUCCESS) {
1440 "mod_rewrite: can't load DBM library '%s': %s",
1441 err->reason, err->msg);
1442 return NULL;
1443 }
1447 "mod_rewrite: can't open DBM RewriteMap %s", file);
1448 return NULL;
1449 }
1450#else
1451 if ((rv = apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY,
1453 {
1455 "mod_rewrite: can't open DBM RewriteMap %s", file);
1456 return NULL;
1457 }
1458#endif
1459
1460 dbmkey.dptr = key;
1461 dbmkey.dsize = strlen(key);
1462
1463 if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) {
1464 value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize);
1465 }
1466 else {
1467 value = NULL;
1468 }
1469
1471
1472 return value;
1473}
1474static char *lookup_map_dbd(request_rec *r, char *key, const char *label)
1475{
1476 apr_status_t rv;
1478 const char *errmsg;
1481 char *ret = NULL;
1482 int n = 0;
1483 ap_dbd_t *db = dbd_acquire(r);
1484
1485 if (db == NULL) {
1487 "rewritemap: No db handle available! "
1488 "Check your database access");
1489 return NULL;
1490 }
1491
1493
1494 rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res,
1495 stmt, 0, key, NULL);
1496 if (rv != 0) {
1497 errmsg = apr_dbd_error(db->driver, db->handle, rv);
1499 "rewritemap: error %s querying for %s", errmsg, key);
1500 return NULL;
1501 }
1502 while ((rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1)) == 0) {
1503 ++n;
1504 if (ret == NULL) {
1505 ret = apr_pstrdup(r->pool,
1506 apr_dbd_get_entry(db->driver, row, 0));
1507 }
1508 else {
1509 /* randomise crudely amongst multiple results */
1510 if ((double)rand() < (double)RAND_MAX/(double)n) {
1511 ret = apr_pstrdup(r->pool,
1512 apr_dbd_get_entry(db->driver, row, 0));
1513 }
1514 }
1515 }
1516 if (rv != -1) {
1517 errmsg = apr_dbd_error(db->driver, db->handle, rv);
1519 "rewritemap: error %s looking up %s", errmsg, key);
1520 }
1521 switch (n) {
1522 case 0:
1523 return NULL;
1524 case 1:
1525 return ret;
1526 default:
1527 /* what's a fair rewritelog level for this? */
1528 rewritelog((r, 3, NULL, "Multiple values found for %s", key));
1529 return ret;
1530 }
1531}
1532
1534 apr_file_t *fpout, char *key)
1535{
1536 char *buf;
1537 char c;
1539 apr_status_t rv;
1540 const char *eol = APR_EOL_STR;
1541 apr_size_t eolc = 0;
1542 int found_nl = 0;
1544
1545#ifndef NO_WRITEV
1546 struct iovec iova[2];
1548#endif
1549
1550 /* when `RewriteEngine off' was used in the per-server
1551 * context then the rewritemap-programs were not spawned.
1552 * In this case using such a map (usually in per-dir context)
1553 * is useless because it is not available.
1554 *
1555 * newlines in the key leave bytes in the pipe and cause
1556 * bad things to happen (next map lookup will use the chars
1557 * after the \n instead of the new key etc etc - in other words,
1558 * the Rewritemap falls out of sync with the requests).
1559 */
1560 if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) {
1561 return NULL;
1562 }
1563
1564 /* take the lock */
1567 if (rv != APR_SUCCESS) {
1569 "apr_global_mutex_lock(rewrite_mapr_lock_acquire) "
1570 "failed");
1571 return NULL; /* Maybe this should be fatal? */
1572 }
1573 }
1574
1575 /* write out the request key */
1576#ifdef NO_WRITEV
1577 nbytes = strlen(key);
1578 /* XXX: error handling */
1580 nbytes = 1;
1581 apr_file_write_full(fpin, "\n", nbytes, NULL);
1582#else
1583 iova[0].iov_base = key;
1584 iova[0].iov_len = strlen(key);
1585 iova[1].iov_base = "\n";
1586 iova[1].iov_len = 1;
1587
1588 niov = 2;
1589 /* XXX: error handling */
1591#endif
1592
1594
1595 /* read in the response value */
1596 nbytes = 1;
1597 apr_file_read(fpout, &c, &nbytes);
1598 do {
1599 i = 0;
1600 while (nbytes == 1 && (i < REWRITE_PRG_MAP_BUF)) {
1601 if (c == eol[eolc]) {
1602 if (!eol[++eolc]) {
1603 /* remove eol from the buffer */
1604 --eolc;
1605 if (i < eolc) {
1606 curbuf->len -= eolc-i;
1607 i = 0;
1608 }
1609 else {
1610 i -= eolc;
1611 }
1612 ++found_nl;
1613 break;
1614 }
1615 }
1616
1617 /* only partial (invalid) eol sequence -> reset the counter */
1618 else if (eolc) {
1619 eolc = 0;
1620 }
1621
1622 /* catch binary mode, e.g. on Win32 */
1623 else if (c == '\n') {
1624 ++found_nl;
1625 break;
1626 }
1627
1628 buf[i++] = c;
1629 apr_file_read(fpout, &c, &nbytes);
1630 }
1631
1632 /* well, if there wasn't a newline yet, we need to read further */
1633 if (buflist || (nbytes == 1 && !found_nl)) {
1634 if (!buflist) {
1635 curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist));
1636 }
1637 else if (i) {
1638 curbuf->next = apr_palloc(r->pool, sizeof(*buflist));
1639 curbuf = curbuf->next;
1640
1641 }
1642 curbuf->next = NULL;
1643
1644 if (i) {
1645 curbuf->string = buf;
1646 curbuf->len = i;
1647 combined_len += i;
1649 }
1650
1651 if (nbytes == 1 && !found_nl) {
1652 continue;
1653 }
1654 }
1655
1656 break;
1657 } while (1);
1658
1659 /* concat the stuff */
1660 if (buflist) {
1661 char *p;
1662
1663 p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */
1664 while (buflist) {
1665 if (buflist->len) {
1666 memcpy(p, buflist->string, buflist->len);
1667 p += buflist->len;
1668 }
1669 buflist = buflist->next;
1670 }
1671 *p = '\0';
1672 i = combined_len;
1673 }
1674 else {
1675 buf[i] = '\0';
1676 }
1677
1678 /* give the lock back */
1681 if (rv != APR_SUCCESS) {
1683 "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) "
1684 "failed");
1685 return NULL; /* Maybe this should be fatal? */
1686 }
1687 }
1688
1689 /* catch the "failed" case */
1690 if (i == 4 && !strcasecmp(buf, "NULL")) {
1691 return NULL;
1692 }
1693
1694 return buf;
1695}
1696
1697/*
1698 * generic map lookup
1699 */
1700static char *lookup_map(request_rec *r, char *name, char *key)
1701{
1702 rewrite_server_conf *conf;
1704 char *value;
1706 apr_status_t rv;
1707
1708 /* get map configuration */
1709 conf = ap_get_module_config(r->server->module_config, &rewrite_module);
1711
1712 /* map doesn't exist */
1713 if (!s) {
1714 return NULL;
1715 }
1716
1717 switch (s->type) {
1718 /*
1719 * Text file map (perhaps random)
1720 */
1721 case MAPTYPE_RND:
1722 case MAPTYPE_TXT:
1723 rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
1724 if (rv != APR_SUCCESS) {
1726 "mod_rewrite: can't access text RewriteMap file %s",
1727 s->checkfile);
1728 return NULL;
1729 }
1730
1731 value = get_cache_value(s->cachename, st.mtime, key, r->pool);
1732 if (!value) {
1733 rewritelog((r, 6, NULL,
1734 "cache lookup FAILED, forcing new map lookup"));
1735
1736 value = lookup_map_txtfile(r, s->datafile, key);
1737 if (!value) {
1738 rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[txt] key=%s",
1739 name, key));
1740 set_cache_value(s->cachename, st.mtime, key, "");
1741 return NULL;
1742 }
1743
1744 rewritelog((r, 5, NULL,"map lookup OK: map=%s[txt] key=%s -> val=%s",
1745 name, key, value));
1746 set_cache_value(s->cachename, st.mtime, key, value);
1747 }
1748 else {
1749 rewritelog((r,5,NULL,"cache lookup OK: map=%s[txt] key=%s -> val=%s",
1750 name, key, value));
1751 }
1752
1753 if (s->type == MAPTYPE_RND && *value) {
1755 rewritelog((r, 5, NULL, "randomly chosen the subvalue `%s'",value));
1756 }
1757
1758 return *value ? value : NULL;
1759
1760 /*
1761 * DBM file map
1762 */
1763 case MAPTYPE_DBM:
1764 rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
1765 if (rv != APR_SUCCESS) {
1767 "mod_rewrite: can't access DBM RewriteMap file %s",
1768 s->checkfile);
1769 }
1770 else if(s->checkfile2 != NULL) {
1772
1773 rv = apr_stat(&st2, s->checkfile2, APR_FINFO_MIN, r->pool);
1774 if (rv != APR_SUCCESS) {
1776 "mod_rewrite: can't access DBM RewriteMap "
1777 "file %s", s->checkfile2);
1778 }
1779 else if(st2.mtime > st.mtime) {
1780 st.mtime = st2.mtime;
1781 }
1782 }
1783 if(rv != APR_SUCCESS) {
1784 return NULL;
1785 }
1786
1787 value = get_cache_value(s->cachename, st.mtime, key, r->pool);
1788 if (!value) {
1789 rewritelog((r, 6, NULL,
1790 "cache lookup FAILED, forcing new map lookup"));
1791
1792 value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key);
1793 if (!value) {
1794 rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s",
1795 name, key));
1796 set_cache_value(s->cachename, st.mtime, key, "");
1797 return NULL;
1798 }
1799
1800 rewritelog((r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> "
1801 "val=%s", name, key, value));
1802
1803 set_cache_value(s->cachename, st.mtime, key, value);
1804 return value;
1805 }
1806
1807 rewritelog((r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s",
1808 name, key, value));
1809 return *value ? value : NULL;
1810
1811 /*
1812 * SQL map without cache
1813 */
1814 case MAPTYPE_DBD:
1815 value = lookup_map_dbd(r, key, s->dbdq);
1816 if (!value) {
1817 rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
1818 name, key));
1819 return NULL;
1820 }
1821
1822 rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
1823 name, key, value));
1824
1825 return value;
1826
1827 /*
1828 * SQL map with cache
1829 */
1830 case MAPTYPE_DBD_CACHE:
1831 value = get_cache_value(s->cachename, 0, key, r->pool);
1832 if (!value) {
1833 rewritelog((r, 6, NULL,
1834 "cache lookup FAILED, forcing new map lookup"));
1835
1836 value = lookup_map_dbd(r, key, s->dbdq);
1837 if (!value) {
1838 rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
1839 name, key));
1840 set_cache_value(s->cachename, 0, key, "");
1841 return NULL;
1842 }
1843
1844 rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
1845 name, key, value));
1846
1847 set_cache_value(s->cachename, 0, key, value);
1848 return value;
1849 }
1850
1851 rewritelog((r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s",
1852 name, key, value));
1853 return *value ? value : NULL;
1854
1855 /*
1856 * Program file map
1857 */
1858 case MAPTYPE_PRG:
1859 value = lookup_map_program(r, s->fpin, s->fpout, key);
1860 if (!value) {
1861 rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
1862 key));
1863 return NULL;
1864 }
1865
1866 rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
1867 name, key, value));
1868 return value;
1869
1870 /*
1871 * Internal Map
1872 */
1873 case MAPTYPE_INT:
1874 value = s->func(r, key);
1875 if (!value) {
1876 rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
1877 key));
1878 return NULL;
1879 }
1880
1881 rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
1882 name, key, value));
1883 return value;
1884 }
1885
1886 return NULL;
1887}
1888
1889/*
1890 * lookup a HTTP header and set VARY note
1891 */
1892static const char *lookup_header(const char *name, rewrite_ctx *ctx)
1893{
1894 const char *val = apr_table_get(ctx->r->headers_in, name);
1895
1896 /* Skip the 'Vary: Host' header combination
1897 * as indicated in rfc7231 section-7.1.4
1898 */
1899 if (val && strcasecmp(name, "Host") != 0) {
1900 ctx->vary_this = ctx->vary_this
1901 ? apr_pstrcat(ctx->r->pool, ctx->vary_this, ", ",
1902 name, NULL)
1903 : apr_pstrdup(ctx->r->pool, name);
1904 }
1905
1906 return val;
1907}
1908
1909/*
1910 * lookahead helper function
1911 * Determine the correct URI path in perdir context
1912 */
1913static APR_INLINE const char *la_u(rewrite_ctx *ctx)
1914{
1915 rewrite_perdir_conf *conf;
1916
1917 if (*ctx->uri == '/') {
1918 return ctx->uri;
1919 }
1920
1921 conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module);
1922
1923 return apr_pstrcat(ctx->r->pool, conf->baseurl
1924 ? conf->baseurl : conf->directory,
1925 ctx->uri, NULL);
1926}
1927
1928/*
1929 * generic variable lookup
1930 */
1931static char *lookup_variable(char *var, rewrite_ctx *ctx)
1932{
1933 const char *result;
1934 request_rec *r = ctx->r;
1935 apr_size_t varlen = strlen(var);
1936
1937 /* fast exit */
1938 if (varlen < 4) {
1939 return "";
1940 }
1941
1942 result = NULL;
1943
1944 /* fast tests for variable length variables (sic) first */
1945 if (var[3] == ':') {
1946 if (var[4] && !strncasecmp(var, "ENV", 3)) {
1947 var += 4;
1949
1950 if (!result) {
1952 }
1953 if (!result) {
1954 result = getenv(var);
1955 }
1956 }
1957 else if (var[4] && !strncasecmp(var, "SSL", 3)) {
1959 var + 4);
1960 }
1961 }
1962 else if (var[4] == ':') {
1963 if (var[5]) {
1964 request_rec *rr;
1965 const char *path;
1966
1967 if (!strncasecmp(var, "HTTP", 4)) {
1969 }
1970 else if (!strncasecmp(var, "LA-U", 4)) {
1971 if (ctx->uri && subreq_ok(r)) {
1972 path = ctx->perdir ? la_u(ctx) : ctx->uri;
1974 ctx->r = rr;
1976 ctx->r = r;
1978
1979 rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
1980 "-> val=%s", path, var+5, result));
1981
1982 return (char *)result;
1983 }
1984 }
1985 else if (!strncasecmp(var, "LA-F", 4)) {
1986 if (ctx->uri && subreq_ok(r)) {
1987 path = ctx->uri;
1988 if (ctx->perdir && *path == '/') {
1989 /* sigh, the user wants a file based subrequest, but
1990 * we can't do one, since we don't know what the file
1991 * path is! In this case behave like LA-U.
1992 */
1994 }
1995 else {
1996 if (ctx->perdir) {
1997 rewrite_perdir_conf *conf;
1998
2000 &rewrite_module);
2001
2002 path = apr_pstrcat(r->pool, conf->directory, path,
2003 NULL);
2004 }
2005
2007 }
2008
2009 ctx->r = rr;
2011 ctx->r = r;
2013
2014 rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
2015 "-> val=%s", path, var+5, result));
2016
2017 return (char *)result;
2018 }
2019 }
2020 }
2021 }
2022
2023 /* well, do it the hard way */
2024 else {
2026
2027 /* can't do this above, because of the getenv call */
2029
2030 switch (varlen) {
2031 case 4:
2032 if (!strcmp(var, "TIME")) {
2034 result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
2035 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
2037 rewritelog((r, 1, ctx->perdir, "RESULT='%s'", result));
2038 return (char *)result;
2039 }
2040 else if (!strcmp(var, "IPV6")) {
2041 int flag = FALSE;
2042#if APR_HAVE_IPV6
2044 flag = (addr->family == AF_INET6 &&
2046 rewritelog((r, 1, ctx->perdir, "IPV6='%s'", flag ? "on" : "off"));
2047#else
2048 rewritelog((r, 1, ctx->perdir, "IPV6='off' (IPv6 is not enabled)"));
2049#endif
2050 result = (flag ? "on" : "off");
2051 }
2052 break;
2053
2054 case 5:
2055 if (!strcmp(var, "HTTPS")) {
2057 return apr_pstrdup(r->pool, flag ? "on" : "off");
2058 }
2059 break;
2060
2061 case 8:
2062 switch (var[6]) {
2063 case 'A':
2064 if (!strcmp(var, "TIME_DAY")) {
2066 return apr_psprintf(r->pool, "%02d", tm.tm_mday);
2067 }
2068 break;
2069
2070 case 'E':
2071 if (!strcmp(var, "TIME_SEC")) {
2073 return apr_psprintf(r->pool, "%02d", tm.tm_sec);
2074 }
2075 break;
2076
2077 case 'I':
2078 if (!strcmp(var, "TIME_MIN")) {
2080 return apr_psprintf(r->pool, "%02d", tm.tm_min);
2081 }
2082 break;
2083
2084 case 'O':
2085 if (!strcmp(var, "TIME_MON")) {
2087 return apr_psprintf(r->pool, "%02d", tm.tm_mon+1);
2088 }
2089 break;
2090 }
2091 break;
2092
2093 case 9:
2094 switch (var[7]) {
2095 case 'A':
2096 if (var[8] == 'Y' && !strcmp(var, "TIME_WDAY")) {
2098 return apr_psprintf(r->pool, "%d", tm.tm_wday);
2099 }
2100 else if (!strcmp(var, "TIME_YEAR")) {
2102 return apr_psprintf(r->pool, "%04d", tm.tm_year+1900);
2103 }
2104 break;
2105
2106 case 'E':
2107 if (!strcmp(var, "IS_SUBREQ")) {
2108 result = (r->main ? "true" : "false");
2109 }
2110 break;
2111
2112 case 'F':
2113 if (!strcmp(var, "PATH_INFO")) {
2114 result = r->path_info;
2115 }
2116 break;
2117
2118 case 'P':
2119 if (!strcmp(var, "AUTH_TYPE")) {
2121 }
2122 break;
2123
2124 case 'S':
2125 if (!strcmp(var, "HTTP_HOST")) {
2126 result = lookup_header("Host", ctx);
2127 }
2128 break;
2129
2130 case 'U':
2131 if (!strcmp(var, "TIME_HOUR")) {
2133 return apr_psprintf(r->pool, "%02d", tm.tm_hour);
2134 }
2135 break;
2136 }
2137 break;
2138
2139 case 11:
2140 switch (var[8]) {
2141 case 'A':
2142 if (!strcmp(var, "SERVER_NAME")) {
2144 }
2145 break;
2146
2147 case 'D':
2148 if (*var == 'R' && !strcmp(var, "REMOTE_ADDR")) {
2150 }
2151 else if (!strcmp(var, "SERVER_ADDR")) {
2153 }
2154 break;
2155
2156 case 'E':
2157 if (*var == 'H' && !strcmp(var, "HTTP_ACCEPT")) {
2158 result = lookup_header("Accept", ctx);
2159 }
2160 else if (!strcmp(var, "THE_REQUEST")) {
2161 result = r->the_request;
2162 }
2163 break;
2164
2165 case 'I':
2166 if (!strcmp(var, "API_VERSION")) {
2167 return apr_psprintf(r->pool, "%d:%d",
2170 }
2171 break;
2172
2173 case 'K':
2174 if (!strcmp(var, "HTTP_COOKIE")) {
2175 result = lookup_header("Cookie", ctx);
2176 }
2177 break;
2178
2179 case 'O':
2180 if (*var == 'S' && !strcmp(var, "SERVER_PORT")) {
2181 return apr_psprintf(r->pool, "%u", ap_get_server_port(r));
2182 }
2183 else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) {
2185 REMOTE_NAME, NULL);
2186 }
2187 else if (!strcmp(var, "REMOTE_PORT")) {
2188 return apr_itoa(r->pool, r->useragent_addr->port);
2189 }
2190 break;
2191
2192 case 'S':
2193 if (*var == 'R' && !strcmp(var, "REMOTE_USER")) {
2194 result = r->user;
2195 }
2196 else if (!strcmp(var, "SCRIPT_USER")) {
2197 result = "<unknown>";
2198 if (r->finfo.valid & APR_FINFO_USER) {
2199 apr_uid_name_get((char **)&result, r->finfo.user,
2200 r->pool);
2201 }
2202 }
2203 break;
2204
2205 case 'U':
2206 if (!strcmp(var, "REQUEST_URI")) {
2207 result = r->uri;
2208 }
2209 break;
2210 }
2211 break;
2212
2213 case 12:
2214 switch (var[3]) {
2215 case 'I':
2216 if (!strcmp(var, "SCRIPT_GROUP")) {
2217 result = "<unknown>";
2218 if (r->finfo.valid & APR_FINFO_GROUP) {
2219 apr_gid_name_get((char **)&result, r->finfo.group,
2220 r->pool);
2221 }
2222 }
2223 break;
2224
2225 case 'O':
2226 if (!strcmp(var, "REMOTE_IDENT")) {
2228 }
2229 break;
2230
2231 case 'P':
2232 if (!strcmp(var, "HTTP_REFERER")) {
2233 result = lookup_header("Referer", ctx);
2234 }
2235 break;
2236
2237 case 'R':
2238 if (!strcmp(var, "QUERY_STRING")) {
2239 result = r->args;
2240 }
2241 break;
2242
2243 case 'V':
2244 if (!strcmp(var, "SERVER_ADMIN")) {
2246 }
2247 break;
2248 }
2249 break;
2250
2251 case 13:
2252 if (!strcmp(var, "DOCUMENT_ROOT")) {
2254 }
2255 break;
2256
2257 case 14:
2258 if (*var == 'H' && !strcmp(var, "HTTP_FORWARDED")) {
2259 result = lookup_header("Forwarded", ctx);
2260 }
2261 else if (*var == 'C' && !strcmp(var, "CONTEXT_PREFIX")) {
2263 }
2264 else if (var[8] == 'M' && !strcmp(var, "REQUEST_METHOD")) {
2265 result = r->method;
2266 }
2267 else if (!strcmp(var, "REQUEST_SCHEME")) {
2269 }
2270 break;
2271
2272 case 15:
2273 switch (var[7]) {
2274 case 'E':
2275 if (!strcmp(var, "HTTP_USER_AGENT")) {
2276 result = lookup_header("User-Agent", ctx);
2277 }
2278 break;
2279
2280 case 'F':
2281 if (!strcmp(var, "SCRIPT_FILENAME")) {
2282 result = r->filename; /* same as request_filename (16) */
2283 }
2284 break;
2285
2286 case 'P':
2287 if (!strcmp(var, "SERVER_PROTOCOL")) {
2288 result = r->protocol;
2289 }
2290 break;
2291
2292 case 'S':
2293 if (!strcmp(var, "SERVER_SOFTWARE")) {
2295 }
2296 break;
2297 }
2298 break;
2299
2300 case 16:
2301 if (*var == 'C' && !strcmp(var, "CONN_REMOTE_ADDR")) {
2303 }
2304 else if (!strcmp(var, "REQUEST_FILENAME")) {
2305 result = r->filename; /* same as script_filename (15) */
2306 }
2307 break;
2308
2309 case 21:
2310 if (!strcmp(var, "HTTP_PROXY_CONNECTION")) {
2311 result = lookup_header("Proxy-Connection", ctx);
2312 }
2313 else if (!strcmp(var, "CONTEXT_DOCUMENT_ROOT")) {
2315 }
2316 break;
2317 }
2318 }
2319
2320 return apr_pstrdup(r->pool, result ? result : "");
2321}
2322
2323
2324/*
2325 * +-------------------------------------------------------+
2326 * | |
2327 * | Expansion functions
2328 * | |
2329 * +-------------------------------------------------------+
2330 */
2331
2332/*
2333 * Bracketed expression handling
2334 * s points after the opening bracket
2335 */
2337{
2338 unsigned depth;
2339
2340 for (depth = 1; *s; ++s) {
2341 if (*s == RIGHT_CURLY && --depth == 0) {
2342 return s;
2343 }
2344 else if (*s == LEFT_CURLY) {
2345 ++depth;
2346 }
2347 }
2348
2349 return NULL;
2350}
2351
2352static APR_INLINE char *find_char_in_curlies(char *s, int c)
2353{
2354 unsigned depth;
2355
2356 for (depth = 1; *s; ++s) {
2357 if (*s == c && depth == 1) {
2358 return s;
2359 }
2360 else if (*s == RIGHT_CURLY && --depth == 0) {
2361 return NULL;
2362 }
2363 else if (*s == LEFT_CURLY) {
2364 ++depth;
2365 }
2366 }
2367
2368 return NULL;
2369}
2370
2371/* perform all the expansions on the input string
2372 * putting the result into a new string
2373 *
2374 * for security reasons this expansion must be performed in a
2375 * single pass, otherwise an attacker can arrange for the result
2376 * of an earlier expansion to include expansion specifiers that
2377 * are interpreted by a later expansion, producing results that
2378 * were not intended by the administrator.
2379 */
2380static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
2381{
2382 result_list *result, *current;
2384 unsigned spc = 0;
2385 apr_size_t span, inputlen, outlen;
2386 char *p, *c;
2387 apr_pool_t *pool = ctx->r->pool;
2388
2389 span = strcspn(input, "\\$%");
2390 inputlen = strlen(input);
2391
2392 /* fast exit */
2393 if (inputlen == span) {
2394 return apr_pstrmemdup(pool, input, inputlen);
2395 }
2396
2397 /* well, actually something to do */
2398 result = current = &(sresult[spc++]);
2399
2400 p = input + span;
2401 current->next = NULL;
2402 current->string = input;
2403 current->len = span;
2404 outlen = span;
2405
2406 /* loop for specials */
2407 do {
2408 /* prepare next entry */
2409 if (current->len) {
2410 current->next = (spc < SMALL_EXPANSION)
2411 ? &(sresult[spc++])
2413 sizeof(result_list));
2414 current = current->next;
2415 current->next = NULL;
2416 current->len = 0;
2417 }
2418
2419 /* escaped character */
2420 if (*p == '\\') {
2421 current->len = 1;
2422 ++outlen;
2423 if (!p[1]) {
2424 current->string = p;
2425 break;
2426 }
2427 else {
2428 current->string = ++p;
2429 ++p;
2430 }
2431 }
2432
2433 /* variable or map lookup */
2434 else if (p[1] == '{') {
2435 char *endp;
2436
2437 endp = find_closing_curly(p+2);
2438 if (!endp) {
2439 current->len = 2;
2440 current->string = p;
2441 outlen += 2;
2442 p += 2;
2443 }
2444
2445 /* variable lookup */
2446 else if (*p == '%') {
2447 p = lookup_variable(apr_pstrmemdup(pool, p+2, endp-p-2), ctx);
2448
2449 span = strlen(p);
2450 current->len = span;
2451 current->string = p;
2452 outlen += span;
2453 p = endp + 1;
2454 }
2455
2456 /* map lookup */
2457 else { /* *p == '$' */
2458 char *key;
2459
2460 /*
2461 * To make rewrite maps useful, the lookup key and
2462 * default values must be expanded, so we make
2463 * recursive calls to do the work. For security
2464 * reasons we must never expand a string that includes
2465 * verbatim data from the network. The recursion here
2466 * isn't a problem because the result of expansion is
2467 * only passed to lookup_map() so it cannot be
2468 * re-expanded, only re-looked-up. Another way of
2469 * looking at it is that the recursion is entirely
2470 * driven by the syntax of the nested curly brackets.
2471 */
2472
2473 key = find_char_in_curlies(p+2, ':');
2474 if (!key) {
2475 current->len = 2;
2476 current->string = p;
2477 outlen += 2;
2478 p += 2;
2479 }
2480 else {
2481 char *map, *dflt;
2482
2483 map = apr_pstrmemdup(pool, p+2, endp-p-2);
2484 key = map + (key-p-2);
2485 *key++ = '\0';
2486 dflt = find_char_in_curlies(key, '|');
2487 if (dflt) {
2488 *dflt++ = '\0';
2489 }
2490
2491 /* reuse of key variable as result */
2492 key = lookup_map(ctx->r, map, do_expand(key, ctx, entry));
2493
2494 if (!key && dflt && *dflt) {
2495 key = do_expand(dflt, ctx, entry);
2496 }
2497
2498 if (key) {
2499 span = strlen(key);
2500 current->len = span;
2501 current->string = key;
2502 outlen += span;
2503 }
2504
2505 p = endp + 1;
2506 }
2507 }
2508 }
2509
2510 /* backreference */
2511 else if (apr_isdigit(p[1])) {
2512 int n = p[1] - '0';
2513 backrefinfo *bri = (*p == '$') ? &ctx->briRR : &ctx->briRC;
2514
2515 /* see ap_pregsub() in server/util.c */
2516 if (bri->source && n < AP_MAX_REG_MATCH
2517 && bri->regmatch[n].rm_eo > bri->regmatch[n].rm_so) {
2518 span = bri->regmatch[n].rm_eo - bri->regmatch[n].rm_so;
2519 if (entry && (entry->flags & RULEFLAG_ESCAPEBACKREF)) {
2520 /* escape the backreference */
2521 char *tmp2, *tmp;
2522 tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span);
2523 tmp2 = escape_backref(pool, tmp, entry->escapes, entry->noescapes,
2524 entry->flags);
2525 rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'",
2526 tmp, tmp2));
2527
2528 current->len = span = strlen(tmp2);
2529 current->string = tmp2;
2530 } else {
2531 current->len = span;
2532 current->string = bri->source + bri->regmatch[n].rm_so;
2533 }
2534
2535 outlen += span;
2536 }
2537
2538 p += 2;
2539 }
2540
2541 /* not for us, just copy it */
2542 else {
2543 current->len = 1;
2544 current->string = p++;
2545 ++outlen;
2546 }
2547
2548 /* check the remainder */
2549 if (*p && (span = strcspn(p, "\\$%")) > 0) {
2550 if (current->len) {
2551 current->next = (spc < SMALL_EXPANSION)
2552 ? &(sresult[spc++])
2554 sizeof(result_list));
2555 current = current->next;
2556 current->next = NULL;
2557 }
2558
2559 current->len = span;
2560 current->string = p;
2561 p += span;
2562 outlen += span;
2563 }
2564
2565 } while (p < input+inputlen);
2566
2567 /* assemble result */
2568 c = p = apr_palloc(pool, outlen + 1); /* don't forget the \0 */
2569 do {
2570 if (result->len) {
2571 ap_assert(c+result->len <= p+outlen); /* XXX: can be removed after
2572 * extensive testing and
2573 * review
2574 */
2575 memcpy(c, result->string, result->len);
2576 c += result->len;
2577 }
2578 result = result->next;
2579 } while (result);
2580
2581 p[outlen] = '\0';
2582
2583 return p;
2584}
2585
2586/*
2587 * perform all the expansions on the environment variables
2588 */
2590{
2591 char *name, *val;
2592
2593 while (env) {
2594 name = do_expand(env->data, ctx, NULL);
2595 if (*name == '!') {
2596 name++;
2597 apr_table_unset(ctx->r->subprocess_env, name);
2598 rewritelog((ctx->r, 5, NULL, "unsetting env variable '%s'", name));
2599 }
2600 else {
2601 if ((val = ap_strchr(name, ':')) != NULL) {
2602 *val++ = '\0';
2603 } else {
2604 val = "";
2605 }
2606
2607 apr_table_set(ctx->r->subprocess_env, name, val);
2608 rewritelog((ctx->r, 5, NULL, "setting env variable '%s' to '%s'",
2609 name, val));
2610 }
2611
2612 env = env->next;
2613 }
2614
2615 return;
2616}
2617
2618/*
2619 * perform all the expansions on the cookies
2620 *
2621 * TODO: use cached time similar to how logging does it
2622 */
2623static void add_cookie(request_rec *r, char *s)
2624{
2625 char *var;
2626 char *val;
2627 char *domain;
2628 char *expires;
2629 char *path;
2630 char *secure;
2631 char *httponly;
2632 char *samesite;
2633
2634 char *tok_cntx;
2635 char *cookie;
2636 /* long-standing default, but can't use ':' in a cookie */
2637 const char *sep = ":";
2638
2639 /* opt-in to ; separator if first character is a ; */
2640 if (s && *s == ';') {
2641 sep = ";";
2642 s++;
2643 }
2644
2645 var = apr_strtok(s, sep, &tok_cntx);
2647 domain = apr_strtok(NULL, sep, &tok_cntx);
2648
2649 if (var && val && domain) {
2650 request_rec *rmain = r;
2651 char *notename;
2652 void *data;
2653
2654 while (rmain->main) {
2655 rmain = rmain->main;
2656 }
2657
2658 notename = apr_pstrcat(rmain->pool, var, "_rewrite", NULL);
2660 if (!data) {
2661 char *exp_time = NULL;
2662
2663 expires = apr_strtok(NULL, sep, &tok_cntx);
2664 path = expires ? apr_strtok(NULL, sep, &tok_cntx) : NULL;
2665 secure = path ? apr_strtok(NULL, sep, &tok_cntx) : NULL;
2666 httponly = secure ? apr_strtok(NULL, sep, &tok_cntx) : NULL;
2667 samesite = httponly ? apr_strtok(NULL, sep, &tok_cntx) : NULL;
2668
2669 if (expires) {
2671 long exp_min;
2672
2673 exp_min = atol(expires);
2674 if (exp_min) {
2676 + apr_time_from_sec((60 * exp_min)));
2677 exp_time = apr_psprintf(r->pool, "%s, %.2d-%s-%.4d "
2678 "%.2d:%.2d:%.2d GMT",
2679 apr_day_snames[tms.tm_wday],
2680 tms.tm_mday,
2681 apr_month_snames[tms.tm_mon],
2682 tms.tm_year+1900,
2683 tms.tm_hour, tms.tm_min, tms.tm_sec);
2684 }
2685 }
2686
2687 cookie = apr_pstrcat(rmain->pool,
2688 var, "=", val,
2689 "; path=", path ? path : "/",
2690 "; domain=", domain,
2691 expires ? (exp_time ? "; expires=" : "")
2692 : NULL,
2693 expires ? (exp_time ? exp_time : "")
2694 : NULL,
2695 (secure && (!ap_cstr_casecmp(secure, "true")
2696 || !strcmp(secure, "1")
2697 || !ap_cstr_casecmp(secure,
2698 "secure"))) ?
2699 "; secure" : NULL,
2700 (httponly && (!ap_cstr_casecmp(httponly, "true")
2701 || !strcmp(httponly, "1")
2703 "HttpOnly"))) ?
2704 "; HttpOnly" : NULL,
2705 NULL);
2706
2707 if (samesite && strcmp(samesite, "0") && ap_cstr_casecmp(samesite,"false")) {
2708 cookie = apr_pstrcat(rmain->pool, cookie, "; SameSite=",
2709 samesite, NULL);
2710 }
2711
2712 apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie);
2713 apr_pool_userdata_set("set", notename, NULL, rmain->pool);
2714 rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie));
2715 }
2716 else {
2717 rewritelog((rmain, 5, NULL, "skipping already set cookie '%s'",
2718 var));
2719 }
2720 }
2721
2722 return;
2723}
2724
2726{
2727 while (cookie) {
2728 add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL));
2729 cookie = cookie->next;
2730 }
2731
2732 return;
2733}
2734
2735#if APR_HAS_USER
2736/*
2737 * Expand tilde-paths (/~user) through Unix /etc/passwd
2738 * database information (or other OS-specific database)
2739 */
2740static char *expand_tildepaths(request_rec *r, char *uri)
2741{
2742 if (uri && *uri == '/' && uri[1] == '~') {
2743 char *p, *user;
2744
2745 p = user = uri + 2;
2746 while (*p && *p != '/') {
2747 ++p;
2748 }
2749
2750 if (p > user) {
2751 char *homedir;
2752
2753 user = apr_pstrmemdup(r->pool, user, p-user);
2754 if (apr_uid_homepath_get(&homedir, user, r->pool) == APR_SUCCESS) {
2755 if (*p) {
2756 /* reuse of user variable */
2757 user = homedir + strlen(homedir) - 1;
2758 if (user >= homedir && *user == '/') {
2759 *user = '\0';
2760 }
2761
2762 return apr_pstrcat(r->pool, homedir, p, NULL);
2763 }
2764 else {
2765 return homedir;
2766 }
2767 }
2768 }
2769 }
2770
2771 return uri;
2772}
2773#endif /* if APR_HAS_USER */
2774
2775
2776/*
2777 * +-------------------------------------------------------+
2778 * | |
2779 * | rewriting lockfile support
2780 * | |
2781 * +-------------------------------------------------------+
2782 */
2783
2785{
2787
2788 /* create the lockfile */
2791 if (rc != APR_SUCCESS) {
2792 return rc;
2793 }
2794
2795 return APR_SUCCESS;
2796}
2797
2799{
2800 /* destroy the rewritelock */
2804 }
2805 return APR_SUCCESS;
2806}
2807
2808
2809/*
2810 * +-------------------------------------------------------+
2811 * | |
2812 * | configuration directive handling
2813 * | |
2814 * +-------------------------------------------------------+
2815 */
2816
2817/*
2818 * own command line parser for RewriteRule and RewriteCond,
2819 * which doesn't have the '\\' problem.
2820 * (returns true on error)
2821 *
2822 * XXX: what an inclined parser. Seems we have to leave it so
2823 * for backwards compat. *sigh*
2824 */
2825static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3)
2826{
2827 char quote;
2828
2829 while (apr_isspace(*str)) {
2830 ++str;
2831 }
2832
2833 /*
2834 * determine first argument
2835 */
2836 quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
2837 *a1 = str;
2838
2839 for (; *str; ++str) {
2840 if ((apr_isspace(*str) && !quote) || (*str == quote)) {
2841 break;
2842 }
2843 if (*str == '\\' && apr_isspace(str[1])) {
2844 ++str;
2845 continue;
2846 }
2847 }
2848
2849 if (!*str) {
2850 return 1;
2851 }
2852 *str++ = '\0';
2853
2854 while (apr_isspace(*str)) {
2855 ++str;
2856 }
2857
2858 /*
2859 * determine second argument
2860 */
2861 quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
2862 *a2 = str;
2863
2864 for (; *str; ++str) {
2865 if ((apr_isspace(*str) && !quote) || (*str == quote)) {
2866 break;
2867 }
2868 if (*str == '\\' && apr_isspace(str[1])) {
2869 ++str;
2870 continue;
2871 }
2872 }
2873
2874 if (!*str) {
2875 *a3 = NULL; /* 3rd argument is optional */
2876 *a2_end = str;
2877 return 0;
2878 }
2879 *a2_end = str;
2880 *str++ = '\0';
2881
2882 while (apr_isspace(*str)) {
2883 ++str;
2884 }
2885
2886 if (!*str) {
2887 *a3 = NULL; /* 3rd argument is still optional */
2888 return 0;
2889 }
2890
2891 /*
2892 * determine third argument
2893 */
2894 quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
2895 *a3 = str;
2896 for (; *str; ++str) {
2897 if ((apr_isspace(*str) && !quote) || (*str == quote)) {
2898 break;
2899 }
2900 if (*str == '\\' && apr_isspace(str[1])) {
2901 ++str;
2902 continue;
2903 }
2904 }
2905 *str = '\0';
2906
2907 return 0;
2908}
2909
2911{
2913
2915
2916 a->state = ENGINE_DISABLED;
2917 a->options = OPTION_NONE;
2918 a->rewritemaps = apr_hash_make(p);
2919 a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry));
2920 a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry));
2921 a->server = s;
2922
2923 return (void *)a;
2924}
2925
2927{
2929
2931 sizeof(rewrite_server_conf));
2934
2935 a->state = (overrides->state_set == 0) ? base->state : overrides->state;
2936 a->state_set = overrides->state_set || base->state_set;
2937 a->options = (overrides->options_set == 0) ? base->options : overrides->options;
2938 a->options_set = overrides->options_set || base->options_set;
2939
2940 a->server = overrides->server;
2941
2942 if (a->options & OPTION_INHERIT ||
2943 (base->options & OPTION_INHERIT_DOWN &&
2944 !(a->options & OPTION_IGNORE_INHERIT))) {
2945 /*
2946 * local directives override
2947 * and anything else is inherited
2948 */
2949 a->rewritemaps = apr_hash_overlay(p, overrides->rewritemaps,
2950 base->rewritemaps);
2951 a->rewriteconds = apr_array_append(p, overrides->rewriteconds,
2952 base->rewriteconds);
2953 a->rewriterules = apr_array_append(p, overrides->rewriterules,
2954 base->rewriterules);
2955 }
2956 else if (a->options & OPTION_INHERIT_BEFORE ||
2957 (base->options & OPTION_INHERIT_DOWN_BEFORE &&
2958 !(a->options & OPTION_IGNORE_INHERIT))) {
2959 /*
2960 * local directives override
2961 * and anything else is inherited (preserving order)
2962 */
2963 a->rewritemaps = apr_hash_overlay(p, base->rewritemaps,
2964 overrides->rewritemaps);
2965 a->rewriteconds = apr_array_append(p, base->rewriteconds,
2966 overrides->rewriteconds);
2967 a->rewriterules = apr_array_append(p, base->rewriterules,
2968 overrides->rewriterules);
2969 }
2970 else {
2971 /*
2972 * local directives override
2973 * and anything else gets defaults
2974 */
2975 a->rewritemaps = overrides->rewritemaps;
2976 a->rewriteconds = overrides->rewriteconds;
2977 a->rewriterules = overrides->rewriterules;
2978 }
2979
2980 return (void *)a;
2981}
2982
2984{
2986
2988
2989 a->state = ENGINE_DISABLED;
2990 a->options = OPTION_NONE;
2991 a->baseurl = NULL;
2992 a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry));
2993 a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry));
2994
2995 if (path == NULL) {
2996 a->directory = NULL;
2997 }
2998 else {
2999 /* make sure it has a trailing slash */
3000 if (path[strlen(path)-1] == '/') {
3001 a->directory = apr_pstrdup(p, path);
3002 }
3003 else {
3004 a->directory = apr_pstrcat(p, path, "/", NULL);
3005 }
3006 }
3007
3008 return (void *)a;
3009}
3010
3012{
3014
3016 sizeof(rewrite_perdir_conf));
3019
3020 a->state = (overrides->state_set == 0) ? base->state : overrides->state;
3021 a->state_set = overrides->state_set || base->state_set;
3022 a->options = (overrides->options_set == 0) ? base->options : overrides->options;
3023 a->options_set = overrides->options_set || base->options_set;
3024
3025 if (a->options & OPTION_MERGEBASE) {
3026 a->baseurl = (overrides->baseurl_set == 0) ? base->baseurl : overrides->baseurl;
3027 a->baseurl_set = overrides->baseurl_set || base->baseurl_set;
3028 }
3029 else {
3030 a->baseurl = overrides->baseurl;
3031 }
3032
3033 a->directory = overrides->directory;
3034
3035 if (a->options & OPTION_INHERIT ||
3036 (base->options & OPTION_INHERIT_DOWN &&
3037 !(a->options & OPTION_IGNORE_INHERIT))) {
3038 a->rewriteconds = apr_array_append(p, overrides->rewriteconds,
3039 base->rewriteconds);
3040 a->rewriterules = apr_array_append(p, overrides->rewriterules,
3041 base->rewriterules);
3042 }
3043 else if (a->options & OPTION_INHERIT_BEFORE ||
3044 (base->options & OPTION_INHERIT_DOWN_BEFORE &&
3045 !(a->options & OPTION_IGNORE_INHERIT))) {
3046 a->rewriteconds = apr_array_append(p, base->rewriteconds,
3047 overrides->rewriteconds);
3048 a->rewriterules = apr_array_append(p, base->rewriterules,
3049 overrides->rewriterules);
3050 }
3051 else {
3052 a->rewriteconds = overrides->rewriteconds;
3053 a->rewriterules = overrides->rewriterules;
3054 }
3055
3056 return (void *)a;
3057}
3058
3059static const char *cmd_rewriteengine(cmd_parms *cmd,
3060 void *in_dconf, int flag)
3061{
3064
3065 sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
3066
3067 /* server command? set both global scope and base directory scope */
3068 if (cmd->path == NULL) {
3070 sconf->state_set = 1;
3071 dconf->state = sconf->state;
3072 dconf->state_set = 1;
3073 }
3074 /* directory command? set directory scope only */
3075 else {
3077 dconf->state_set = 1;
3078 }
3079
3080 return NULL;
3081}
3082
3084 void *in_dconf, const char *option)
3085{
3086 int options = 0;
3087
3088 while (*option) {
3089 char *w = ap_getword_conf(cmd->temp_pool, &option);
3090
3091 if (!strcasecmp(w, "inherit")) {
3092 options |= OPTION_INHERIT;
3093 }
3094 else if (!strcasecmp(w, "inheritbefore")) {
3095 options |= OPTION_INHERIT_BEFORE;
3096 }
3097 else if (!strcasecmp(w, "inheritdown")) {
3098 options |= OPTION_INHERIT_DOWN;
3099 }
3100 else if(!strcasecmp(w, "inheritdownbefore")) {
3101 options |= OPTION_INHERIT_DOWN_BEFORE;
3102 }
3103 else if (!strcasecmp(w, "ignoreinherit")) {
3104 options |= OPTION_IGNORE_INHERIT;
3105 }
3106 else if (!strcasecmp(w, "allownoslash")) {
3107 options |= OPTION_NOSLASH;
3108 }
3109 else if (!strncasecmp(w, "MaxRedirects=", 13)) {
3110 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00664)
3111 "RewriteOptions: MaxRedirects option has been "
3112 "removed in favor of the global "
3113 "LimitInternalRecursion directive and will be "
3114 "ignored.");
3115 }
3116 else if (!strcasecmp(w, "allowanyuri")) {
3117 options |= OPTION_ANYURI;
3118 }
3119 else if (!strcasecmp(w, "mergebase")) {
3120 options |= OPTION_MERGEBASE;
3121 }
3122 else if (!strcasecmp(w, "ignorecontextinfo")) {
3123 options |= OPTION_IGNORE_CONTEXT_INFO;
3124 }
3125 else if (!strcasecmp(w, "legacyprefixdocroot")) {
3127 }
3128 else if (!strcasecmp(w, "UnsafePrefixStat")) {
3129 options |= OPTION_UNSAFE_PREFIX_STAT;
3130 }
3131 else {
3132 return apr_pstrcat(cmd->pool, "RewriteOptions: unknown option '",
3133 w, "'", NULL);
3134 }
3135 }
3136
3137 /* server command? set both global scope and base directory scope */
3138 if (cmd->path == NULL) { /* is server command */
3141 ap_get_module_config(cmd->server->module_config,
3142 &rewrite_module);
3143
3144 sconf->options |= options;
3145 sconf->options_set = 1;
3146 dconf->options |= options;
3147 dconf->options_set = 1;
3148 }
3149 /* directory command? set directory scope only */
3150 else { /* is per-directory command */
3152
3153 dconf->options |= options;
3154 dconf->options_set = 1;
3155 }
3156
3157 return NULL;
3158}
3159
3160static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1,
3161 const char *a2, const char *a3)
3162{
3166 const char *fname;
3167
3168 sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
3169
3170 newmap = apr_pcalloc(cmd->pool, sizeof(rewritemap_entry));
3171
3172 if (strncasecmp(a2, "txt:", 4) == 0) {
3173 if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) {
3174 return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ",
3175 a2+4, NULL);
3176 }
3177
3178 newmap->type = MAPTYPE_TXT;
3179 newmap->datafile = fname;
3180 newmap->checkfile = fname;
3181 newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
3182 (void *)cmd->server, a1);
3183 }
3184 else if (strncasecmp(a2, "rnd:", 4) == 0) {
3185 if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) {
3186 return apr_pstrcat(cmd->pool, "RewriteMap: bad path to rnd map: ",
3187 a2+4, NULL);
3188 }
3189
3190 newmap->type = MAPTYPE_RND;
3191 newmap->datafile = fname;
3192 newmap->checkfile = fname;
3193 newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
3194 (void *)cmd->server, a1);
3195 }
3196 else if (strncasecmp(a2, "dbm", 3) == 0) {
3197 apr_status_t rv;
3198
3199 newmap->type = MAPTYPE_DBM;
3200 fname = NULL;
3201 newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
3202 (void *)cmd->server, a1);
3203
3204 if (a2[3] == ':') {
3205 newmap->dbmtype = "default";
3206 fname = a2+4;
3207 }
3208 else if (a2[3] == '=') {
3209 const char *colon = ap_strchr_c(a2 + 4, ':');
3210
3211 if (colon) {
3212 newmap->dbmtype = apr_pstrndup(cmd->pool, a2 + 4,
3213 colon - (a2 + 3) - 1);
3214 fname = colon + 1;
3215 }
3216 }
3217
3218 if (!fname) {
3219 return apr_pstrcat(cmd->pool, "RewriteMap: bad map:",
3220 a2, NULL);
3221 }
3222
3223 if ((newmap->datafile = ap_server_root_relative(cmd->pool,
3224 fname)) == NULL) {
3225 return apr_pstrcat(cmd->pool, "RewriteMap: bad path to dbm map: ",
3226 fname, NULL);
3227 }
3228
3229 rv = apr_dbm_get_usednames_ex(cmd->pool, newmap->dbmtype,
3230 newmap->datafile, &newmap->checkfile,
3231 &newmap->checkfile2);
3232 if (rv != APR_SUCCESS) {
3233 return apr_pstrcat(cmd->pool, "RewriteMap: dbm type ",
3234 newmap->dbmtype, " is invalid", NULL);
3235 }
3236 }
3237 else if ((strncasecmp(a2, "dbd:", 4) == 0)
3238 || (strncasecmp(a2, "fastdbd:", 8) == 0)) {
3239 if (dbd_prepare == NULL) {
3240 return "RewriteMap types dbd and fastdbd require mod_dbd!";
3241 }
3242 if ((a2[0] == 'd') || (a2[0] == 'D')) {
3243 newmap->type = MAPTYPE_DBD;
3244 fname = a2+4;
3245 }
3246 else {
3247 newmap->type = MAPTYPE_DBD_CACHE;
3248 fname = a2+8;
3249 newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
3250 (void *)cmd->server, a1);
3251 }
3252 newmap->dbdq = a1;
3253 dbd_prepare(cmd->server, fname, newmap->dbdq);
3254 }
3255 else if (strncasecmp(a2, "prg:", 4) == 0) {
3256 apr_tokenize_to_argv(a2 + 4, &newmap->argv, cmd->pool);
3257
3258 fname = newmap->argv[0];
3259 if ((newmap->argv[0] = ap_server_root_relative(cmd->pool,
3260 fname)) == NULL) {
3261 return apr_pstrcat(cmd->pool, "RewriteMap: bad path to prg map: ",
3262 fname, NULL);
3263 }
3264
3265 newmap->type = MAPTYPE_PRG;
3266 newmap->checkfile = newmap->argv[0];
3268
3269 if (a3) {
3270 char *tok_cntx;
3271 newmap->user = apr_strtok(apr_pstrdup(cmd->pool, a3), ":", &tok_cntx);
3272 newmap->group = apr_strtok(NULL, ":", &tok_cntx);
3273 }
3274 }
3275 else if (strncasecmp(a2, "int:", 4) == 0) {
3276 newmap->type = MAPTYPE_INT;
3277 newmap->func = (char *(*)(request_rec *,char *))
3278 apr_hash_get(mapfunc_hash, a2+4, strlen(a2+4));
3279 if (newmap->func == NULL) {
3280 return apr_pstrcat(cmd->pool, "RewriteMap: internal map not found:",
3281 a2+4, NULL);
3282 }
3283 }
3284 else {
3285 if ((fname = ap_server_root_relative(cmd->pool, a2)) == NULL) {
3286 return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ",
3287 a2, NULL);
3288 }
3289
3290 newmap->type = MAPTYPE_TXT;
3291 newmap->datafile = fname;
3292 newmap->checkfile = fname;
3293 newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
3294 (void *)cmd->server, a1);
3295 }
3296
3297 if (newmap->checkfile
3298 && (apr_stat(&st, newmap->checkfile, APR_FINFO_MIN,
3299 cmd->pool) != APR_SUCCESS)) {
3300 return apr_pstrcat(cmd->pool,
3301 "RewriteMap: file for map ", a1,
3302 " not found:", newmap->checkfile, NULL);
3303 }
3304
3306
3307 return NULL;
3308}
3309
3310static const char *cmd_rewritebase(cmd_parms *cmd, void *in_dconf,
3311 const char *a1)
3312{
3314
3315 if (cmd->path == NULL || dconf == NULL) {
3316 return "RewriteBase: only valid in per-directory config files";
3317 }
3318 if (a1[0] == '\0') {
3319 return "RewriteBase: empty URL not allowed";
3320 }
3321 if (a1[0] != '/') {
3322 return "RewriteBase: argument is not a valid URL";
3323 }
3324
3325 dconf->baseurl = a1;
3326 dconf->baseurl_set = 1;
3327
3328 return NULL;
3329}
3330
3331/*
3332 * generic lexer for RewriteRule and RewriteCond flags.
3333 * The parser will be passed in as a function pointer
3334 * and called if a flag was found
3335 */
3336static const char *cmd_parseflagfield(apr_pool_t *p, void *cfg, char *key,
3337 const char *(*parse)(apr_pool_t *,
3338 void *,
3339 char *, char *))
3340{
3341 char *val, *nextp, *endp;
3342 const char *err;
3343
3344 endp = key + strlen(key) - 1;
3345 if (*key != '[' || *endp != ']') {
3346 return "bad flag delimiters";
3347 }
3348
3349 *endp = ','; /* for simpler parsing */
3350 ++key;
3351
3352 while (*key) {
3353 /* skip leading spaces */
3354 while (apr_isspace(*key)) {
3355 ++key;
3356 }
3357
3358 if (!*key || (nextp = ap_strchr(key, ',')) == NULL) { /* NULL should not
3359 * happen, but ...
3360 */
3361 break;
3362 }
3363
3364 /* strip trailing spaces */
3365 endp = nextp - 1;
3366 while (apr_isspace(*endp)) {
3367 --endp;
3368 }
3369 *++endp = '\0';
3370
3371 /* split key and val */
3372 val = ap_strchr(key, '=');
3373 if (val) {
3374 *val++ = '\0';
3375 }
3376 else {
3377 val = endp;
3378 }
3379
3380 err = parse(p, cfg, key, val);
3381 if (err) {
3382 return err;
3383 }
3384
3385 key = nextp + 1;
3386 }
3387
3388 return NULL;
3389}
3390
3391static const char *cmd_rewritecond_setflag(apr_pool_t *p, void *_cfg,
3392 char *key, char *val)
3393{
3394 rewritecond_entry *cfg = _cfg;
3395
3396 if ( strcasecmp(key, "nocase") == 0
3397 || strcasecmp(key, "NC") == 0 ) {
3398 cfg->flags |= CONDFLAG_NOCASE;
3399 }
3400 else if ( strcasecmp(key, "ornext") == 0
3401 || strcasecmp(key, "OR") == 0 ) {
3402 cfg->flags |= CONDFLAG_ORNEXT;
3403 }
3404 else if ( strcasecmp(key, "novary") == 0
3405 || strcasecmp(key, "NV") == 0 ) {
3406 cfg->flags |= CONDFLAG_NOVARY;
3407 }
3408 else {
3409 return apr_pstrcat(p, "unknown flag '", key, "'", NULL);
3410 }
3411 return NULL;
3412}
3413
3414static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
3415 const char *in_str)
3416{
3418 char *str = apr_pstrdup(cmd->pool, in_str);
3421 ap_regex_t *regexp;
3422 char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL;
3423 const char *err;
3424
3425 sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
3426
3427 /* make a new entry in the internal temporary rewrite rule list */
3428 if (cmd->path == NULL) { /* is server command */
3429 newcond = apr_array_push(sconf->rewriteconds);
3430 }
3431 else { /* is per-directory command */
3433 }
3434
3435 /* parse the argument line ourself
3436 * a1 .. a3 are substrings of str, which is a fresh copy
3437 * of the argument line. So we can use a1 .. a3 without
3438 * copying them again.
3439 */
3440 if (parseargline(str, &a1, &a2, &a2_end, &a3)) {
3441 return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str,
3442 "'", NULL);
3443 }
3444
3445 /* arg1: the input string */
3446 newcond->input = a1;
3447
3448 /* arg3: optional flags field
3449 * (this has to be parsed first, because we need to
3450 * know if the regex should be compiled with ICASE!)
3451 */
3452 newcond->flags = CONDFLAG_NONE;
3453 if (a3 != NULL) {
3454 if ((err = cmd_parseflagfield(cmd->pool, newcond, a3,
3456 return apr_pstrcat(cmd->pool, "RewriteCond: ", err, NULL);
3457 }
3458 }
3459
3460 /* arg2: the pattern */
3461 newcond->pattern = a2;
3462 if (*a2 == '!') {
3463 newcond->flags |= CONDFLAG_NOTMATCH;
3464 ++a2;
3465 }
3466
3467 /* determine the pattern type */
3468 newcond->ptype = CONDPAT_REGEX;
3469 if (strcasecmp(a1, "expr") == 0) {
3470 newcond->ptype = CONDPAT_AP_EXPR;
3471 }
3472 else if (*a2 && a2[1]) {
3473 if (*a2 == '-') {
3474 if (!a2[2]) {
3475 switch (a2[1]) {
3476 case 'f': newcond->ptype = CONDPAT_FILE_EXISTS; break;
3477 case 's': newcond->ptype = CONDPAT_FILE_SIZE; break;
3478 case 'd': newcond->ptype = CONDPAT_FILE_DIR; break;
3479 case 'x': newcond->ptype = CONDPAT_FILE_XBIT; break;
3480 case 'h': newcond->ptype = CONDPAT_FILE_LINK; break;
3481 case 'L': newcond->ptype = CONDPAT_FILE_LINK; break;
3482 case 'l': newcond->ptype = CONDPAT_FILE_LINK; break;
3483 case 'U': newcond->ptype = CONDPAT_LU_URL; break;
3484 case 'F': newcond->ptype = CONDPAT_LU_FILE; break;
3485 }
3486 }
3487 else if (a2[3]) {
3488 switch (a2[1]) {
3489 case 'l':
3490 if (a2[2] == 't') {
3491 a2 += 3;
3492 newcond->ptype = CONDPAT_INT_LT;
3493 }
3494 else if (a2[2] == 'e') {
3495 a2 += 3;
3496 newcond->ptype = CONDPAT_INT_LE;
3497 }
3498 break;
3499
3500 case 'g':
3501 if (a2[2] == 't') {
3502 a2 += 3;
3503 newcond->ptype = CONDPAT_INT_GT;
3504 }
3505 else if (a2[2] == 'e') {
3506 a2 += 3;
3507 newcond->ptype = CONDPAT_INT_GE;
3508 }
3509 break;
3510
3511 case 'e':
3512 if (a2[2] == 'q') {
3513 a2 += 3;
3514 newcond->ptype = CONDPAT_INT_EQ;
3515 }
3516 break;
3517
3518 case 'n':
3519 if (a2[2] == 'e') {
3520 /* Inversion, ensure !-ne == -eq */
3521 a2 += 3;
3522 newcond->ptype = CONDPAT_INT_EQ;
3523 newcond->flags ^= CONDFLAG_NOTMATCH;
3524 }
3525 break;
3526 }
3527 }
3528 }
3529 else {
3530 switch (*a2) {
3531 case '>': if (*++a2 == '=')
3532 ++a2, newcond->ptype = CONDPAT_STR_GE;
3533 else
3534 newcond->ptype = CONDPAT_STR_GT;
3535 break;
3536
3537 case '<': if (*++a2 == '=')
3538 ++a2, newcond->ptype = CONDPAT_STR_LE;
3539 else
3540 newcond->ptype = CONDPAT_STR_LT;
3541 break;
3542
3543 case '=': newcond->ptype = CONDPAT_STR_EQ;
3544 /* "" represents an empty string */
3545 if (*++a2 == '"' && a2[1] == '"' && !a2[2])
3546 a2 += 2;
3547 break;
3548 }
3549 }
3550 }
3551
3552 if ((newcond->ptype != CONDPAT_REGEX) &&
3553 (newcond->ptype < CONDPAT_STR_LT || newcond->ptype > CONDPAT_STR_GE) &&
3554 (newcond->flags & CONDFLAG_NOCASE)) {
3555 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00665)
3556 "RewriteCond: NoCase option for non-regex pattern '%s' "
3557 "is not supported and will be ignored. (%s:%d)", a2,
3558 cmd->directive->filename, cmd->directive->line_num);
3559 newcond->flags &= ~CONDFLAG_NOCASE;
3560 }
3561
3562 newcond->pskip = a2 - newcond->pattern;
3563 newcond->pattern += newcond->pskip;
3564
3565 if (newcond->ptype == CONDPAT_REGEX) {
3566 regexp = ap_pregcomp(cmd->pool, a2,
3568 ? AP_REG_ICASE : 0));
3569 if (!regexp) {
3570 return apr_pstrcat(cmd->pool, "RewriteCond: cannot compile regular "
3571 "expression '", a2, "'", NULL);
3572 }
3573
3574 newcond->regexp = regexp;
3575 }
3576 else if (newcond->ptype == CONDPAT_AP_EXPR) {
3577 unsigned int flags = newcond->flags & CONDFLAG_NOVARY ?
3580 if (err)
3581 return apr_psprintf(cmd->pool, "RewriteCond: cannot compile "
3582 "expression \"%s\": %s", a2, err);
3583 }
3584
3585 return NULL;
3586}
3587
3588static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg,
3589 char *key, char *val)
3590{
3591 rewriterule_entry *cfg = _cfg;
3592 int error = 0;
3593
3594 switch (*key++) {
3595 case 'b':
3596 case 'B':
3597 if (!*key || !strcasecmp(key, "ackrefescaping")) {
3599 if (val && *val) {
3600 cfg->escapes = val;
3601 }
3602 }
3603 else if (!strcasecmp(key, "NE")) {
3604 if (val && *val) {
3605 cfg->noescapes = val;
3606 }
3607 else {
3608 return "flag 'BNE' wants a list of characters (i.e. [BNE=...])";
3609 }
3610 }
3611 else if (!strcasecmp(key, "NP") || !strcasecmp(key, "ackrefernoplus")) {
3613 }
3614 else if (!strcasecmp(key, "CTLS")) {
3616 }
3617 else {
3618 ++error;
3619 }
3620 break;
3621 case 'c':
3622 case 'C':
3623 if (!*key || !strcasecmp(key, "hain")) { /* chain */
3624 cfg->flags |= RULEFLAG_CHAIN;
3625 }
3626 else if (((*key == 'O' || *key == 'o') && !key[1])
3627 || !strcasecmp(key, "ookie")) { /* cookie */
3628 data_item *cp = cfg->cookie;
3629
3630 if (!cp) {
3631 cp = cfg->cookie = apr_palloc(p, sizeof(*cp));
3632 }
3633 else {
3634 while (cp->next) {
3635 cp = cp->next;
3636 }
3637 cp->next = apr_palloc(p, sizeof(*cp));
3638 cp = cp->next;
3639 }
3640
3641 cp->next = NULL;
3642 cp->data = val;
3643 }
3644 else {
3645 ++error;
3646 }
3647 break;
3648 case 'd':
3649 case 'D':
3650 if (!*key || !strcasecmp(key, "PI") || !strcasecmp(key,"iscardpath")) {
3652 }
3653 break;
3654 case 'e':
3655 case 'E':
3656 if (!*key || !strcasecmp(key, "nv")) { /* env */
3657 data_item *cp = cfg->env;
3658
3659 if (!cp) {
3660 cp = cfg->env = apr_palloc(p, sizeof(*cp));
3661 }
3662 else {
3663 while (cp->next) {
3664 cp = cp->next;
3665 }
3666 cp->next = apr_palloc(p, sizeof(*cp));
3667 cp = cp->next;
3668 }
3669
3670 cp->next = NULL;
3671 cp->data = val;
3672 }
3673 else if (!strcasecmp(key, "nd")) { /* end */
3674 cfg->flags |= RULEFLAG_END;
3675 }
3676 else {
3677 ++error;
3678 }
3679 break;
3680
3681 case 'f':
3682 case 'F':
3683 if (!*key || !strcasecmp(key, "orbidden")) { /* forbidden */
3686 }
3687 else {
3688 ++error;
3689 }
3690 break;
3691
3692 case 'g':
3693 case 'G':
3694 if (!*key || !strcasecmp(key, "one")) { /* gone */
3697 }
3698 else {
3699 ++error;
3700 }
3701 break;
3702
3703 case 'h':
3704 case 'H':
3705 if (!*key || !strcasecmp(key, "andler")) { /* handler */
3706 cfg->forced_handler = val;
3707 }
3708 else {
3709 ++error;
3710 }
3711 break;
3712 case 'l':
3713 case 'L':
3714 if (!*key || !strcasecmp(key, "ast")) { /* last */
3715 cfg->flags |= RULEFLAG_LASTRULE;
3716 }
3717 else {
3718 ++error;
3719 }
3720 break;
3721
3722 case 'n':
3723 case 'N':
3724 if (((*key == 'E' || *key == 'e') && !key[1])
3725 || !strcasecmp(key, "oescape")) { /* noescape */
3726 cfg->flags |= RULEFLAG_NOESCAPE;
3727 }
3728 else if (!*key || !strcasecmp(key, "ext")) { /* next */
3729 cfg->flags |= RULEFLAG_NEWROUND;
3730 if (val && *val) {
3731 cfg->maxrounds = atoi(val);
3732 }
3733
3734 }
3735 else if (((*key == 'S' || *key == 's') && !key[1])
3736 || !strcasecmp(key, "osubreq")) { /* nosubreq */
3738 }
3739 else if (((*key == 'C' || *key == 'c') && !key[1])
3740 || !strcasecmp(key, "ocase")) { /* nocase */
3741 cfg->flags |= RULEFLAG_NOCASE;
3742 }
3743 else {
3744 ++error;
3745 }
3746 break;
3747
3748 case 'p':
3749 case 'P':
3750 if (!*key || !strcasecmp(key, "roxy")) { /* proxy */
3751 cfg->flags |= RULEFLAG_PROXY;
3752 }
3753 else if (((*key == 'T' || *key == 't') && !key[1])
3754 || !strcasecmp(key, "assthrough")) { /* passthrough */
3756 }
3757 else {
3758 ++error;
3759 }
3760 break;
3761
3762 case 'q':
3763 case 'Q':
3764 if ( !strcasecmp(key, "SA")
3765 || !strcasecmp(key, "sappend")) { /* qsappend */
3766 cfg->flags |= RULEFLAG_QSAPPEND;
3767 } else if ( !strcasecmp(key, "SD")
3768 || !strcasecmp(key, "sdiscard") ) { /* qsdiscard */
3769 cfg->flags |= RULEFLAG_QSDISCARD;
3770 } else if ( !strcasecmp(key, "SL")
3771 || !strcasecmp(key, "slast") ) { /* qslast */
3772 cfg->flags |= RULEFLAG_QSLAST;
3773 }
3774 else {
3775 ++error;
3776 }
3777 break;
3778
3779 case 'r':
3780 case 'R':
3781 if (!*key || !strcasecmp(key, "edirect")) { /* redirect */
3782 int status = 0;
3783
3785 if (*val) {
3786 if (strcasecmp(val, "permanent") == 0) {
3788 }
3789 else if (strcasecmp(val, "temp") == 0) {
3791 }
3792 else if (strcasecmp(val, "seeother") == 0) {
3794 }
3795 else if (apr_isdigit(*val)) {
3796 status = atoi(val);
3798 int idx =
3800
3802 return apr_psprintf(p, "invalid HTTP "
3803 "response code '%s' for "
3804 "flag 'R'",
3805 val);
3806 }
3807 }
3810 }
3811 }
3813 }
3814 }
3815 else {
3816 ++error;
3817 }
3818 break;
3819
3820 case 's':
3821 case 'S':
3822 if (!*key || !strcasecmp(key, "kip")) { /* skip */
3823 cfg->skip = atoi(val);
3824 }
3825 else {
3826 ++error;
3827 }
3828 break;
3829
3830 case 't':
3831 case 'T':
3832 if (!*key || !strcasecmp(key, "ype")) { /* type */
3833 cfg->forced_mimetype = val;
3834 }
3835 else {
3836 ++error;
3837 }
3838 break;
3839 case 'u':
3840 case 'U':
3841 if (!strcasecmp(key, "nsafePrefixStat")){
3843 }
3844 else if(!strcasecmp(key, "nsafeAllow3F")) {
3846 }
3847 else if(!strcasecmp(key, "NC")) {
3848 cfg->flags |= RULEFLAG_UNC;
3849 }
3850 else {
3851 ++error;
3852 }
3853 break;
3854 default:
3855 ++error;
3856 break;
3857 }
3858
3859 if (error) {
3860 return apr_pstrcat(p, "unknown flag '", --key, "'", NULL);
3861 }
3862
3863 return NULL;
3864}
3865
3866static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
3867 const char *in_str)
3868{
3870 char *str = apr_pstrdup(cmd->pool, in_str);
3873 ap_regex_t *regexp;
3874 char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL;
3875 const char *err;
3876
3877 sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
3878
3879 /* make a new entry in the internal rewrite rule list */
3880 if (cmd->path == NULL) { /* is server command */
3881 newrule = apr_array_push(sconf->rewriterules);
3882 }
3883 else { /* is per-directory command */
3885 }
3886
3887 /* parse the argument line ourself */
3888 if (parseargline(str, &a1, &a2, &a2_end, &a3)) {
3889 return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str,
3890 "'", NULL);
3891 }
3892
3893 newrule->forced_mimetype = NULL;
3894 newrule->forced_handler = NULL;
3895 newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY;
3896 newrule->flags = RULEFLAG_NONE;
3897 newrule->env = NULL;
3898 newrule->cookie = NULL;
3899 newrule->skip = 0;
3900 newrule->maxrounds = REWRITE_MAX_ROUNDS;
3901 newrule->escapes = newrule->noescapes = NULL;
3902
3903 /* arg3: optional flags field */
3904 if (a3 != NULL) {
3905 if ((err = cmd_parseflagfield(cmd->pool, newrule, a3,
3907 return apr_pstrcat(cmd->pool, "RewriteRule: ", err, NULL);
3908 }
3909 }
3910
3911 /* arg1: the pattern
3912 * try to compile the regexp to test if is ok
3913 */
3914 if (*a1 == '!') {
3915 newrule->flags |= RULEFLAG_NOTMATCH;
3916 ++a1;
3917 }
3918
3919 regexp = ap_pregcomp(cmd->pool, a1, AP_REG_EXTENDED |
3920 ((newrule->flags & RULEFLAG_NOCASE)
3921 ? AP_REG_ICASE : 0));
3922 if (!regexp) {
3923 return apr_pstrcat(cmd->pool,
3924 "RewriteRule: cannot compile regular expression '",
3925 a1, "'", NULL);
3926 }
3927
3928 newrule->pattern = a1;
3929 newrule->regexp = regexp;
3930
3931 /* arg2: the output string */
3932 newrule->output = a2;
3933 if (*a2 == '-' && !a2[1]) {
3934 newrule->flags |= RULEFLAG_NOSUB;
3935 }
3936
3937 if (*(a2_end-1) == '?') {
3938 /* a literal ? at the end of the unsubstituted rewrite rule */
3939 if (newrule->flags & RULEFLAG_QSAPPEND) {
3940 /* with QSA, splitout_queryargs will safely handle it if RULEFLAG_QSLAST is set */
3941 newrule->flags |= RULEFLAG_QSLAST;
3942 }
3943 else {
3944 /* avoid getting a query string via inadvertent capture */
3945 newrule->flags |= RULEFLAG_QSNONE;
3946 /* trailing ? has done its job, but splitout_queryargs will not chop it off */
3947 *(a2_end-1) = '\0';
3948 }
3949 }
3950 else if (newrule->flags & RULEFLAG_QSDISCARD) {
3951 if (NULL == ap_strchr(newrule->output, '?')) {
3952 newrule->flags |= RULEFLAG_QSNONE;
3953 }
3954 }
3955
3956 /* now, if the server or per-dir config holds an
3957 * array of RewriteCond entries, we take it for us
3958 * and clear the array
3959 */
3960 if (cmd->path == NULL) { /* is server command */
3961 newrule->rewriteconds = sconf->rewriteconds;
3962 sconf->rewriteconds = apr_array_make(cmd->pool, 2,
3963 sizeof(rewritecond_entry));
3964 }
3965 else { /* is per-directory command */
3966 newrule->rewriteconds = dconf->rewriteconds;
3967 dconf->rewriteconds = apr_array_make(cmd->pool, 2,
3968 sizeof(rewritecond_entry));
3969 }
3970
3971 return NULL;
3972}
3973
3974
3975/*
3976 * +-------------------------------------------------------+
3977 * | |
3978 * | the rewriting engine
3979 * | |
3980 * +-------------------------------------------------------+
3981 */
3982
3983/* Lexicographic Compare */
3984static APR_INLINE int compare_lexicography(char *a, char *b)
3985{
3987
3988 lena = strlen(a);
3989 lenb = strlen(b);
3990
3991 if (lena == lenb) {
3992 for (i = 0; i < lena; ++i) {
3993 if (a[i] != b[i]) {
3994 return ((unsigned char)a[i] > (unsigned char)b[i]) ? 1 : -1;
3995 }
3996 }
3997
3998 return 0;
3999 }
4000
4001 return ((lena > lenb) ? 1 : -1);
4002}
4003
4004/*
4005 * Apply a single rewriteCond
4006 */
4008{
4009 char *input = NULL;
4011 request_rec *rsub, *r = ctx->r;
4013 int rc = 0;
4014 int basis;
4015
4016 if (p->ptype != CONDPAT_AP_EXPR)
4017 input = do_expand(p->input, ctx, NULL);
4018
4019 switch (p->ptype) {
4022 && sb.filetype == APR_REG) {
4023 rc = 1;
4024 }
4025 break;
4026
4027 case CONDPAT_FILE_SIZE:
4029 && sb.filetype == APR_REG && sb.size > 0) {
4030 rc = 1;
4031 }
4032 break;
4033
4034 case CONDPAT_FILE_LINK:
4035#if !defined(OS2)
4037 r->pool) == APR_SUCCESS
4038 && sb.filetype == APR_LNK) {
4039 rc = 1;
4040 }
4041#endif
4042 break;
4043
4044 case CONDPAT_FILE_DIR:
4046 && sb.filetype == APR_DIR) {
4047 rc = 1;
4048 }
4049 break;
4050
4051 case CONDPAT_FILE_XBIT:
4053 && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
4054 rc = 1;
4055 }
4056 break;
4057
4058 case CONDPAT_LU_URL:
4059 if (*input && subreq_ok(r)) {
4061 if (rsub->status < 400) {
4062 rc = 1;
4063 }
4064 rewritelog((r, 5, NULL, "RewriteCond URI (-U) check: "
4065 "path=%s -> status=%d", input, rsub->status));
4067 }
4068 break;
4069
4070 case CONDPAT_LU_FILE:
4071 if (*input && subreq_ok(r)) {
4073 if (rsub->status < 300 &&
4074 /* double-check that file exists since default result is 200 */
4075 apr_stat(&sb, rsub->filename, APR_FINFO_MIN,
4076 r->pool) == APR_SUCCESS) {
4077 rc = 1;
4078 }
4079 rewritelog((r, 5, NULL, "RewriteCond file (-F) check: path=%s "
4080 "-> file=%s status=%d", input, rsub->filename,
4081 rsub->status));
4083 }
4084 break;
4085
4086 case CONDPAT_STR_GE:
4087 basis = 0;
4088 goto test_str_g;
4089 case CONDPAT_STR_GT:
4090 basis = 1;
4092 if (p->flags & CONDFLAG_NOCASE) {
4093 rc = (strcasecmp(input, p->pattern) >= basis) ? 1 : 0;
4094 }
4095 else {
4096 rc = (compare_lexicography(input, p->pattern) >= basis) ? 1 : 0;
4097 }
4098 break;
4099
4100 case CONDPAT_STR_LE:
4101 basis = 0;
4102 goto test_str_l;
4103 case CONDPAT_STR_LT:
4104 basis = -1;
4106 if (p->flags & CONDFLAG_NOCASE) {
4107 rc = (strcasecmp(input, p->pattern) <= basis) ? 1 : 0;
4108 }
4109 else {
4110 rc = (compare_lexicography(input, p->pattern) <= basis) ? 1 : 0;
4111 }
4112 break;
4113
4114 case CONDPAT_STR_EQ:
4115 /* Note: the only type where the operator is dropped from p->pattern */
4116 if (p->flags & CONDFLAG_NOCASE) {
4117 rc = !strcasecmp(input, p->pattern);
4118 }
4119 else {
4120 rc = !strcmp(input, p->pattern);
4121 }
4122 break;
4123
4124 case CONDPAT_INT_GE: rc = (atoi(input) >= atoi(p->pattern)); break;
4125 case CONDPAT_INT_GT: rc = (atoi(input) > atoi(p->pattern)); break;
4126
4127 case CONDPAT_INT_LE: rc = (atoi(input) <= atoi(p->pattern)); break;
4128 case CONDPAT_INT_LT: rc = (atoi(input) < atoi(p->pattern)); break;
4129
4130 case CONDPAT_INT_EQ: rc = (atoi(input) == atoi(p->pattern)); break;
4131
4132 case CONDPAT_AP_EXPR:
4133 {
4134 const char *err, *source;
4135 rc = ap_expr_exec_re(r, p->expr, AP_MAX_REG_MATCH, regmatch,
4136 &source, &err);
4137 if (rc < 0 || err) {
4138 rewritelog((r, 1, ctx->perdir,
4139 "RewriteCond: expr='%s' evaluation failed: %s",
4140 p->pattern - p->pskip, err));
4141 rc = 0;
4142 }
4143 /* update briRC backref info */
4144 if (rc && !(p->flags & CONDFLAG_NOTMATCH)) {
4145 ctx->briRC.source = source;
4146 memcpy(ctx->briRC.regmatch, regmatch, sizeof(regmatch));
4147 }
4148 }
4149 break;
4150 default:
4151 /* it is really a regexp pattern, so apply it */
4152 rc = !ap_regexec(p->regexp, input, AP_MAX_REG_MATCH, regmatch, 0);
4153
4154 /* update briRC backref info */
4155 if (rc && !(p->flags & CONDFLAG_NOTMATCH)) {
4156 ctx->briRC.source = input;
4157 memcpy(ctx->briRC.regmatch, regmatch, sizeof(regmatch));
4158 }
4159 break;
4160 }
4161
4162 if (p->flags & CONDFLAG_NOTMATCH) {
4163 rc = !rc;
4164 }
4165
4166 rewritelog((r, 4, ctx->perdir, "RewriteCond: input='%s' pattern='%s'%s "
4167 "=> %s", input, p->pattern - p->pskip,
4168 (p->flags & CONDFLAG_NOCASE) ? " [NC]" : "",
4169 rc ? "matched" : "not-matched"));
4170
4171 return rc;
4172}
4173
4174/* check for forced type and handler */
4177{
4178 char *expanded;
4179
4180 if (p->forced_mimetype) {
4181 expanded = do_expand(p->forced_mimetype, ctx, p);
4182
4183 if (*expanded) {
4185
4186 rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have MIME-type "
4187 "'%s'", ctx->r->filename, expanded));
4188
4190 expanded);
4191 }
4192 }
4193
4194 if (p->forced_handler) {
4195 expanded = do_expand(p->forced_handler, ctx, p);
4196
4197 if (*expanded) {
4199
4200 rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have "
4201 "Content-handler '%s'", ctx->r->filename, expanded));
4202
4204 expanded);
4205 }
4206 }
4207}
4208
4209/*
4210 * Apply a single RewriteRule
4211 */
4214{
4216 apr_array_header_t *rewriteconds;
4218 int i, rc;
4219 char *newuri = NULL;
4220 request_rec *r = ctx->r;
4221 int is_proxyreq = 0;
4222
4223 ctx->uri = r->filename;
4224
4225 if (ctx->perdir) {
4226 apr_size_t dirlen = strlen(ctx->perdir);
4227
4228 /*
4229 * Proxy request?
4230 */
4232 && !strncmp(r->filename, "proxy:", 6));
4233
4234 /* Since we want to match against the (so called) full URL, we have
4235 * to re-add the PATH_INFO postfix
4236 */
4237 if (r->path_info && *r->path_info) {
4238 rewritelog((r, 3, ctx->perdir, "add path info postfix: %s -> %s%s",
4239 ctx->uri, ctx->uri, r->path_info));
4240 ctx->uri = apr_pstrcat(r->pool, ctx->uri, r->path_info, NULL);
4241 }
4242
4243 /* Additionally we strip the physical path from the url to match
4244 * it independent from the underlying filesystem.
4245 */
4246 if (!is_proxyreq && strlen(ctx->uri) >= dirlen &&
4247 !strncmp(ctx->uri, ctx->perdir, dirlen)) {
4248
4249 rewritelog((r, 3, ctx->perdir, "strip per-dir prefix: %s -> %s",
4250 ctx->uri, ctx->uri + dirlen));
4251 ctx->uri = ctx->uri + dirlen;
4252 }
4253 }
4254
4255 /* Try to match the URI against the RewriteRule pattern
4256 * and exit immediately if it didn't apply.
4257 */
4258 rewritelog((r, 3, ctx->perdir, "applying pattern '%s' to uri '%s'",
4259 p->pattern, ctx->uri));
4260
4261 rc = !ap_regexec(p->regexp, ctx->uri, AP_MAX_REG_MATCH, regmatch, 0);
4262 if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
4263 (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) {
4264 return RULE_RC_NOMATCH;
4265 }
4266
4267 /* It matched, wow! Now it's time to prepare the context structure for
4268 * further processing
4269 */
4270 ctx->vary_this = NULL;
4271 ctx->briRC.source = NULL;
4272
4273 if (p->flags & RULEFLAG_NOTMATCH) {
4274 ctx->briRR.source = NULL;
4275 }
4276 else {
4277 ctx->briRR.source = apr_pstrdup(r->pool, ctx->uri);
4278 memcpy(ctx->briRR.regmatch, regmatch, sizeof(regmatch));
4279 }
4280
4281 /* Ok, we already know the pattern has matched, but we now
4282 * additionally have to check for all existing preconditions
4283 * (RewriteCond) which have to be also true. We do this at
4284 * this very late stage to avoid unnecessary checks which
4285 * would slow down the rewriting engine.
4286 */
4287 rewriteconds = p->rewriteconds;
4288 conds = (rewritecond_entry *)rewriteconds->elts;
4289
4290 for (i = 0; i < rewriteconds->nelts; ++i) {
4292
4294 /*
4295 * Reset vary_this if the novary flag is set for this condition.
4296 */
4297 if (c->flags & CONDFLAG_NOVARY) {
4298 ctx->vary_this = NULL;
4299 }
4300 if (c->flags & CONDFLAG_ORNEXT) {
4301 if (!rc) {
4302 /* One condition is false, but another can be still true. */
4303 ctx->vary_this = NULL;
4304 continue;
4305 }
4306 else {
4307 /* skip the rest of the chained OR conditions */
4309 && c->flags & CONDFLAG_ORNEXT) {
4310 c = &conds[++i];
4311 }
4312 }
4313 }
4314 else if (!rc) {
4315 return RULE_RC_NOMATCH;
4316 }
4317
4318 /* If some HTTP header was involved in the condition, remember it
4319 * for later use
4320 */
4321 if (ctx->vary_this) {
4322 ctx->vary = ctx->vary
4323 ? apr_pstrcat(r->pool, ctx->vary, ", ", ctx->vary_this,
4324 NULL)
4325 : ctx->vary_this;
4326 ctx->vary_this = NULL;
4327 }
4328 }
4329
4330 /* expand the result */
4331 if (!(p->flags & RULEFLAG_NOSUB)) {
4332 newuri = do_expand(p->output, ctx, p);
4333 rewritelog((r, 2, ctx->perdir, "rewrite '%s' -> '%s'", ctx->uri,
4334 newuri));
4335 if (!(p->flags & RULEFLAG_UNSAFE_ALLOW3F) &&
4336 ap_strcasestr(r->unparsed_uri, "%3f") &&
4337 ap_strchr_c(newuri, '?')) {
4339 "Unsafe URL with %%3f URL rewritten without "
4340 "UnsafeAllow3F");
4342 return RULE_RC_STATUS_SET;
4343 }
4344 }
4345
4346 /* expand [E=var:val] and [CO=<cookie>] */
4347 do_expand_env(p->env, ctx);
4348 do_expand_cookie(p->cookie, ctx);
4349
4350 /* non-substitution rules ('RewriteRule <pat> -') end here. */
4351 if (p->flags & RULEFLAG_NOSUB) {
4353
4354 if (p->flags & RULEFLAG_STATUS) {
4355 rewritelog((r, 2, ctx->perdir, "forcing responsecode %d for %s",
4356 p->forced_responsecode, r->filename));
4357
4358 r->status = p->forced_responsecode;
4359 }
4360
4361 return RULE_RC_NOSUB;
4362 }
4363
4364 /* Add the previously stripped per-directory location prefix, unless
4365 * (1) it's an absolute URL path and
4366 * (2) it's a full qualified URL
4367 */
4368 if (!is_proxyreq
4370 && !AP_IS_SLASH(*newuri)
4371 && !is_absolute_uri(newuri, NULL)) {
4372 if (ctx->perdir) {
4373 rewritelog((r, 3, ctx->perdir, "add per-dir prefix: %s -> %s%s",
4374 newuri, ctx->perdir, newuri));
4375 newuri = apr_pstrcat(r->pool, ctx->perdir, newuri, NULL);
4376 }
4377 else if (!(p->flags & (RULEFLAG_PROXY | RULEFLAG_FORCEREDIRECT))) {
4378 /* Not an absolute URI-path and the scheme (if any) is unknown,
4379 * and it won't be passed to fully_qualify_uri() below either,
4380 * so add an implicit '/' prefix. This avoids potentially a common
4381 * rule like "RewriteRule ^/some/path(.*) $1" that is given a path
4382 * like "/some/pathscheme:..." to produce the fully qualified URL
4383 * "scheme:..." which could be misinterpreted later.
4384 */
4385 rewritelog((r, 3, ctx->perdir, "add root prefix: %s -> /%s",
4386 newuri, newuri));
4387
4388 newuri = apr_pstrcat(r->pool, "/", newuri, NULL);
4389 }
4390 }
4391
4392 /* Now adjust API's knowledge about r->filename and r->args */
4393 r->filename = newuri;
4394
4395 if (ctx->perdir && (p->flags & RULEFLAG_DISCARDPATHINFO)) {
4396 r->path_info = NULL;
4397 }
4398
4399 splitout_queryargs(r, p->flags);
4400
4401 /* If this rule is forced for proxy throughput
4402 * (`RewriteRule ... ... [P]') then emulate mod_proxy's
4403 * URL-to-filename handler to be sure mod_proxy is triggered
4404 * for this URL later in the Apache API. But make sure it is
4405 * a fully-qualified URL. (If not it is qualified with
4406 * ourself).
4407 */
4408 if (p->flags & RULEFLAG_PROXY) {
4409 /* For rules evaluated in server context, the mod_proxy fixup
4410 * hook can be relied upon to escape the URI as and when
4411 * necessary, since it occurs later. If in directory context,
4412 * the ordering of the fixup hooks is forced such that
4413 * mod_proxy comes first, so the URI must be escaped here
4414 * instead. See PR 39746, 46428, and other headaches. */
4415 if (ctx->perdir && (p->flags & RULEFLAG_NOESCAPE) == 0) {
4416 char *old_filename = r->filename;
4417
4419 rewritelog((r, 2, ctx->perdir, "escaped URI in per-dir context "
4420 "for proxy, %s -> %s", old_filename, r->filename));
4421 }
4422
4424
4425 rewritelog((r, 2, ctx->perdir, "forcing proxy-throughput with %s",
4426 r->filename));
4427
4428 r->filename = apr_pstrcat(r->pool, "proxy:", r->filename, NULL);
4429 return RULE_RC_MATCH;
4430 }
4431
4432 /* If this rule is explicitly forced for HTTP redirection
4433 * (`RewriteRule .. .. [R]') then force an external HTTP
4434 * redirect. But make sure it is a fully-qualified URL. (If
4435 * not it is qualified with ourself).
4436 */
4437 if (p->flags & RULEFLAG_FORCEREDIRECT) {
4439
4440 rewritelog((r, 2, ctx->perdir, "explicitly forcing redirect with %s",
4441 r->filename));
4442
4443 r->status = p->forced_responsecode;
4444 return RULE_RC_MATCH;
4445 }
4446
4447 /* Special Rewriting Feature: Self-Reduction
4448 * We reduce the URL by stripping a possible
4449 * http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which
4450 * corresponds to ourself. This is to simplify rewrite maps
4451 * and to avoid recursion, etc. When this prefix is not a
4452 * coincidence then the user has to use [R] explicitly (see
4453 * above).
4454 */
4455 reduce_uri(r);
4456
4457 /* If this rule is still implicitly forced for HTTP
4458 * redirection (`RewriteRule .. <scheme>://...') then
4459 * directly force an external HTTP redirect.
4460 */
4461 if (is_absolute_uri(r->filename, NULL)) {
4462 rewritelog((r, 2, ctx->perdir, "implicitly forcing redirect (rc=%d) "
4463 "with %s", p->forced_responsecode, r->filename));
4464
4465 r->status = p->forced_responsecode;
4466 return RULE_RC_MATCH;
4467 }
4468
4469 if (!(p->flags & RULEFLAG_UNC)) {
4470 /* merge leading slashes, unless they were literals in the sub */
4471 if (!AP_IS_SLASH(p->output[0]) || !AP_IS_SLASH(p->output[1])) {
4472 while (AP_IS_SLASH(r->filename[0]) &&
4473 AP_IS_SLASH(r->filename[1])) {
4474 r->filename++;
4475 }
4476 }
4477 }
4478
4479 /* Finally remember the forced mime-type */
4481
4482 /* Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_)
4483 * But now we're done for this particular rule.
4484 */
4485 return RULE_RC_MATCH;
4486}
4487
4488/*
4489 * Apply a complete rule set,
4490 * i.e. a list of rewrite rules
4491 */
4493 char *perdir, rewriterule_entry **lastsub)
4494{
4495 rewriterule_entry *entries;
4497 int i;
4498 int changed;
4500 int s;
4502 int round = 1;
4503
4504 ctx = apr_palloc(r->pool, sizeof(*ctx));
4505 ctx->perdir = perdir;
4506 ctx->r = r;
4507 *lastsub = NULL;
4508
4509 /*
4510 * Iterate over all existing rules
4511 */
4512 entries = (rewriterule_entry *)rewriterules->elts;
4513 changed = 0;
4514 loop:
4515 for (i = 0; i < rewriterules->nelts; i++) {
4516 p = &entries[i];
4517
4518 /*
4519 * Ignore this rule on subrequests if we are explicitly
4520 * asked to do so or this is a proxy-throughput or a
4521 * forced redirect rule.
4522 */
4523 if (r->main != NULL &&
4524 (p->flags & RULEFLAG_IGNOREONSUBREQ ||
4525 p->flags & RULEFLAG_FORCEREDIRECT )) {
4526 continue;
4527 }
4528
4529 /*
4530 * Apply the current rule.
4531 */
4532 ctx->vary = NULL;
4534
4535 if (rc != RULE_RC_NOMATCH) {
4536
4537 if (!(p->flags & RULEFLAG_NOSUB)) {
4538 rewritelog((r, 2, perdir, "setting lastsub to rule with output %s", p->output));
4539 *lastsub = p;
4540 }
4541
4542 /* Catch looping rules with pathinfo growing unbounded */
4543 if ( strlen( r->filename ) > 2*r->server->limit_req_line ) {
4545 "RewriteRule '%s' and URI '%s' "
4546 "exceeded maximum length (%d)",
4547 p->pattern, r->uri, 2*r->server->limit_req_line );
4549 return ACTION_STATUS;
4550 }
4551
4552 /* Regardless of what we do next, we've found a match. Check to see
4553 * if any of the request header fields were involved, and add them
4554 * to the Vary field of the response.
4555 */
4556 if (ctx->vary) {
4557 apr_table_merge(r->headers_out, "Vary", ctx->vary);
4558 }
4559
4560
4561 /* Error while evaluating rule, r->status set */
4562 if (RULE_RC_STATUS_SET == rc) {
4563 return ACTION_STATUS_SET;
4564 }
4565
4566 /*
4567 * The rule sets the response code (implies match-only)
4568 */
4569 if (p->flags & RULEFLAG_STATUS) {
4570 return ACTION_STATUS;
4571 }
4572
4573 /*
4574 * Indicate a change if this was not a match-only rule.
4575 */
4576 if (rc != RULE_RC_NOSUB) {
4577 changed = ((p->flags & RULEFLAG_NOESCAPE)
4579 }
4580
4581 /*
4582 * Pass-Through Feature (`RewriteRule .. .. [PT]'):
4583 * Because the Apache 1.x API is very limited we
4584 * need this hack to pass the rewritten URL to other
4585 * modules like mod_alias, mod_userdir, etc.
4586 */
4587 if (p->flags & RULEFLAG_PASSTHROUGH) {
4588 rewritelog((r, 2, perdir, "forcing '%s' to get passed through "
4589 "to next API URI-to-filename handler", r->filename));
4590 r->filename = apr_pstrcat(r->pool, "passthrough:",
4591 r->filename, NULL);
4593 break;
4594 }
4595
4596 if (p->flags & RULEFLAG_END) {
4597 rewritelog((r, 8, perdir, "Rule has END flag, no further rewriting for this request"));
4599 break;
4600 }
4601 /*
4602 * Stop processing also on proxy pass-through and
4603 * last-rule and new-round flags.
4604 */
4605 if (p->flags & (RULEFLAG_PROXY | RULEFLAG_LASTRULE)) {
4606 break;
4607 }
4608
4609 /*
4610 * On "new-round" flag we just start from the top of
4611 * the rewriting ruleset again.
4612 */
4613 if (p->flags & RULEFLAG_NEWROUND) {
4614 if (++round >= p->maxrounds) {
4616 "RewriteRule '%s' and URI '%s' exceeded "
4617 "maximum number of rounds (%d) via the [N] flag",
4618 p->pattern, r->uri, p->maxrounds);
4619
4621 return ACTION_STATUS;
4622 }
4623 goto loop;
4624 }
4625
4626 /*
4627 * If we are forced to skip N next rules, do it now.
4628 */
4629 if (p->skip > 0) {
4630 s = p->skip;
4632 && s > 0) {
4633 i++;
4634 s--;
4635 }
4636 }
4637 }
4638 else {
4639 /*
4640 * If current rule is chained with next rule(s),
4641 * skip all this next rule(s)
4642 */
4644 && p->flags & RULEFLAG_CHAIN) {
4645 i++;
4646 p = &entries[i];
4647 }
4648 }
4649 }
4650 return changed;
4651}
4652
4653
4654/*
4655 * +-------------------------------------------------------+
4656 * | |
4657 * | Module Initialization Hooks
4658 * | |
4659 * +-------------------------------------------------------+
4660 */
4661
4683
4686 apr_pool_t *ptemp,
4687 server_rec *s)
4688{
4689 apr_status_t rv;
4690
4691 /* check if proxy module is available */
4692 proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL);
4693
4694 if (rewrite_lock_needed) {
4695 rv = rewritelock_create(s, p);
4696 if (rv != APR_SUCCESS) {
4698 }
4699
4702 }
4703
4704 /* if we are not doing the initial config, step through the servers and
4705 * open the RewriteMap prg:xxx programs,
4706 */
4708 for (; s; s = s->next) {
4711 }
4712 }
4713 }
4714
4715 return OK;
4716}
4717
4719{
4720 apr_status_t rv = 0; /* get a rid of gcc warning (REWRITELOG_DISABLED) */
4721
4725 if (rv != APR_SUCCESS) {
4727 "mod_rewrite: could not init rewrite_mapr_lock_acquire"
4728 " in child");
4729 }
4730 }
4731
4732 /* create the lookup cache */
4733 if (!init_cache(p)) {
4735 "mod_rewrite: could not init map cache in child");
4736 }
4737}
4738
4739
4740/*
4741 * +-------------------------------------------------------+
4742 * | |
4743 * | runtime hooks
4744 * | |
4745 * +-------------------------------------------------------+
4746 */
4747
4748/*
4749 * URI-to-filename hook
4750 * [deals with RewriteRules in server context]
4751 */
4753{
4754 rewrite_perdir_conf *dconf;
4755 rewrite_server_conf *conf;
4756 const char *saved_rulestatus;
4757 const char *var;
4758 const char *thisserver;
4759 char *thisport;
4760 const char *thisurl;
4761 unsigned int port;
4762 int rulestatus;
4763 void *skipdata;
4764 const char *oargs;
4766
4767 /*
4768 * retrieve the config structures
4769 */
4770 conf = ap_get_module_config(r->server->module_config, &rewrite_module);
4771
4773 &rewrite_module);
4774
4775 /*
4776 * only do something under runtime if the engine is really enabled,
4777 * else return immediately!
4778 */
4779 if (!dconf || dconf->state == ENGINE_DISABLED) {
4780 return DECLINED;
4781 }
4782
4783 /*
4784 * check for the ugly API case of a virtual host section where no
4785 * mod_rewrite directives exists. In this situation we became no chance
4786 * by the API to setup our default per-server config so we have to
4787 * on-the-fly assume we have the default config. But because the default
4788 * config has a disabled rewriting engine we are lucky because can
4789 * just stop operating now.
4790 */
4791 if (conf->server != r->server) {
4792 return DECLINED;
4793 }
4794
4795 /* END flag was used as a RewriteRule flag on this request */
4797 if (skipdata != NULL) {
4798 rewritelog((r, 8, NULL, "Declining, no further rewriting due to END flag"));
4799 return DECLINED;
4800 }
4801
4802 /* Unless the anyuri option is set, ensure that the input to the
4803 * first rule really is a URL-path, avoiding security issues with
4804 * poorly configured rules. See CVE-2011-3368, CVE-2011-4317. */
4805 if ((dconf->options & OPTION_ANYURI) == 0
4806 && ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0')
4807 || !r->uri || r->uri[0] != '/')) {
4808 rewritelog((r, 8, NULL, "Declining, request-URI '%s' is not a URL-path. "
4809 "Consult the manual entry for the RewriteOptions directive "
4810 "for options and caveats about matching other strings.",
4811 r->uri));
4812 return DECLINED;
4813 }
4814
4815 /*
4816 * remember the original query string for later check, since we don't
4817 * want to apply URL-escaping when no substitution has changed it.
4818 */
4819 oargs = r->args;
4820
4821 /*
4822 * add the SCRIPT_URL variable to the env. this is a bit complicated
4823 * due to the fact that apache uses subrequests and internal redirects
4824 */
4825
4826 if (r->main == NULL) {
4828 if (var == NULL) {
4830 }
4831 else {
4833 }
4834 }
4835 else {
4838 }
4839
4840 /*
4841 * create the SCRIPT_URI variable for the env
4842 */
4843
4844 /* add the canonical URI of this URL */
4847 if (ap_is_default_port(port, r)) {
4848 thisport = "";
4849 }
4850 else {
4851 thisport = apr_psprintf(r->pool, ":%u", port);
4852 }
4854
4855 /* set the variable */
4857 thisurl, NULL);
4859
4860 if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) {
4861 /* if filename was not initially set,
4862 * we start with the requested URI
4863 */
4864 if (r->filename == NULL) {
4865 r->filename = apr_pstrdup(r->pool, r->uri);
4866 rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s",
4867 r->filename));
4868 }
4869 else {
4870 rewritelog((r, 2, NULL, "init rewrite engine with passed filename "
4871 "%s. Original uri = %s", r->filename, r->uri));
4872 }
4873
4874 /*
4875 * now apply the rules ...
4876 */
4878 apr_table_setn(r->notes, "mod_rewrite_rewritten",
4879 apr_psprintf(r->pool,"%d",rulestatus));
4880 }
4881 else {
4882 rewritelog((r, 2, NULL, "uri already rewritten. Status %s, Uri %s, "
4883 "r->filename %s", saved_rulestatus, r->uri, r->filename));
4884
4886 }
4887
4888 if (rulestatus) {
4889 apr_size_t flen = r->filename ? strlen(r->filename) : 0;
4890 unsigned skip_absolute = flen ? is_absolute_uri(r->filename, NULL) : 0;
4891 int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0);
4893
4894 if (r->args
4895 && !will_escape
4896 && *(ap_scan_vchar_obstext(r->args))) {
4897 /*
4898 * We have a raw control character or a ' ' in r->args.
4899 * Correct encoding was missed.
4900 * Correct encoding was missed and we're not going to escape
4901 * it before returning.
4902 */
4904 "Rewritten query string contains control "
4905 "characters or spaces");
4906 return HTTP_FORBIDDEN;
4907 }
4908
4909 if (ACTION_STATUS == rulestatus) {
4910 int n = r->status;
4911
4912 r->status = HTTP_OK;
4913 return n;
4914 }
4915 else if (ACTION_STATUS_SET == rulestatus) {
4916 return r->status;
4917 }
4918
4919 if (to_proxyreq) {
4920 /* it should be go on as an internal proxy request */
4921
4922 /* check if the proxy module is enabled, so
4923 * we can actually use it!
4924 */
4925 if (!proxy_available) {
4927 "attempt to make remote request from mod_rewrite "
4928 "without proxy enabled: %s", r->filename);
4929 return HTTP_FORBIDDEN;
4930 }
4931
4932 if (rulestatus == ACTION_NOESCAPE) {
4933 apr_table_setn(r->notes, "proxy-nocanon", "1");
4934 }
4935
4936 /* make sure the QUERY_STRING and
4937 * PATH_INFO parts get incorporated
4938 */
4939 if (r->path_info != NULL) {
4941 r->path_info, NULL);
4942 }
4943 if ((r->args != NULL)
4944 && ((r->proxyreq == PROXYREQ_PROXY)
4945 || (rulestatus == ACTION_NOESCAPE))) {
4946 /* see proxy_http:proxy_http_canon() */
4948 "?", r->args, NULL);
4949 }
4950
4951 /* now make sure the request gets handled by the proxy handler */
4952 if (PROXYREQ_NONE == r->proxyreq) {
4954 }
4955 r->handler = "proxy-server";
4956
4957 rewritelog((r, 1, NULL, "go-ahead with proxy request %s [OK]",
4958 r->filename));
4959 return OK;
4960 }
4961 else if (skip_absolute > 0) {
4962 int n;
4963
4964 /* it was finally rewritten to a remote URL */
4965
4966 if (rulestatus != ACTION_NOESCAPE) {
4967 rewritelog((r, 1, NULL, "escaping %s for redirect",
4968 r->filename));
4970 }
4971
4972 /* append the QUERY_STRING part */
4973 if (r->args) {
4974 char *escaped_args = NULL;
4976 (oargs && !strcmp(r->args, oargs)));
4977
4978 r->filename = apr_pstrcat(r->pool, r->filename, "?",
4979 noescape
4980 ? r->args
4981 : (escaped_args =
4982 ap_escape_uri(r->pool, r->args)),
4983 NULL);
4984
4985 rewritelog((r, 1, NULL, "%s %s to query string for redirect %s",
4986 noescape ? "copying" : "escaping",
4987 r->args ,
4988 noescape ? "" : escaped_args));
4989 }
4990
4991 /* determine HTTP redirect response code */
4993 n = r->status;
4994 r->status = HTTP_OK; /* make Apache kernel happy */
4995 }
4996 else {
4998 }
4999
5000 /* now do the redirection */
5001 apr_table_setn(r->headers_out, "Location", r->filename);
5002 rewritelog((r, 1, NULL, "redirect to %s [REDIRECT/%d]", r->filename,
5003 n));
5004
5005 return n;
5006 }
5007 else if (flen > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
5008 /*
5009 * Hack because of underpowered API: passing the current
5010 * rewritten filename through to other URL-to-filename handlers
5011 * just as it were the requested URL. This is to enable
5012 * post-processing by mod_alias, etc. which always act on
5013 * r->uri! The difference here is: We do not try to
5014 * add the document root
5015 */
5016 r->uri = apr_pstrdup(r->pool, r->filename+12);
5017 return DECLINED;
5018 }
5019 else {
5020 /* it was finally rewritten to a local path */
5021 const char *uri_reduced = NULL;
5022
5023 /* expand "/~user" prefix */
5024#if APR_HAS_USER
5026#endif
5027 rewritelog((r, 2, NULL, "local path result: %s", r->filename));
5028
5029 /* the filename must be either an absolute local path or an
5030 * absolute local URL.
5031 */
5032 if ( *r->filename != '/'
5034 return HTTP_BAD_REQUEST;
5035 }
5036
5037 /* We have r->filename as a path in a server-context rewrite without
5038 * the PT flag. The historical behavior is to treat it as a verbatim
5039 * filesystem path iff the first component of the path exists and is
5040 * readable by httpd. Otherwise, it is interpreted as DocumentRoot
5041 * relative.
5042 *
5043 * NOTICE:
5044 * We cannot leave out the prefix_stat because
5045 * - If we always prefix with document_root
5046 * then no absolute path can could ever be used in
5047 * a substitution. e.g. emulating an Alias.
5048 * - If we never prefix with document_root
5049 * then the files under document_root have to
5050 * be references directly and document_root
5051 * gets never used and will be a dummy parameter -
5052 * this is also bad.
5053 * - Later addition: This part is questionable.
5054 * If we had never prefixed, users would just
5055 * need %{DOCUMENT_ROOT} in substitutions or the
5056 * [PT] flag.
5057 *
5058 * BUT:
5059 * Under real Unix systems this is no perf problem,
5060 * because we only do stat() on the first directory
5061 * and this gets cached by the kernel for along time!
5062 */
5063
5064 if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
5065 uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced");
5066 }
5067
5068 if (!prefix_stat(r, r->filename, r->pool,
5070 || uri_reduced != NULL) {
5071 int res;
5072 char *tmp = r->uri;
5073
5074 r->uri = r->filename;
5076 r->uri = tmp;
5077
5078 if (res != OK) {
5079 rewritelog((r, 1, NULL, "prefixing with document_root of %s"
5080 " FAILED", r->filename));
5081
5082 return res;
5083 }
5084
5085 rewritelog((r, 2, NULL, "prefixed with document_root to %s",
5086 r->filename));
5087 }
5088
5089 rewritelog((r, 1, NULL, "go-ahead with %s [OK]", r->filename));
5090 return OK;
5091 }
5092 }
5093 else {
5094 rewritelog((r, 1, NULL, "pass through %s", r->filename));
5095 return DECLINED;
5096 }
5097}
5098
5099/*
5100 * Fixup hook
5101 * [RewriteRules in directory context]
5102 */
5104{
5105 rewrite_perdir_conf *dconf;
5106 char *cp;
5107 char *cp2;
5108 const char *ccp;
5109 apr_size_t l;
5110 int rulestatus;
5111 int n;
5112 char *ofilename, *oargs;
5113 int is_proxyreq;
5114 void *skipdata;
5116
5118 &rewrite_module);
5119
5120 /* if there is no per-dir config we return immediately */
5121 if (dconf == NULL) {
5122 return DECLINED;
5123 }
5124
5125 /*
5126 * only do something under runtime if the engine is really enabled,
5127 * for this directory, else return immediately!
5128 */
5129 if (dconf->state == ENGINE_DISABLED) {
5130 return DECLINED;
5131 }
5132
5133 /* if there are no real (i.e. no RewriteRule directives!)
5134 per-dir config of us, we return also immediately */
5135 if (dconf->directory == NULL) {
5136 return DECLINED;
5137 }
5138
5139 /*
5140 * Proxy request?
5141 */
5143 && !strncmp(r->filename, "proxy:", 6));
5144
5145 /*
5146 * .htaccess file is called before really entering the directory, i.e.:
5147 * URL: http://localhost/foo and .htaccess is located in foo directory
5148 * Ignore such attempts, allowing mod_dir to direct the client to the
5149 * canonical URL. This can be controlled with the AllowNoSlash option.
5150 */
5151 if (!is_proxyreq && !(dconf->options & OPTION_NOSLASH)) {
5152 l = strlen(dconf->directory) - 1;
5153 if (r->filename && strlen(r->filename) == l &&
5154 (dconf->directory)[l] == '/' &&
5155 !strncmp(r->filename, dconf->directory, l)) {
5156 return DECLINED;
5157 }
5158 }
5159
5160 /* END flag was used as a RewriteRule flag on this request */
5162 if (skipdata != NULL) {
5163 rewritelog((r, 8, dconf->directory, "Declining, no further rewriting due to END flag"));
5164 return DECLINED;
5165 }
5166
5167 /*
5168 * Do the Options check after engine check, so
5169 * the user is able to explicitly turn RewriteEngine Off.
5170 */
5172 /* FollowSymLinks is mandatory! */
5174 "Options FollowSymLinks and SymLinksIfOwnerMatch are both off, "
5175 "so the RewriteRule directive is also forbidden "
5176 "due to its similar ability to circumvent directory restrictions : "
5177 "%s", r->filename);
5178 return HTTP_FORBIDDEN;
5179 }
5180
5181 /*
5182 * remember the current filename before rewriting for later check
5183 * to prevent deadlooping because of internal redirects
5184 * on final URL/filename which can be equal to the initial one.
5185 * also, we'll restore original r->filename if we decline this
5186 * request
5187 */
5188 ofilename = r->filename;
5189 oargs = r->args;
5190
5191 if (r->filename == NULL) {
5192 r->filename = apr_pstrdup(r->pool, r->uri);
5193 rewritelog((r, 2, dconf->directory, "init rewrite engine with"
5194 " requested uri %s", r->filename));
5195 }
5196
5197 /*
5198 * now apply the rules ...
5199 */
5201 if (rulestatus) {
5203 int to_proxyreq = 0;
5204 int will_escape = 0;
5205
5206 l = strlen(r->filename);
5207 to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0;
5209
5210 if (r->args
5211 && !will_escape
5212 && *(ap_scan_vchar_obstext(r->args))) {
5213 /*
5214 * We have a raw control character or a ' ' in r->args.
5215 * Correct encoding was missed.
5216 */
5218 "Rewritten query string contains control "
5219 "characters or spaces");
5220 return HTTP_FORBIDDEN;
5221 }
5222
5223 if (ACTION_STATUS == rulestatus) {
5224 int n = r->status;
5225
5226 r->status = HTTP_OK;
5227 return n;
5228 }
5229 else if (ACTION_STATUS_SET == rulestatus) {
5230 return r->status;
5231 }
5232
5233 if (to_proxyreq) {
5234 /* it should go on as an internal proxy request */
5235
5236 /* make sure the QUERY_STRING and
5237 * PATH_INFO parts get incorporated
5238 * (r->path_info was already appended by the
5239 * rewriting engine because of the per-dir context!)
5240 */
5241 if (r->args != NULL) {
5242 /* see proxy_http:proxy_http_canon() */
5244 "?", r->args, NULL);
5245 }
5246
5247 /* now make sure the request gets handled by the proxy handler */
5248 if (PROXYREQ_NONE == r->proxyreq) {
5250 }
5251 r->handler = "proxy-server";
5252
5253 rewritelog((r, 1, dconf->directory, "go-ahead with proxy request "
5254 "%s [OK]", r->filename));
5255 return OK;
5256 }
5257 else if (skip_absolute > 0) {
5258 /* it was finally rewritten to a remote URL */
5259
5260 /* because we are in a per-dir context
5261 * first try to replace the directory with its base-URL
5262 * if there is a base-URL available
5263 */
5264 if (dconf->baseurl != NULL) {
5265 /* skip 'scheme://' */
5266 cp = r->filename + skip_absolute;
5267
5268 if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) {
5269 rewritelog((r, 2, dconf->directory,
5270 "trying to replace prefix %s with %s",
5271 dconf->directory, dconf->baseurl));
5272
5273 /* I think, that hack needs an explanation:
5274 * well, here is it:
5275 * mod_rewrite was written for unix systems, were
5276 * absolute file-system paths start with a slash.
5277 * URL-paths _also_ start with slashes, so they
5278 * can be easily compared with system paths.
5279 *
5280 * the following assumes, that the actual url-path
5281 * may be prefixed by the current directory path and
5282 * tries to replace the system path with the RewriteBase
5283 * URL.
5284 * That assumption is true if we use a RewriteRule like
5285 *
5286 * RewriteRule ^foo bar [R]
5287 *
5288 * (see apply_rewrite_rule function)
5289 * However on systems that don't have a / as system
5290 * root this will never match, so we skip the / after the
5291 * hostname and compare/substitute only the stuff after it.
5292 *
5293 * (note that cp was already increased to the right value)
5294 */
5295 cp2 = subst_prefix_path(r, cp, (*dconf->directory == '/')
5296 ? dconf->directory + 1
5297 : dconf->directory,
5298 dconf->baseurl + 1);
5299 if (strcmp(cp2, cp) != 0) {
5300 *cp = '\0';
5302 cp2, NULL);
5303 }
5304 }
5305 }
5306
5307 /* now prepare the redirect... */
5308 if (rulestatus != ACTION_NOESCAPE) {
5309 rewritelog((r, 1, dconf->directory, "escaping %s for redirect",
5310 r->filename));
5312 }
5313
5314 /* append the QUERY_STRING part */
5315 if (r->args) {
5316 char *escaped_args = NULL;
5318 (oargs && !strcmp(r->args, oargs)));
5319
5320 r->filename = apr_pstrcat(r->pool, r->filename, "?",
5321 noescape
5322 ? r->args
5324 NULL);
5325
5326 rewritelog((r, 1, dconf->directory, "%s %s to query string for redirect %s",
5327 noescape ? "copying" : "escaping",
5328 r->args ,
5329 noescape ? "" : escaped_args));
5330 }
5331
5332 /* determine HTTP redirect response code */
5334 n = r->status;
5335 r->status = HTTP_OK; /* make Apache kernel happy */
5336 }
5337 else {
5339 }
5340
5341 /* now do the redirection */
5342 apr_table_setn(r->headers_out, "Location", r->filename);
5343 rewritelog((r, 1, dconf->directory, "redirect to %s [REDIRECT/%d]",
5344 r->filename, n));
5345 return n;
5346 }
5347 else {
5348 const char *tmpfilename = NULL;
5349 /* it was finally rewritten to a local path */
5350
5351 /* if someone used the PASSTHROUGH flag in per-dir
5352 * context we just ignore it. It is only useful
5353 * in per-server context
5354 */
5355 if (l > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
5356 r->filename = apr_pstrdup(r->pool, r->filename+12);
5357 }
5358
5359 /* the filename must be either an absolute local path or an
5360 * absolute local URL.
5361 */
5362 if ( *r->filename != '/'
5364 return HTTP_BAD_REQUEST;
5365 }
5366
5367 /* Check for deadlooping:
5368 * At this point we KNOW that at least one rewriting
5369 * rule was applied, but when the resulting URL is
5370 * the same as the initial URL, we are not allowed to
5371 * use the following internal redirection stuff because
5372 * this would lead to a deadloop.
5373 */
5374 if (ofilename != NULL && strcmp(r->filename, ofilename) == 0) {
5375 rewritelog((r, 1, dconf->directory, "initial URL equal rewritten"
5376 " URL: %s [IGNORING REWRITE]", r->filename));
5377 return OK;
5378 }
5379
5381
5382 /* if there is a valid base-URL then substitute
5383 * the per-dir prefix with this base-URL if the
5384 * current filename still is inside this per-dir
5385 * context. If not then treat the result as a
5386 * plain URL
5387 */
5388 if (dconf->baseurl != NULL) {
5389 rewritelog((r, 2, dconf->directory, "trying to replace prefix "
5390 "%s with %s", dconf->directory, dconf->baseurl));
5391
5393 dconf->directory,
5394 dconf->baseurl);
5395 }
5396 else {
5397 /* if no explicit base-URL exists we assume
5398 * that the directory prefix is also a valid URL
5399 * for this webserver and only try to remove the
5400 * document_root if it is prefix
5401 */
5402 if ((ccp = ap_document_root(r)) != NULL) {
5403 /* strip trailing slash */
5404 l = strlen(ccp);
5405 if (ccp[l-1] == '/') {
5406 --l;
5407 }
5408 if (!strncmp(r->filename, ccp, l) &&
5409 r->filename[l] == '/') {
5410 rewritelog((r, 2,dconf->directory, "strip document_root"
5411 " prefix: %s -> %s", r->filename,
5412 r->filename+l));
5413
5415 }
5416 }
5417 }
5418
5419 /* No base URL, or r->filename wasn't still under dconf->directory
5420 * or, r->filename wasn't still under the document root.
5421 * If there's a context document root AND a context prefix, and
5422 * the context document root is a prefix of r->filename, replace.
5423 * This allows a relative substitution on a path found by mod_userdir
5424 * or mod_alias without baking in a RewriteBase.
5425 */
5426 if (tmpfilename == r->filename &&
5427 !(dconf->options & OPTION_IGNORE_CONTEXT_INFO)) {
5428 if ((ccp = ap_context_document_root(r)) != NULL) {
5429 const char *prefix = ap_context_prefix(r);
5430 if (prefix != NULL) {
5431 rewritelog((r, 2, dconf->directory, "trying to replace "
5432 "context docroot %s with context prefix %s",
5433 ccp, prefix));
5435 ccp, prefix);
5436 }
5437 }
5438 }
5439
5440 apr_table_setn(r->notes, "redirect-keeps-vary", "");
5441
5442 /* now initiate the internal redirect */
5443 rewritelog((r, 1, dconf->directory, "internal redirect with %s "
5444 "[INTERNAL REDIRECT]", r->filename));
5445 r->filename = apr_pstrcat(r->pool, "redirect:", r->filename, NULL);
5447 return OK;
5448 }
5449 }
5450 else {
5451 rewritelog((r, 1, dconf->directory, "pass through %s", r->filename));
5452 r->filename = ofilename;
5453 return DECLINED;
5454 }
5455}
5456
5457/*
5458 * MIME-type hook
5459 * [T=...,H=...] execution
5460 */
5462{
5463 const char *t;
5464
5465 /* type */
5467 if (t && *t) {
5468 rewritelog((r, 1, NULL, "force filename %s to have MIME-type '%s'",
5469 r->filename, t));
5470
5472 }
5473
5474 /* handler */
5476 if (t && *t) {
5477 rewritelog((r, 1, NULL, "force filename %s to have the "
5478 "Content-handler '%s'", r->filename, t));
5479
5480 r->handler = t;
5481 }
5482
5483 return OK;
5484}
5485
5486
5487/*
5488 * "content" handler for internal redirects
5489 */
5491{
5493 return DECLINED;
5494 }
5495
5496 /* just make sure that we are really meant! */
5497 if (strncmp(r->filename, "redirect:", 9) != 0) {
5498 return DECLINED;
5499 }
5500
5501 /* now do the internal redirect */
5503 r->args ? "?" : NULL, r->args, NULL), r);
5504
5505 /* and return gracefully */
5506 return OK;
5507}
5508
5509
5510/*
5511 * +-------------------------------------------------------+
5512 * | |
5513 * | Module paraphernalia
5514 * | |
5515 * +-------------------------------------------------------+
5516 */
5517
5518static const command_rec command_table[] = {
5519 AP_INIT_FLAG( "RewriteEngine", cmd_rewriteengine, NULL, OR_FILEINFO,
5520 "On or Off to enable or disable (default) the whole "
5521 "rewriting engine"),
5523 "List of option strings to set"),
5525 "the base URL of the per-directory context"),
5527 "an input string and a to be applied regexp-pattern"),
5529 "an URL-applied regexp-pattern and a substitution URL"),
5531 "a mapname and a filename and options"),
5532 { NULL }
5533};
5534
5536{
5537 apr_hash_set(mapfunc_hash, name, strlen(name), (const void *)func);
5538}
5539
5541{
5542 /* fixup after mod_proxy, so that the proxied url will not
5543 * escaped accidentally by mod_proxy's fixup.
5544 */
5545 static const char * const aszPre[]={ "mod_proxy.c", NULL };
5546
5547 /* make the hashtable before registering the function, so that
5548 * other modules are prevented from accessing uninitialized memory.
5549 */
5552
5557
5561}
5562
5563 /* the main config structure */
5566 config_perdir_create, /* create per-dir config structures */
5567 config_perdir_merge, /* merge per-dir config structures */
5568 config_server_create, /* create per-server config structures */
5569 config_server_merge, /* merge per-server config structures */
5570 command_table, /* table of config file commands */
5571 register_hooks /* register hooks */
5572};
5573
5574/*EOF*/
Symbol export macros and hook functions.
Expression parser.
int n
Definition ap_regex.h:278
int int const char ** match
Definition ap_regex.h:279
#define AP_REG_ICASE
Definition ap_regex.h:73
const char apr_size_t len
Definition ap_regex.h:187
#define AP_REG_EXTENDED
Definition ap_regex.h:78
#define AP_REWRITE_LOG(arg0, arg1, arg2, arg3, arg4)
#define FALSE
Definition abts.h:35
APR-UTIL DBD library.
APR-UTIL DBM library.
#define TEST_CHAR(c, f)
Definition apr_escape.c:39
APR Global Locking Routines.
APR Hash Tables.
APR general purpose library routines.
APR Signal Handling.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
apr_array_append(apr_pool_t *p, const apr_array_header_t *first, const apr_array_header_t *second)
Definition apr_tables.c:213
APR Thread Mutex Routines.
APR User ID Services.
APR Versioning Interface.
APR Standard Headers Support.
APR-util Versioning Interface.
static sem_id lock
Definition threadpriv.c:21
static apr_pool_t * pconf
Definition event.c:441
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
#define AP_INIT_RAW_ARGS(directive, func, mconfig, where, help)
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
void ap_hook_pre_config(ap_HOOK_pre_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:91
request_rec int int apr_table_t const char * path
request_rec * r
void ap_hook_child_init(ap_HOOK_child_init_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:167
#define AP_INIT_TAKE23(directive, func, mconfig, where, help)
#define ap_default_port(r)
Definition httpd.h:292
#define ap_http_scheme(r)
Definition httpd.h:297
#define AP_MAX_REG_MATCH
Definition httpd.h:309
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define ap_is_default_port(port, r)
Definition httpd.h:287
const char * ap_get_server_banner(void)
Definition core.c:3593
#define OPT_SYM_OWNER
Definition http_core.h:84
#define OPT_SYM_LINKS
Definition http_core.h:76
apr_port_t ap_get_server_port(const request_rec *r)
Definition core.c:1199
int ap_state_query(int query_code)
Definition core.c:5378
const char * ap_get_server_name(request_rec *r)
Definition core.c:1145
#define AP_SQ_MS_CREATE_CONFIG
Definition http_core.h:1051
int ap_core_translate(request_rec *r)
Definition core.c:4756
#define AP_SQ_MAIN_STATE
Definition http_core.h:1030
const char * ap_document_root(request_rec *r)
Definition core.c:829
const char * ap_get_remote_logname(request_rec *r)
Definition core.c:1121
const char * ap_get_server_name_for_url(request_rec *r)
Definition core.c:1187
int ap_allow_options(request_rec *r)
Definition core.c:771
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_R_IS_LEVEL(r, level)
Definition http_log.h:229
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
#define MODULE_MAGIC_NUMBER_MAJOR
Definition ap_mmn.h:611
#define MODULE_MAGIC_NUMBER_MINOR
Definition ap_mmn.h:613
apr_status_t ap_global_mutex_create(apr_global_mutex_t **mutex, const char **name, const char *type, const char *instance_id, server_rec *server, apr_pool_t *pool, apr_int32_t options)
Definition util_mutex.c:407
apr_status_t ap_mutex_register(apr_pool_t *pconf, const char *type, const char *default_dir, apr_lockmech_e default_mech, apr_int32_t options)
Definition util_mutex.c:254
int ap_index_of_response(int status)
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
const char * ap_ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name)
Definition ssl.c:186
int ap_ssl_conn_is_ssl(conn_rec *c)
Definition ssl.c:90
void ap_hook_translate_name(ap_HOOK_translate_name_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:81
request_rec * ap_sub_req_lookup_uri(const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2297
request_rec * ap_sub_req_lookup_file(const char *new_file, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2433
void ap_hook_fixups(ap_HOOK_fixups_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:87
void ap_destroy_sub_req(request_rec *r)
Definition request.c:2547
void ap_internal_redirect(const char *new_uri, request_rec *r)
const char apr_port_t port
Definition http_vhost.h:125
void * dummy
Definition http_vhost.h:62
const char * host
Definition http_vhost.h:124
apr_brigade_flush void * ctx
apr_bucket apr_bucket_brigade * a
const char const apr_dbd_driver_t ** driver
Definition apr_dbd.h:106
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
struct apr_dbd_prepared_t apr_dbd_prepared_t
Definition apr_dbd.h:87
apr_pool_t apr_dbd_t const char const char * label
Definition apr_dbd.h:397
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
struct apr_dbd_results_t apr_dbd_results_t
Definition apr_dbd.h:85
apr_pool_t apr_dbd_results_t apr_dbd_row_t ** row
Definition apr_dbd.h:320
struct apr_dbd_row_t apr_dbd_row_t
Definition apr_dbd.h:86
#define APR_DBM_READONLY
Definition apr_dbm.h:56
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char apr_ssize_t slen
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
const void apr_size_t int colon
Definition apr_escape.h:355
const char apr_ssize_t int quote
Definition apr_escape.h:323
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
const char *const * aszPre
Definition apr_hooks.h:345
#define APR_HOOK_LAST
Definition apr_hooks.h:305
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define APR_REGISTER_OPTIONAL_FN(name)
apr_redis_t * rc
Definition apr_redis.h:173
const char * uri
Definition apr_uri.h:159
apr_text_header const char * text
Definition apr_xml.h:78
#define AP_EXPR_FLAG_DONT_VARY
Definition ap_expr.h:61
#define ap_expr_parse_cmd(cmd, expr, flags, err, lookup_fn)
Definition ap_expr.h:340
int ap_expr_exec_re(request_rec *r, const ap_expr_info_t *expr, apr_size_t nmatch, ap_regmatch_t *pmatch, const char **source, const char **err)
#define RSRC_CONF
#define OR_FILEINFO
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_SEE_OTHER
Definition httpd.h:503
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_MOVED_TEMPORARILY
Definition httpd.h:502
#define ap_is_HTTP_REDIRECT(x)
Definition httpd.h:552
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_MOVED_PERMANENTLY
Definition httpd.h:501
#define HTTP_GONE
Definition httpd.h:518
ap_dbd_t * ap_dbd_acquire(request_rec *r)
Definition mod_dbd.c:929
void ap_dbd_prepare(server_rec *s, const char *query, const char *label)
Definition mod_dbd.c:313
#define REWRITE_REDIRECT_HANDLER_NAME
Definition mod_rewrite.h:32
char *() rewrite_mapfunc_t(request_rec *r, char *key)
Definition mod_rewrite.h:35
#define M_CONNECT
Definition httpd.h:596
#define STANDARD20_MODULE_STUFF
#define ap_strchr(s, c)
Definition httpd.h:2351
#define AP_IS_SLASH(s)
Definition httpd.h:2698
#define ap_strrchr(s, c)
Definition httpd.h:2355
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
char * ap_strcasestr(const char *s1, const char *s2)
Definition util.c:289
#define ap_strstr_c(s, c)
Definition httpd.h:2361
#define ap_strchr_c(s, c)
Definition httpd.h:2353
const char * ap_context_prefix(request_rec *r)
Definition core.c:839
const char * ap_context_document_root(request_rec *r)
Definition core.c:848
void ap_str_toupper(char *s)
Definition util.c:2418
#define PROXYREQ_NONE
Definition httpd.h:1133
char * ap_make_dirstr_parent(apr_pool_t *p, const char *s)
Definition util.c:692
const char * ap_scan_vchar_obstext(const char *ptr)
Definition util.c:1674
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define PROXYREQ_PROXY
Definition httpd.h:1134
void ap_str_tolower(char *s)
Definition util.c:2410
#define ap_assert(exp)
Definition httpd.h:2271
int ap_unescape_url(char *url)
Definition util.c:1939
#define PROXYREQ_REVERSE
Definition httpd.h:1135
ap_regex_t * ap_pregcomp(apr_pool_t *p, const char *pattern, int cflags)
Definition util.c:262
apr_uint32_t ap_random_pick(apr_uint32_t min, apr_uint32_t max)
Definition core.c:5485
char * ap_getword_conf(apr_pool_t *p, const char **line)
Definition util.c:833
int ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
Definition util.c:229
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
const char * input
Definition apr_cstr.h:93
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isalnum(c)
Definition apr_lib.h:203
#define apr_isdigit(c)
Definition apr_lib.h:209
#define apr_isalpha(c)
Definition apr_lib.h:205
const char * value
Definition apr_env.h:51
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
@ APR_LNK
@ APR_REG
@ APR_DIR
const char * key
void apr_size_t * nbytes
apr_seek_where_t where
const char apr_int32_t flag
const char * format
void * data
const char apr_file_t * file
#define APR_READ
Definition apr_file_io.h:93
#define APR_BUFFERED
#define APR_UEXECUTE
#define APR_GEXECUTE
#define APR_WEXECUTE
#define APR_OS_DEFAULT
#define APR_FINFO_MIN
#define APR_FINFO_USER
const char * fname
#define APR_FINFO_GROUP
#define APR_FINFO_LINK
#define APR_FINFO_PROT
#define APR_FILEPATH_NOTRELATIVE
#define APR_FILEPATH_NOTABOVEROOT
#define APR_FILEPATH_TRUENAME
apr_array_header_t ** result
int strcasecmp(const char *a, const char *b)
int strncasecmp(const char *a, const char *b, size_t n)
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_vformatter_buff_t const char * fmt
Definition apr_lib.h:175
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_vformatter_buff_t const char va_list ap
Definition apr_lib.h:176
char const *const char const *const ** env
apr_sockaddr_t apr_sockaddr_t apr_sockaddr_t * source
apr_sockaddr_t * addr
apr_interval_time_t t
apr_interval_time_t apr_pollcb_cb_t func
Definition apr_poll.h:422
apr_pool_t * b
Definition apr_pools.h:529
const char apr_status_t(*) apr_pool_t *poo __attribute__)((nonnull(2, 4)))
Definition apr_pools.h:567
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
@ APR_LOCK_DEFAULT
const char * sep
const char * s
Definition apr_strings.h:95
int nelts
Definition apr_tables.h:122
apr_int32_t apr_int32_t apr_int32_t err
#define APR_FULL_BLOCK
apr_cmdtype_e cmd
#define APR_NO_PIPE
const char * progname
int int status
@ APR_KILL_AFTER_TIMEOUT
@ APR_PROGRAM
apr_size_t apr_size_t const char apr_time_exp_t * tm
Definition apr_time.h:221
APR_DECLARE_DATA const char apr_month_snames[12][4]
Definition timestr.c:33
APR_DECLARE_DATA const char apr_day_snames[7][4]
Definition timestr.c:37
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_from_sec(sec)
Definition apr_time.h:78
const char * ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip)
Definition core.c:957
const char * ap_get_useragent_host(request_rec *req, int type, int *str_is_ip)
Definition core.c:1036
#define REMOTE_NOLOOKUP
Definition http_core.h:111
#define REMOTE_NAME
Definition http_core.h:106
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
SSL protocol handling.
Virtual Host package.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
Database Access Extension Module for Apache.
const char * argv[3]
static int parse(server_rec *, apr_pool_t *p, char *, int)
static int prefix_stat(request_rec *r, const char *path, apr_pool_t *pool, rewriterule_entry *lastsub)
static void * config_perdir_merge(apr_pool_t *p, void *basev, void *overridesv)
static void set_cache_value(const char *name, apr_time_t t, char *key, char *val)
#define ENGINE_ENABLED
static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
static int hook_uri2file(request_rec *r)
#define MAPTYPE_PRG
static rule_return_type apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx)
#define OPTION_ANYURI
static const char * really_last_key
static int pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
#define RULEFLAG_UNSAFE_ALLOW3F
static char * rewrite_mapfunc_unescape(request_rec *r, char *key)
#define OPTION_UNSAFE_PREFIX_STAT
static const command_rec command_table[]
#define OPTION_IGNORE_CONTEXT_INFO
#define MAPTYPE_RND
#define OPTION_NONE
static int rewrite_lock_needed
#define RULEFLAG_PROXY
static ap_dbd_t *(* dbd_acquire)(request_rec *)
#define RULEFLAG_STATUS
static void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx)
#define OPTION_NOSLASH
static const char * lookup_header(const char *name, rewrite_ctx *ctx)
#define RULEFLAG_NEWROUND
static APR_INLINE int compare_lexicography(char *a, char *b)
static void rewrite_child_errfn(apr_pool_t *p, apr_status_t err, const char *desc)
static int proxy_available
#define REWRITE_FORCED_MIMETYPE_NOTEVAR
#define RULEFLAG_DISCARDPATHINFO
pattern_type
@ CONDPAT_AP_EXPR
@ CONDPAT_REGEX
@ CONDPAT_FILE_XBIT
@ CONDPAT_LU_FILE
@ CONDPAT_INT_GT
@ CONDPAT_FILE_DIR
@ CONDPAT_INT_LE
@ CONDPAT_STR_EQ
@ CONDPAT_STR_GE
@ CONDPAT_FILE_LINK
@ CONDPAT_INT_EQ
@ CONDPAT_INT_GE
@ CONDPAT_FILE_EXISTS
@ CONDPAT_INT_LT
@ CONDPAT_STR_LT
@ CONDPAT_STR_GT
@ CONDPAT_LU_URL
@ CONDPAT_STR_LE
@ CONDPAT_FILE_SIZE
static char * rewrite_mapfunc_escape(request_rec *r, char *key)
#define REWRITE_MAX_ROUNDS
static const char * rewritemap_mutex_type
static apr_status_t rewritelock_remove(void *data)
static void reduce_uri(request_rec *r)
static char * do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p)
static apr_status_t rewritemap_program_child(apr_pool_t *p, const char *progname, char **argv, const char *user, const char *group, apr_file_t **fpout, apr_file_t **fpin)
static APR_INLINE const char * la_u(rewrite_ctx *ctx)
#define RULEFLAG_CHAIN
#define RULEFLAG_NOSUB
static const char * cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1, const char *a2, const char *a3)
#define RULEFLAG_ESCAPENOPLUS
#define ENGINE_DISABLED
static APR_INLINE void force_type_handler(rewriterule_entry *p, rewrite_ctx *ctx)
static const char c2x_table[]
static const char * cmd_rewritecond_setflag(apr_pool_t *p, void *_cfg, char *key, char *val)
#define RULEFLAG_ESCAPEBACKREF
static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p)
#define CONDFLAG_NOTMATCH
static char * select_random_value_part(request_rec *r, char *value)
#define RULEFLAG_FORCEREDIRECT
static char * lookup_map_dbmfile(request_rec *r, const char *file, const char *dbmtype, char *key)
static const char * cmd_parseflagfield(apr_pool_t *p, void *cfg, char *key, const char *(*parse)(apr_pool_t *, void *, char *, char *))
#define SMALL_EXPANSION
static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3)
#define REWRITE_FORCED_HANDLER_NOTEVAR
#define RAND_MAX
#define OPTION_IGNORE_INHERIT
static char * get_cache_value(const char *name, apr_time_t t, char *key, apr_pool_t *p)
static void * config_server_merge(apr_pool_t *p, void *basev, void *overridesv)
static APR_INLINE unsigned char * c2x(unsigned what, unsigned char prefix, unsigned char *where)
#define REWRITE_MAX_TXT_MAP_LINE
#define OPTION_INHERIT_DOWN_BEFORE
#define RULEFLAG_ESCAPECTLS
static char * rewrite_mapfunc_tolower(request_rec *r, char *key)
static char * lookup_variable(char *var, rewrite_ctx *ctx)
#define ACTION_STATUS_SET
#define MAPTYPE_DBM
static char * lookup_map_dbd(request_rec *r, char *key, const char *label)
#define REDIRECT_ENVVAR_SCRIPT_URL
static void * config_server_create(apr_pool_t *p, server_rec *s)
#define RIGHT_CURLY
static void register_hooks(apr_pool_t *p)
static const char * cmd_rewriteengine(cmd_parms *cmd, void *in_dconf, int flag)
static int is_absolute_path(const char *path)
static char * lookup_map_program(request_rec *r, apr_file_t *fpin, apr_file_t *fpout, char *key)
static unsigned is_absolute_uri(char *uri, int *supportsqs)
#define MAPTYPE_DBD
#define CONDFLAG_NONE
#define ENVVAR_SCRIPT_URL
#define OPTION_INHERIT
static const char * cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg, char *key, char *val)
#define RULEFLAG_QSLAST
static void(* dbd_prepare)(server_rec *, const char *, const char *)
#define OPTION_INHERIT_DOWN
static apr_hash_t * mapfunc_hash
#define ACTION_NOESCAPE
#define OPTION_INHERIT_BEFORE
#define RULEFLAG_UNC
#define REWRITE_PRG_MAP_BUF
#define ACTION_NORMAL
#define MAPTYPE_INT
#define CONDFLAG_NOVARY
static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, char *perdir, rewriterule_entry **lastsub)
static void add_cookie(request_rec *r, char *s)
#define RULEFLAG_UNSAFE_PREFIX_STAT
static int apply_rewrite_cond(rewritecond_entry *p, rewrite_ctx *ctx)
#define RULEFLAG_QSAPPEND
static char * subst_prefix_path(request_rec *r, char *input, const char *match, const char *subst)
#define RULEFLAG_NONE
static const char * cmd_rewriterule(cmd_parms *cmd, void *in_dconf, const char *in_str)
static void do_expand_env(data_item *env, rewrite_ctx *ctx)
#define LEFT_CURLY
static int init_cache(apr_pool_t *p)
static void fully_qualify_uri(request_rec *r)
static char * escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
static void splitout_queryargs(request_rec *r, int flags)
#define RULEFLAG_NOTMATCH
static char * lookup_map_txtfile(request_rec *r, const char *file, char *key)
#define RULEFLAG_NOCASE
static cache * cachep
#define RULEFLAG_PASSTHROUGH
#define MAPTYPE_DBD_CACHE
#define rewritelog(x)
#define RULEFLAG_QSDISCARD
#define RULEFLAG_QSNONE
#define MAPTYPE_TXT
#define OPTION_MERGEBASE
static char * escape_backref(apr_pool_t *p, const char *path, const char *escapeme, const char *noescapeme, int flags)
static int hook_fixup(request_rec *r)
#define CONDFLAG_NOCASE
#define subreq_ok(r)
static APR_INLINE char * find_char_in_curlies(char *s, int c)
static apr_global_mutex_t * rewrite_mapr_lock_acquire
#define ENVVAR_SCRIPT_URI
#define RULEFLAG_NOESCAPE
static char * rewrite_mapfunc_toupper(request_rec *r, char *key)
static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func)
static void do_rewritelog(request_rec *r, int level, char *perdir, const char *fmt,...) __attribute__((format(printf
static int hook_mimetype(request_rec *r)
static APR_INLINE char * find_closing_curly(char *s)
#define ACTION_STATUS
#define CONDFLAG_ORNEXT
#define RULEFLAG_IGNOREONSUBREQ
static const char * cmd_rewritecond(cmd_parms *cmd, void *in_dconf, const char *in_str)
rule_return_type
@ RULE_RC_MATCH
@ RULE_RC_NOMATCH
@ RULE_RC_STATUS_SET
@ RULE_RC_NOSUB
static char * lookup_map(request_rec *r, char *name, char *key)
static const char * cmd_rewriteoptions(cmd_parms *cmd, void *in_dconf, const char *option)
static void * config_perdir_create(apr_pool_t *p, char *path)
#define OPTION_LEGACY_PREFIX_DOCROOT
static void init_child(apr_pool_t *p, server_rec *s)
#define RULEFLAG_END
static int startsWith(request_rec *r, const char *haystack, const char *needle)
static const char * cmd_rewritebase(cmd_parms *cmd, void *in_dconf, const char *a1)
static int handler_redirect(request_rec *r)
#define RULEFLAG_LASTRULE
Rewrite Extension module for Apache.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
sconf
Definition mod_so.c:349
#define T_VCHAR_OBSTEXT
char * name
const apr_dbd_driver_t * driver
Definition mod_dbd.h:78
apr_hash_t * prepared
Definition mod_dbd.h:79
apr_dbd_t * handle
Definition mod_dbd.h:77
apr_gid_t group
apr_uid_t user
apr_int32_t valid
apr_int32_t family
apr_int32_t tm_sec
Definition apr_time.h:101
apr_int32_t tm_hour
Definition apr_time.h:105
apr_int32_t tm_year
Definition apr_time.h:111
apr_int32_t tm_min
Definition apr_time.h:103
apr_int32_t tm_wday
Definition apr_time.h:113
apr_int32_t tm_mday
Definition apr_time.h:107
apr_int32_t tm_mon
Definition apr_time.h:109
const char * source
ap_regmatch_t regmatch[10]
apr_pool_t * pool
apr_hash_t * maps
apr_time_t mtime
apr_hash_t * entries
apr_pool_t * pool
char * client_ip
Definition httpd.h:1171
char * local_ip
Definition httpd.h:1181
struct data_item * next
char * data
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
char * useragent_ip
Definition httpd.h:1101
request_rec * prev
Definition httpd.h:856
const char * handler
Definition httpd.h:994
apr_table_t * notes
Definition httpd.h:985
char * the_request
Definition httpd.h:866
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
apr_sockaddr_t * useragent_addr
Definition httpd.h:1100
char * filename
Definition httpd.h:1018
apr_time_t request_time
Definition httpd.h:886
char * unparsed_uri
Definition httpd.h:1014
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
apr_table_t * err_headers_out
Definition httpd.h:981
apr_finfo_t finfo
Definition httpd.h:1094
char * protocol
Definition httpd.h:879
request_rec * main
Definition httpd.h:860
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * method
Definition httpd.h:900
char * path_info
Definition httpd.h:1024
char * args
Definition httpd.h:1026
char * ap_auth_type
Definition httpd.h:1007
apr_table_t * headers_out
Definition httpd.h:978
apr_size_t len
const char * string
struct result_list * next
request_rec * r
const char * vary_this
const char * vary
const char * uri
char * perdir
backrefinfo briRR
backrefinfo briRC
const char * baseurl
unsigned int baseurl_set
unsigned int state_set
unsigned int options_set
apr_array_header_t * rewriterules
apr_array_header_t * rewriteconds
unsigned int state_set
apr_hash_t * rewritemaps
server_rec * server
unsigned int options_set
apr_array_header_t * rewriteconds
apr_array_header_t * rewriterules
char * pattern
ap_expr_info_t * expr
pattern_type ptype
char * input
int pskip
int flags
ap_regex_t * regexp
const char * cachename
const char * user
const char * dbmtype
apr_file_t * fpout
const char * datafile
const char * checkfile2
apr_file_t * fpin
char ** argv
int type
const char * dbdq
apr_file_t * fperr
const char * checkfile
const char * group
char * forced_mimetype
char * output
const char * noescapes
char * forced_handler
int forced_responsecode
data_item * env
ap_regex_t * regexp
int skip
const char * escapes
int maxrounds
int flags
char * pattern
apr_array_header_t * rewriteconds
data_item * cookie
A structure to store information for each virtual server.
Definition httpd.h:1322
int limit_req_line
Definition httpd.h:1391
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
char * server_admin
Definition httpd.h:1363
static apr_array_header_t * a1
Definition testtable.c:33
#define var
#define str
Apache Mutex support library.
int ap_matches_request_vhost(request_rec *r, const char *host, apr_port_t port)
Definition vhost.c:953