~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/mappers/mod_rewrite.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
58
#if APR_HAS_THREADS
 
59
#include "apr_thread_mutex.h"
 
60
#endif
 
61
 
 
62
#define APR_WANT_MEMFUNC
 
63
#define APR_WANT_STRFUNC
 
64
#define APR_WANT_IOVEC
 
65
#include "apr_want.h"
 
66
 
 
67
/* XXX: Do we really need these headers? */
 
68
#if APR_HAVE_UNISTD_H
 
69
#include <unistd.h>
 
70
#endif
 
71
#if APR_HAVE_SYS_TYPES_H
 
72
#include <sys/types.h>
 
73
#endif
 
74
#if APR_HAVE_STDARG_H
 
75
#include <stdarg.h>
 
76
#endif
 
77
#if APR_HAVE_STDLIB_H
 
78
#include <stdlib.h>
 
79
#endif
 
80
#if APR_HAVE_CTYPE_H
 
81
#include <ctype.h>
 
82
#endif
 
83
 
 
84
#include "ap_config.h"
 
85
#include "httpd.h"
 
86
#include "http_config.h"
 
87
#include "http_request.h"
 
88
#include "http_core.h"
 
89
#include "http_log.h"
 
90
#include "http_protocol.h"
 
91
#include "http_vhost.h"
 
92
 
 
93
#include "mod_ssl.h"
 
94
 
 
95
#include "mod_rewrite.h"
 
96
 
 
97
#ifdef AP_NEED_SET_MUTEX_PERMS
 
98
#include "unixd.h"
 
99
#endif
 
100
 
 
101
/*
 
102
 * in order to improve performance on running production systems, you
 
103
 * may strip all rewritelog code entirely from mod_rewrite by using the
 
104
 * -DREWRITELOG_DISABLED compiler option.
 
105
 *
 
106
 * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
 
107
 * responsible for answering all the mod_rewrite questions out there.
 
108
 */
 
109
#ifndef REWRITELOG_DISABLED
 
110
 
 
111
#define rewritelog(x) do_rewritelog x
 
112
#define REWRITELOG_MODE  ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
 
113
#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )
 
114
 
 
115
#else /* !REWRITELOG_DISABLED */
 
116
 
 
117
#define rewritelog(x)
 
118
 
 
119
#endif /* REWRITELOG_DISABLED */
 
120
 
 
121
/* remembered mime-type for [T=...] */
 
122
#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
 
123
#define REWRITE_FORCED_HANDLER_NOTEVAR  "rewrite-forced-handler"
 
124
 
 
125
#define ENVVAR_SCRIPT_URL "SCRIPT_URL"
 
126
#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
 
127
#define ENVVAR_SCRIPT_URI "SCRIPT_URI"
 
128
 
 
129
#define CONDFLAG_NONE               1<<0
 
130
#define CONDFLAG_NOCASE             1<<1
 
131
#define CONDFLAG_NOTMATCH           1<<2
 
132
#define CONDFLAG_ORNEXT             1<<3
 
133
 
 
134
#define RULEFLAG_NONE               1<<0
 
135
#define RULEFLAG_FORCEREDIRECT      1<<1
 
136
#define RULEFLAG_LASTRULE           1<<2
 
137
#define RULEFLAG_NEWROUND           1<<3
 
138
#define RULEFLAG_CHAIN              1<<4
 
139
#define RULEFLAG_IGNOREONSUBREQ     1<<5
 
140
#define RULEFLAG_NOTMATCH           1<<6
 
141
#define RULEFLAG_PROXY              1<<7
 
142
#define RULEFLAG_PASSTHROUGH        1<<8
 
143
#define RULEFLAG_QSAPPEND           1<<9
 
144
#define RULEFLAG_NOCASE             1<<10
 
145
#define RULEFLAG_NOESCAPE           1<<11
 
146
#define RULEFLAG_NOSUB              1<<12
 
147
#define RULEFLAG_STATUS             1<<13
 
148
 
 
149
/* return code of the rewrite rule
 
150
 * the result may be escaped - or not
 
151
 */
 
152
#define ACTION_NORMAL               1<<0
 
153
#define ACTION_NOESCAPE             1<<1
 
154
#define ACTION_STATUS               1<<2
 
155
 
 
156
 
 
157
#define MAPTYPE_TXT                 1<<0
 
158
#define MAPTYPE_DBM                 1<<1
 
159
#define MAPTYPE_PRG                 1<<2
 
160
#define MAPTYPE_INT                 1<<3
 
161
#define MAPTYPE_RND                 1<<4
 
162
 
 
163
#define ENGINE_DISABLED             1<<0
 
164
#define ENGINE_ENABLED              1<<1
 
165
 
 
166
#define OPTION_NONE                 1<<0
 
167
#define OPTION_INHERIT              1<<1
 
168
 
 
169
#ifndef RAND_MAX
 
170
#define RAND_MAX 32767
 
171
#endif
 
172
 
 
173
/* max cookie size in rfc 2109 */
 
174
/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
 
175
#define MAX_COOKIE_LEN 4096
 
176
 
 
177
/* max line length (incl.\n) in text rewrite maps */
 
178
#ifndef REWRITE_MAX_TXT_MAP_LINE
 
179
#define REWRITE_MAX_TXT_MAP_LINE 1024
 
180
#endif
 
181
 
 
182
/* buffer length for prg rewrite maps */
 
183
#ifndef REWRITE_PRG_MAP_BUF
 
184
#define REWRITE_PRG_MAP_BUF 1024
 
185
#endif
 
186
 
 
187
/* for better readbility */
 
188
#define LEFT_CURLY  '{'
 
189
#define RIGHT_CURLY '}'
 
190
 
 
191
/*
 
192
 * expansion result items on the stack to save some cycles
 
193
 *
 
194
 * (5 == about 2 variables like "foo%{var}bar%{var}baz")
 
195
 */
 
196
#define SMALL_EXPANSION 5
 
197
 
 
198
/*
 
199
 * check that a subrequest won't cause infinite recursion
 
200
 *
 
201
 * either not in a subrequest, or in a subrequest
 
202
 * and URIs aren't NULL and sub/main URIs differ
 
203
 */
 
204
#define subreq_ok(r) (!r->main || \
 
205
    (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
 
206
 
 
207
 
 
208
/*
 
209
 * +-------------------------------------------------------+
 
210
 * |                                                       |
 
211
 * |                 Types and Structures
 
212
 * |                                                       |
 
213
 * +-------------------------------------------------------+
 
214
 */
 
215
 
 
216
typedef struct {
 
217
    const char *datafile;          /* filename for map data files         */
 
218
    const char *dbmtype;           /* dbm type for dbm map data files     */
 
219
    const char *checkfile;         /* filename to check for map existence */
 
220
    const char *cachename;         /* for cached maps (txt/rnd/dbm)       */
 
221
    int   type;                    /* the type of the map                 */
 
222
    apr_file_t *fpin;              /* in  file pointer for program maps   */
 
223
    apr_file_t *fpout;             /* out file pointer for program maps   */
 
224
    apr_file_t *fperr;             /* err file pointer for program maps   */
 
225
    char *(*func)(request_rec *,   /* function pointer for internal maps  */
 
226
                  char *);
 
227
    char **argv;                   /* argv of the external rewrite map    */
 
228
} rewritemap_entry;
 
229
 
 
230
/* special pattern types for RewriteCond */
 
231
typedef enum {
 
232
    CONDPAT_REGEX = 0,
 
233
    CONDPAT_FILE_EXISTS,
 
234
    CONDPAT_FILE_SIZE,
 
235
    CONDPAT_FILE_LINK,
 
236
    CONDPAT_FILE_DIR,
 
237
    CONDPAT_FILE_XBIT,
 
238
    CONDPAT_LU_URL,
 
239
    CONDPAT_LU_FILE,
 
240
    CONDPAT_STR_GT,
 
241
    CONDPAT_STR_LT,
 
242
    CONDPAT_STR_EQ
 
243
} pattern_type;
 
244
 
 
245
typedef struct {
 
246
    char        *input;   /* Input string of RewriteCond   */
 
247
    char        *pattern; /* the RegExp pattern string     */
 
248
    ap_regex_t  *regexp;  /* the precompiled regexp        */
 
249
    int          flags;   /* Flags which control the match */
 
250
    pattern_type ptype;   /* pattern type                  */
 
251
} rewritecond_entry;
 
252
 
 
253
/* single linked list for env vars and cookies */
 
254
typedef struct data_item {
 
255
    struct data_item *next;
 
256
    char *data;
 
257
} data_item;
 
258
 
 
259
typedef struct {
 
260
    apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
 
261
    char      *pattern;              /* the RegExp pattern string             */
 
262
    ap_regex_t *regexp;              /* the RegExp pattern compilation        */
 
263
    char      *output;               /* the Substitution string               */
 
264
    int        flags;                /* Flags which control the substitution  */
 
265
    char      *forced_mimetype;      /* forced MIME type of substitution      */
 
266
    char      *forced_handler;       /* forced content handler of subst.      */
 
267
    int        forced_responsecode;  /* forced HTTP response status           */
 
268
    data_item *env;                  /* added environment variables           */
 
269
    data_item *cookie;               /* added cookies                         */
 
270
    int        skip;                 /* number of next rules to skip          */
 
271
} rewriterule_entry;
 
272
 
 
273
typedef struct {
 
274
    int           state;              /* the RewriteEngine state            */
 
275
    int           options;            /* the RewriteOption state            */
 
276
#ifndef REWRITELOG_DISABLED
 
277
    const char   *rewritelogfile;     /* the RewriteLog filename            */
 
278
    apr_file_t   *rewritelogfp;       /* the RewriteLog open filepointer    */
 
279
    int           rewriteloglevel;    /* the RewriteLog level of verbosity  */
 
280
#endif
 
281
    apr_hash_t         *rewritemaps;  /* the RewriteMap entries             */
 
282
    apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.)    */
 
283
    apr_array_header_t *rewriterules; /* the RewriteRule entries            */
 
284
    server_rec   *server;             /* the corresponding server indicator */
 
285
} rewrite_server_conf;
 
286
 
 
287
typedef struct {
 
288
    int           state;              /* the RewriteEngine state           */
 
289
    int           options;            /* the RewriteOption state           */
 
290
    apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.)   */
 
291
    apr_array_header_t *rewriterules; /* the RewriteRule entries           */
 
292
    char         *directory;          /* the directory where it applies    */
 
293
    const char   *baseurl;            /* the base-URL  where it applies    */
 
294
} rewrite_perdir_conf;
 
295
 
 
296
/* the (per-child) cache structures.
 
297
 */
 
298
typedef struct cache {
 
299
    apr_pool_t         *pool;
 
300
    apr_hash_t         *maps;
 
301
#if APR_HAS_THREADS
 
302
    apr_thread_mutex_t *lock;
 
303
#endif
 
304
} cache;
 
305
 
 
306
/* cached maps contain an mtime for the whole map and live in a subpool
 
307
 * of the cachep->pool. That makes it easy to forget them if necessary.
 
308
 */
 
309
typedef struct {
 
310
    apr_time_t mtime;
 
311
    apr_pool_t *pool;
 
312
    apr_hash_t *entries;
 
313
} cachedmap;
 
314
 
 
315
/* the regex structure for the
 
316
 * substitution of backreferences
 
317
 */
 
318
typedef struct backrefinfo {
 
319
    char *source;
 
320
    int nsub;
 
321
    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
 
322
} backrefinfo;
 
323
 
 
324
/* single linked list used for
 
325
 * variable expansion
 
326
 */
 
327
typedef struct result_list {
 
328
    struct result_list *next;
 
329
    apr_size_t len;
 
330
    const char *string;
 
331
} result_list;
 
332
 
 
333
/* context structure for variable lookup and expansion
 
334
 */
 
335
typedef struct {
 
336
    request_rec *r;
 
337
    const char  *uri;
 
338
    const char  *vary_this;
 
339
    const char  *vary;
 
340
    char        *perdir;
 
341
    backrefinfo briRR;
 
342
    backrefinfo briRC;
 
343
} rewrite_ctx;
 
344
 
 
345
/*
 
346
 * +-------------------------------------------------------+
 
347
 * |                                                       |
 
348
 * |                 static module data
 
349
 * |                                                       |
 
350
 * +-------------------------------------------------------+
 
351
 */
 
352
 
 
353
/* the global module structure */
 
354
module AP_MODULE_DECLARE_DATA rewrite_module;
 
355
 
 
356
/* rewritemap int: handler function registry */
 
357
static apr_hash_t *mapfunc_hash;
 
358
 
 
359
/* the cache */
 
360
static cache *cachep;
 
361
 
 
362
/* whether proxy module is available or not */
 
363
static int proxy_available;
 
364
 
 
365
/* whether random seed can be reaped */
 
366
static int rewrite_rand_init_done = 0;
 
367
 
 
368
/* Locks/Mutexes */
 
369
static const char *lockname;
 
370
static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
 
371
 
 
372
#ifndef REWRITELOG_DISABLED
 
373
static apr_global_mutex_t *rewrite_log_lock = NULL;
 
374
#endif
 
375
 
 
376
/* Optional functions imported from mod_ssl when loaded: */
 
377
static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL;
 
378
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL;
 
379
 
 
380
/*
 
381
 * +-------------------------------------------------------+
 
382
 * |                                                       |
 
383
 * |              rewriting logfile support
 
384
 * |                                                       |
 
385
 * +-------------------------------------------------------+
 
386
 */
 
387
 
 
388
#ifndef REWRITELOG_DISABLED
 
389
static char *current_logtime(request_rec *r)
 
390
{
 
391
    apr_time_exp_t t;
 
392
    char tstr[80];
 
393
    apr_size_t len;
 
394
 
 
395
    apr_time_exp_lt(&t, apr_time_now());
 
396
 
 
397
    apr_strftime(tstr, &len, sizeof(tstr), "[%d/%b/%Y:%H:%M:%S ", &t);
 
398
    apr_snprintf(tstr+len, sizeof(tstr)-len, "%c%.2d%.2d]",
 
399
                 t.tm_gmtoff < 0 ? '-' : '+',
 
400
                 t.tm_gmtoff / (60*60), t.tm_gmtoff % (60*60));
 
401
 
 
402
    return apr_pstrdup(r->pool, tstr);
 
403
}
 
404
 
 
405
static int open_rewritelog(server_rec *s, apr_pool_t *p)
 
406
{
 
407
    rewrite_server_conf *conf;
 
408
    const char *fname;
 
409
 
 
410
    conf = ap_get_module_config(s->module_config, &rewrite_module);
 
411
 
 
412
    /* - no logfile configured
 
413
     * - logfilename empty
 
414
     * - virtual log shared w/ main server
 
415
     */
 
416
    if (!conf->rewritelogfile || !*conf->rewritelogfile || conf->rewritelogfp) {
 
417
        return 1;
 
418
    }
 
419
 
 
420
    if (*conf->rewritelogfile == '|') {
 
421
        piped_log *pl;
 
422
 
 
423
        fname = ap_server_root_relative(p, conf->rewritelogfile+1);
 
424
        if (!fname) {
 
425
            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
 
426
                         "mod_rewrite: Invalid RewriteLog "
 
427
                         "path %s", conf->rewritelogfile+1);
 
428
            return 0;
 
429
        }
 
430
 
 
431
        if ((pl = ap_open_piped_log(p, fname)) == NULL) {
 
432
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
433
                         "mod_rewrite: could not open reliable pipe "
 
434
                         "to RewriteLog filter %s", fname);
 
435
            return 0;
 
436
        }
 
437
        conf->rewritelogfp = ap_piped_log_write_fd(pl);
 
438
    }
 
439
    else {
 
440
        apr_status_t rc;
 
441
 
 
442
        fname = ap_server_root_relative(p, conf->rewritelogfile);
 
443
        if (!fname) {
 
444
            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
 
445
                         "mod_rewrite: Invalid RewriteLog "
 
446
                         "path %s", conf->rewritelogfile);
 
447
            return 0;
 
448
        }
 
449
 
 
450
        if ((rc = apr_file_open(&conf->rewritelogfp, fname,
 
451
                                REWRITELOG_FLAGS, REWRITELOG_MODE, p))
 
452
                != APR_SUCCESS) {
 
453
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
 
454
                         "mod_rewrite: could not open RewriteLog "
 
455
                         "file %s", fname);
 
456
            return 0;
 
457
        }
 
458
    }
 
459
 
 
460
    return 1;
 
461
}
 
462
 
 
463
static void do_rewritelog(request_rec *r, int level, char *perdir,
 
464
                          const char *fmt, ...)
 
465
{
 
466
    rewrite_server_conf *conf;
 
467
    char *logline, *text;
 
468
    const char *rhost, *rname;
 
469
    apr_size_t nbytes;
 
470
    int redir;
 
471
    apr_status_t rv;
 
472
    request_rec *req;
 
473
    va_list ap;
 
474
 
 
475
    conf = ap_get_module_config(r->server->module_config, &rewrite_module);
 
476
 
 
477
    if (!conf->rewritelogfp || level > conf->rewriteloglevel) {
 
478
        return;
 
479
    }
 
480
 
 
481
    rhost = ap_get_remote_host(r->connection, r->per_dir_config,
 
482
                               REMOTE_NOLOOKUP, NULL);
 
483
    rname = ap_get_remote_logname(r);
 
484
 
 
485
    for (redir=0, req=r; req->prev; req = req->prev) {
 
486
        ++redir;
 
487
    }
 
488
 
 
489
    va_start(ap, fmt);
 
490
    text = apr_pvsprintf(r->pool, fmt, ap);
 
491
    va_end(ap);
 
492
 
 
493
    logline = apr_psprintf(r->pool, "%s %s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] "
 
494
                                    "(%d) %s%s%s%s" APR_EOL_STR,
 
495
                           rhost ? rhost : "UNKNOWN-HOST",
 
496
                           rname ? rname : "-",
 
497
                           r->user ? (*r->user ? r->user : "\"\"") : "-",
 
498
                           current_logtime(r),
 
499
                           ap_get_server_name(r),
 
500
                           (void *)(r->server),
 
501
                           (void *)r,
 
502
                           r->main ? "subreq" : "initial",
 
503
                           redir ? "/redir#" : "",
 
504
                           redir ? apr_itoa(r->pool, redir) : "",
 
505
                           level,
 
506
                           perdir ? "[perdir " : "",
 
507
                           perdir ? perdir : "",
 
508
                           perdir ? "] ": "",
 
509
                           text);
 
510
 
 
511
    rv = apr_global_mutex_lock(rewrite_log_lock);
 
512
    if (rv != APR_SUCCESS) {
 
513
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
514
                      "apr_global_mutex_lock(rewrite_log_lock) failed");
 
515
        /* XXX: Maybe this should be fatal? */
 
516
    }
 
517
 
 
518
    nbytes = strlen(logline);
 
519
    apr_file_write(conf->rewritelogfp, logline, &nbytes);
 
520
 
 
521
    rv = apr_global_mutex_unlock(rewrite_log_lock);
 
522
    if (rv != APR_SUCCESS) {
 
523
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
524
                      "apr_global_mutex_unlock(rewrite_log_lock) failed");
 
525
        /* XXX: Maybe this should be fatal? */
 
526
    }
 
527
 
 
528
    return;
 
529
}
 
530
#endif /* !REWRITELOG_DISABLED */
 
531
 
 
532
 
 
533
/*
 
534
 * +-------------------------------------------------------+
 
535
 * |                                                       |
 
536
 * |                URI and path functions
 
537
 * |                                                       |
 
538
 * +-------------------------------------------------------+
 
539
 */
 
540
 
 
541
/* return number of chars of the scheme (incl. '://')
 
542
 * if the URI is absolute (includes a scheme etc.)
 
543
 * otherwise 0.
 
544
 *
 
545
 * NOTE: If you add new schemes here, please have a
 
546
 *       look at escape_absolute_uri and splitout_queryargs.
 
547
 *       Not every scheme takes query strings and some schemes
 
548
 *       may be handled in a special way.
 
549
 *
 
550
 * XXX: we may consider a scheme registry, perhaps with
 
551
 *      appropriate escape callbacks to allow other modules
 
552
 *      to extend mod_rewrite at runtime.
 
553
 */
 
554
static unsigned is_absolute_uri(char *uri)
 
555
{
 
556
    /* fast exit */
 
557
    if (*uri == '/' || strlen(uri) <= 5) {
 
558
        return 0;
 
559
    }
 
560
 
 
561
    switch (*uri++) {
 
562
    case 'a':
 
563
    case 'A':
 
564
        if (!strncasecmp(uri, "jp://", 5)) {        /* ajp://    */
 
565
          return 6;
 
566
        }
 
567
 
 
568
    case 'b':
 
569
    case 'B':
 
570
        if (!strncasecmp(uri, "alancer://", 10)) {   /* balancer:// */
 
571
          return 11;
 
572
        }
 
573
        break;
 
574
 
 
575
    case 'f':
 
576
    case 'F':
 
577
        if (!strncasecmp(uri, "tp://", 5)) {        /* ftp://    */
 
578
            return 6;
 
579
        }
 
580
        break;
 
581
 
 
582
    case 'g':
 
583
    case 'G':
 
584
        if (!strncasecmp(uri, "opher://", 8)) {     /* gopher:// */
 
585
            return 9;
 
586
        }
 
587
        break;
 
588
 
 
589
    case 'h':
 
590
    case 'H':
 
591
        if (!strncasecmp(uri, "ttp://", 6)) {       /* http://   */
 
592
            return 7;
 
593
        }
 
594
        else if (!strncasecmp(uri, "ttps://", 7)) { /* https://  */
 
595
            return 8;
 
596
        }
 
597
        break;
 
598
 
 
599
    case 'l':
 
600
    case 'L':
 
601
        if (!strncasecmp(uri, "dap://", 6)) {       /* ldap://   */
 
602
            return 7;
 
603
        }
 
604
        break;
 
605
 
 
606
    case 'm':
 
607
    case 'M':
 
608
        if (!strncasecmp(uri, "ailto:", 6)) {       /* mailto:   */
 
609
            return 7;
 
610
        }
 
611
        break;
 
612
 
 
613
    case 'n':
 
614
    case 'N':
 
615
        if (!strncasecmp(uri, "ews:", 4)) {         /* news:     */
 
616
            return 5;
 
617
        }
 
618
        else if (!strncasecmp(uri, "ntp://", 6)) {  /* nntp://   */
 
619
            return 7;
 
620
        }
 
621
        break;
 
622
    }
 
623
 
 
624
    return 0;
 
625
}
 
626
 
 
627
/*
 
628
 * escape absolute uri, which may or may not be path oriented.
 
629
 * So let's handle them differently.
 
630
 */
 
631
static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
 
632
{
 
633
    char *cp;
 
634
 
 
635
    /* be safe.
 
636
     * NULL should indicate elsewhere, that something's wrong
 
637
     */
 
638
    if (!scheme || strlen(uri) < scheme) {
 
639
        return NULL;
 
640
    }
 
641
 
 
642
    cp = uri + scheme;
 
643
 
 
644
    /* scheme with authority part? */
 
645
    if (cp[-1] == '/') {
 
646
        /* skip host part */
 
647
        while (*cp && *cp != '/') {
 
648
            ++cp;
 
649
        }
 
650
 
 
651
        /* nothing after the hostpart. ready! */
 
652
        if (!*cp || !*++cp) {
 
653
            return apr_pstrdup(p, uri);
 
654
        }
 
655
 
 
656
        /* remember the hostname stuff */
 
657
        scheme = cp - uri;
 
658
 
 
659
        /* special thing for ldap.
 
660
         * The parts are separated by question marks. From RFC 2255:
 
661
         *     ldapurl = scheme "://" [hostport] ["/"
 
662
         *               [dn ["?" [attributes] ["?" [scope]
 
663
         *               ["?" [filter] ["?" extensions]]]]]]
 
664
         */
 
665
        if (!strncasecmp(uri, "ldap", 4)) {
 
666
            char *token[5];
 
667
            int c = 0;
 
668
 
 
669
            token[0] = cp = apr_pstrdup(p, cp);
 
670
            while (*cp && c < 4) {
 
671
                if (*cp == '?') {
 
672
                    token[++c] = cp + 1;
 
673
                    *cp = '\0';
 
674
                }
 
675
                ++cp;
 
676
            }
 
677
 
 
678
            return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
 
679
                                          ap_escape_uri(p, token[0]),
 
680
                               (c >= 1) ? "?" : NULL,
 
681
                               (c >= 1) ? ap_escape_uri(p, token[1]) : NULL,
 
682
                               (c >= 2) ? "?" : NULL,
 
683
                               (c >= 2) ? ap_escape_uri(p, token[2]) : NULL,
 
684
                               (c >= 3) ? "?" : NULL,
 
685
                               (c >= 3) ? ap_escape_uri(p, token[3]) : NULL,
 
686
                               (c >= 4) ? "?" : NULL,
 
687
                               (c >= 4) ? ap_escape_uri(p, token[4]) : NULL,
 
688
                               NULL);
 
689
        }
 
690
    }
 
691
 
 
692
    /* Nothing special here. Apply normal escaping. */
 
693
    return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
 
694
                       ap_escape_uri(p, cp), NULL);
 
695
}
 
696
 
 
697
/*
 
698
 * split out a QUERY_STRING part from
 
699
 * the current URI string
 
700
 */
 
701
static void splitout_queryargs(request_rec *r, int qsappend)
 
702
{
 
703
    char *q;
 
704
 
 
705
    /* don't touch, unless it's an http or mailto URL.
 
706
     * See RFC 1738 and RFC 2368.
 
707
     */
 
708
    if (is_absolute_uri(r->filename)
 
709
        && strncasecmp(r->filename, "ajp", 3)
 
710
        && strncasecmp(r->filename, "balancer", 8)
 
711
        && strncasecmp(r->filename, "http", 4)
 
712
        && strncasecmp(r->filename, "mailto", 6)) {
 
713
        r->args = NULL; /* forget the query that's still flying around */
 
714
        return;
 
715
    }
 
716
 
 
717
    q = ap_strchr(r->filename, '?');
 
718
    if (q != NULL) {
 
719
        char *olduri;
 
720
        apr_size_t len;
 
721
 
 
722
        olduri = apr_pstrdup(r->pool, r->filename);
 
723
        *q++ = '\0';
 
724
        if (qsappend) {
 
725
            r->args = apr_pstrcat(r->pool, q, "&", r->args, NULL);
 
726
        }
 
727
        else {
 
728
            r->args = apr_pstrdup(r->pool, q);
 
729
        }
 
730
 
 
731
        len = strlen(r->args);
 
732
        if (!len) {
 
733
            r->args = NULL;
 
734
        }
 
735
        else if (r->args[len-1] == '&') {
 
736
            r->args[len-1] = '\0';
 
737
        }
 
738
 
 
739
        rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri,
 
740
                    r->filename, r->args ? r->args : "<none>"));
 
741
    }
 
742
 
 
743
    return;
 
744
}
 
745
 
 
746
/*
 
747
 * strip 'http[s]://ourhost/' from URI
 
748
 */
 
749
static void reduce_uri(request_rec *r)
 
750
{
 
751
    char *cp;
 
752
    apr_size_t l;
 
753
 
 
754
    cp = (char *)ap_http_scheme(r);
 
755
    l  = strlen(cp);
 
756
    if (   strlen(r->filename) > l+3
 
757
        && strncasecmp(r->filename, cp, l) == 0
 
758
        && r->filename[l]   == ':'
 
759
        && r->filename[l+1] == '/'
 
760
        && r->filename[l+2] == '/' ) {
 
761
 
 
762
        unsigned short port;
 
763
        char *portp, *host, *url, *scratch;
 
764
 
 
765
        scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */
 
766
 
 
767
        /* cut the hostname and port out of the URI */
 
768
        cp = host = scratch + l + 3;    /* 3 == strlen("://") */
 
769
        while (*cp && *cp != '/' && *cp != ':') {
 
770
            ++cp;
 
771
        }
 
772
 
 
773
        if (*cp == ':') {      /* additional port given */
 
774
            *cp++ = '\0';
 
775
            portp = cp;
 
776
            while (*cp && *cp != '/') {
 
777
                ++cp;
 
778
            }
 
779
            *cp = '\0';
 
780
 
 
781
            port = atoi(portp);
 
782
            url = r->filename + (cp - scratch);
 
783
            if (!*url) {
 
784
                url = "/";
 
785
            }
 
786
        }
 
787
        else if (*cp == '/') { /* default port */
 
788
            *cp = '\0';
 
789
 
 
790
            port = ap_default_port(r);
 
791
            url = r->filename + (cp - scratch);
 
792
        }
 
793
        else {
 
794
            port = ap_default_port(r);
 
795
            url = "/";
 
796
        }
 
797
 
 
798
        /* now check whether we could reduce it to a local path... */
 
799
        if (ap_matches_request_vhost(r, host, port)) {
 
800
            rewritelog((r, 3, NULL, "reduce %s -> %s", r->filename, url));
 
801
            r->filename = apr_pstrdup(r->pool, url);
 
802
        }
 
803
    }
 
804
 
 
805
    return;
 
806
}
 
807
 
 
808
/*
 
809
 * add 'http[s]://ourhost[:ourport]/' to URI
 
810
 * if URI is still not fully qualified
 
811
 */
 
812
static void fully_qualify_uri(request_rec *r)
 
813
{
 
814
    if (!is_absolute_uri(r->filename)) {
 
815
        const char *thisserver;
 
816
        char *thisport;
 
817
        int port;
 
818
 
 
819
        thisserver = ap_get_server_name(r);
 
820
        port = ap_get_server_port(r);
 
821
        thisport = ap_is_default_port(port, r)
 
822
                   ? ""
 
823
                   : apr_psprintf(r->pool, ":%u", port);
 
824
 
 
825
        r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s",
 
826
                                   ap_http_scheme(r), thisserver, thisport,
 
827
                                   (*r->filename == '/') ? "" : "/",
 
828
                                   r->filename);
 
829
    }
 
830
 
 
831
    return;
 
832
}
 
833
 
 
834
/*
 
835
 * stat() only the first segment of a path
 
836
 */
 
837
static int prefix_stat(const char *path, apr_pool_t *pool)
 
838
{
 
839
    const char *curpath = path;
 
840
    const char *root;
 
841
    const char *slash;
 
842
    char *statpath;
 
843
    apr_status_t rv;
 
844
 
 
845
    rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool);
 
846
 
 
847
    if (rv != APR_SUCCESS) {
 
848
        return 0;
 
849
    }
 
850
 
 
851
    /* let's recognize slashes only, the mod_rewrite semantics are opaque
 
852
     * enough.
 
853
     */
 
854
    if ((slash = ap_strchr_c(curpath, '/')) != NULL) {
 
855
        rv = apr_filepath_merge(&statpath, root,
 
856
                                apr_pstrndup(pool, curpath,
 
857
                                             (apr_size_t)(slash - curpath)),
 
858
                                APR_FILEPATH_NOTABOVEROOT |
 
859
                                APR_FILEPATH_NOTRELATIVE, pool);
 
860
    }
 
861
    else {
 
862
        rv = apr_filepath_merge(&statpath, root, curpath,
 
863
                                APR_FILEPATH_NOTABOVEROOT |
 
864
                                APR_FILEPATH_NOTRELATIVE, pool);
 
865
    }
 
866
 
 
867
    if (rv == APR_SUCCESS) {
 
868
        apr_finfo_t sb;
 
869
 
 
870
        if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) {
 
871
            return 1;
 
872
        }
 
873
    }
 
874
 
 
875
    return 0;
 
876
}
 
877
 
 
878
/*
 
879
 * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
 
880
 */
 
881
static char *subst_prefix_path(request_rec *r, char *input, char *match,
 
882
                               const char *subst)
 
883
{
 
884
    apr_size_t len = strlen(match);
 
885
 
 
886
    if (len && match[len - 1] == '/') {
 
887
        --len;
 
888
    }
 
889
 
 
890
    if (!strncmp(input, match, len) && input[len++] == '/') {
 
891
        apr_size_t slen, outlen;
 
892
        char *output;
 
893
 
 
894
        rewritelog((r, 5, NULL, "strip matching prefix: %s -> %s", input,
 
895
                    input+len));
 
896
 
 
897
        slen = strlen(subst);
 
898
        if (slen && subst[slen - 1] != '/') {
 
899
            ++slen;
 
900
        }
 
901
 
 
902
        outlen = strlen(input) + slen - len;
 
903
        output = apr_palloc(r->pool, outlen + 1); /* don't forget the \0 */
 
904
 
 
905
        memcpy(output, subst, slen);
 
906
        if (slen && !output[slen-1]) {
 
907
            output[slen-1] = '/';
 
908
        }
 
909
        memcpy(output+slen, input+len, outlen - slen);
 
910
        output[outlen] = '\0';
 
911
 
 
912
        rewritelog((r, 4, NULL, "add subst prefix: %s -> %s", input+len,
 
913
                    output));
 
914
 
 
915
        return output;
 
916
    }
 
917
 
 
918
    /* prefix didn't match */
 
919
    return input;
 
920
}
 
921
 
 
922
 
 
923
/*
 
924
 * +-------------------------------------------------------+
 
925
 * |                                                       |
 
926
 * |                    caching support
 
927
 * |                                                       |
 
928
 * +-------------------------------------------------------+
 
929
 */
 
930
 
 
931
static void set_cache_value(const char *name, apr_time_t t, char *key,
 
932
                            char *val)
 
933
{
 
934
    cachedmap *map;
 
935
 
 
936
    if (cachep) {
 
937
#if APR_HAS_THREADS
 
938
        apr_thread_mutex_lock(cachep->lock);
 
939
#endif
 
940
        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
 
941
 
 
942
        if (!map) {
 
943
            apr_pool_t *p;
 
944
 
 
945
            if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
 
946
#if APR_HAS_THREADS
 
947
                apr_thread_mutex_unlock(cachep->lock);
 
948
#endif
 
949
                return;
 
950
            }
 
951
 
 
952
            map = apr_palloc(cachep->pool, sizeof(cachedmap));
 
953
            map->pool = p;
 
954
            map->entries = apr_hash_make(map->pool);
 
955
            map->mtime = t;
 
956
 
 
957
            apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
 
958
        }
 
959
        else if (map->mtime != t) {
 
960
            apr_pool_clear(map->pool);
 
961
            map->entries = apr_hash_make(map->pool);
 
962
            map->mtime = t;
 
963
        }
 
964
 
 
965
        /* Now we should have a valid map->entries hash, where we
 
966
         * can store our value.
 
967
         *
 
968
         * We need to copy the key and the value into OUR pool,
 
969
         * so that we don't leave it during the r->pool cleanup.
 
970
         */
 
971
        apr_hash_set(map->entries,
 
972
                     apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING,
 
973
                     apr_pstrdup(map->pool, val));
 
974
 
 
975
#if APR_HAS_THREADS
 
976
        apr_thread_mutex_unlock(cachep->lock);
 
977
#endif
 
978
    }
 
979
 
 
980
    return;
 
981
}
 
982
 
 
983
static char *get_cache_value(const char *name, apr_time_t t, char *key,
 
984
                             apr_pool_t *p)
 
985
{
 
986
    cachedmap *map;
 
987
    char *val = NULL;
 
988
 
 
989
    if (cachep) {
 
990
#if APR_HAS_THREADS
 
991
        apr_thread_mutex_lock(cachep->lock);
 
992
#endif
 
993
        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
 
994
 
 
995
        if (map) {
 
996
            /* if this map is outdated, forget it. */
 
997
            if (map->mtime != t) {
 
998
                apr_pool_clear(map->pool);
 
999
                map->entries = apr_hash_make(map->pool);
 
1000
                map->mtime = t;
 
1001
            }
 
1002
            else {
 
1003
                val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING);
 
1004
                if (val) {
 
1005
                    /* copy the cached value into the supplied pool,
 
1006
                     * where it belongs (r->pool usually)
 
1007
                     */
 
1008
                    val = apr_pstrdup(p, val);
 
1009
                }
 
1010
            }
 
1011
        }
 
1012
 
 
1013
#if APR_HAS_THREADS
 
1014
        apr_thread_mutex_unlock(cachep->lock);
 
1015
#endif
 
1016
    }
 
1017
 
 
1018
    return val;
 
1019
}
 
1020
 
 
1021
static int init_cache(apr_pool_t *p)
 
1022
{
 
1023
    cachep = apr_palloc(p, sizeof(cache));
 
1024
    if (apr_pool_create(&cachep->pool, p) != APR_SUCCESS) {
 
1025
        cachep = NULL; /* turns off cache */
 
1026
        return 0;
 
1027
    }
 
1028
 
 
1029
    cachep->maps = apr_hash_make(cachep->pool);
 
1030
#if APR_HAS_THREADS
 
1031
    (void)apr_thread_mutex_create(&(cachep->lock), APR_THREAD_MUTEX_DEFAULT, p);
 
1032
#endif
 
1033
 
 
1034
    return 1;
 
1035
}
 
1036
 
 
1037
 
 
1038
/*
 
1039
 * +-------------------------------------------------------+
 
1040
 * |                                                       |
 
1041
 * |                    Map Functions
 
1042
 * |                                                       |
 
1043
 * +-------------------------------------------------------+
 
1044
 */
 
1045
 
 
1046
/*
 
1047
 * General Note: key is already a fresh string, created (expanded) just
 
1048
 * for the purpose to be passed in here. So one can modify key itself.
 
1049
 */
 
1050
 
 
1051
static char *rewrite_mapfunc_toupper(request_rec *r, char *key)
 
1052
{
 
1053
    char *p;
 
1054
 
 
1055
    for (p = key; *p; ++p) {
 
1056
        *p = apr_toupper(*p);
 
1057
    }
 
1058
 
 
1059
    return key;
 
1060
}
 
1061
 
 
1062
static char *rewrite_mapfunc_tolower(request_rec *r, char *key)
 
1063
{
 
1064
    char *p;
 
1065
 
 
1066
    for (p = key; *p; ++p) {
 
1067
        *p = apr_tolower(*p);
 
1068
    }
 
1069
 
 
1070
    return key;
 
1071
}
 
1072
 
 
1073
static char *rewrite_mapfunc_escape(request_rec *r, char *key)
 
1074
{
 
1075
    return ap_escape_uri(r->pool, key);
 
1076
}
 
1077
 
 
1078
static char *rewrite_mapfunc_unescape(request_rec *r, char *key)
 
1079
{
 
1080
    ap_unescape_url(key);
 
1081
 
 
1082
    return key;
 
1083
}
 
1084
 
 
1085
static char *select_random_value_part(request_rec *r, char *value)
 
1086
{
 
1087
    char *p = value;
 
1088
    unsigned n = 1;
 
1089
 
 
1090
    /* count number of distinct values */
 
1091
    while ((p = ap_strchr(p, '|')) != NULL) {
 
1092
        ++n;
 
1093
        ++p;
 
1094
    }
 
1095
 
 
1096
    if (n > 1) {
 
1097
        /* initialize random generator
 
1098
         *
 
1099
         * XXX: Probably this should be wrapped into a thread mutex,
 
1100
         * shouldn't it? Is it worth the effort?
 
1101
         */
 
1102
        if (!rewrite_rand_init_done) {
 
1103
            srand((unsigned)(getpid()));
 
1104
            rewrite_rand_init_done = 1;
 
1105
        }
 
1106
 
 
1107
        /* select a random subvalue */
 
1108
        n = (int)(((double)(rand() % RAND_MAX) / RAND_MAX) * n + 1);
 
1109
 
 
1110
        /* extract it from the whole string */
 
1111
        while (--n && (value = ap_strchr(value, '|')) != NULL) {
 
1112
            ++value;
 
1113
        }
 
1114
 
 
1115
        if (value) { /* should not be NULL, but ... */
 
1116
            p = ap_strchr(value, '|');
 
1117
            if (p) {
 
1118
                *p = '\0';
 
1119
            }
 
1120
        }
 
1121
    }
 
1122
 
 
1123
    return value;
 
1124
}
 
1125
 
 
1126
/* child process code */
 
1127
static void rewrite_child_errfn(apr_pool_t *p, apr_status_t err,
 
1128
                                const char *desc)
 
1129
{
 
1130
    ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, "%s", desc);
 
1131
}
 
1132
 
 
1133
static apr_status_t rewritemap_program_child(apr_pool_t *p,
 
1134
                                             const char *progname, char **argv,
 
1135
                                             apr_file_t **fpout,
 
1136
                                             apr_file_t **fpin)
 
1137
{
 
1138
    apr_status_t rc;
 
1139
    apr_procattr_t *procattr;
 
1140
    apr_proc_t *procnew;
 
1141
 
 
1142
    if (   APR_SUCCESS == (rc=apr_procattr_create(&procattr, p))
 
1143
        && APR_SUCCESS == (rc=apr_procattr_io_set(procattr, APR_FULL_BLOCK,
 
1144
                                                  APR_FULL_BLOCK, APR_NO_PIPE))
 
1145
        && APR_SUCCESS == (rc=apr_procattr_dir_set(procattr,
 
1146
                                             ap_make_dirstr_parent(p, argv[0])))
 
1147
        && APR_SUCCESS == (rc=apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
 
1148
        && APR_SUCCESS == (rc=apr_procattr_child_errfn_set(procattr,
 
1149
                                                           rewrite_child_errfn))
 
1150
        && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) {
 
1151
 
 
1152
        procnew = apr_pcalloc(p, sizeof(*procnew));
 
1153
        rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL,
 
1154
                             procattr, p);
 
1155
 
 
1156
        if (rc == APR_SUCCESS) {
 
1157
            apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
 
1158
 
 
1159
            if (fpin) {
 
1160
                (*fpin) = procnew->in;
 
1161
            }
 
1162
 
 
1163
            if (fpout) {
 
1164
                (*fpout) = procnew->out;
 
1165
            }
 
1166
        }
 
1167
    }
 
1168
 
 
1169
    return (rc);
 
1170
}
 
1171
 
 
1172
static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p)
 
1173
{
 
1174
    rewrite_server_conf *conf;
 
1175
    apr_hash_index_t *hi;
 
1176
    apr_status_t rc;
 
1177
    int lock_warning_issued = 0;
 
1178
 
 
1179
    conf = ap_get_module_config(s->module_config, &rewrite_module);
 
1180
 
 
1181
    /*  If the engine isn't turned on,
 
1182
     *  don't even try to do anything.
 
1183
     */
 
1184
    if (conf->state == ENGINE_DISABLED) {
 
1185
        return APR_SUCCESS;
 
1186
    }
 
1187
 
 
1188
    for (hi = apr_hash_first(p, conf->rewritemaps); hi; hi = apr_hash_next(hi)){
 
1189
        apr_file_t *fpin = NULL;
 
1190
        apr_file_t *fpout = NULL;
 
1191
        rewritemap_entry *map;
 
1192
        void *val;
 
1193
 
 
1194
        apr_hash_this(hi, NULL, NULL, &val);
 
1195
        map = val;
 
1196
 
 
1197
        if (map->type != MAPTYPE_PRG) {
 
1198
            continue;
 
1199
        }
 
1200
        if (!(map->argv[0]) || !*(map->argv[0]) || map->fpin || map->fpout) {
 
1201
            continue;
 
1202
        }
 
1203
 
 
1204
        if (!lock_warning_issued && (!lockname || !*lockname)) {
 
1205
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
 
1206
                         "mod_rewrite: Running external rewrite maps "
 
1207
                         "without defining a RewriteLock is DANGEROUS!");
 
1208
            ++lock_warning_issued;
 
1209
        }
 
1210
 
 
1211
        rc = rewritemap_program_child(p, map->argv[0], map->argv,
 
1212
                                      &fpout, &fpin);
 
1213
        if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) {
 
1214
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
 
1215
                         "mod_rewrite: could not start RewriteMap "
 
1216
                         "program %s", map->checkfile);
 
1217
            return rc;
 
1218
        }
 
1219
        map->fpin  = fpin;
 
1220
        map->fpout = fpout;
 
1221
    }
 
1222
 
 
1223
    return APR_SUCCESS;
 
1224
}
 
1225
 
 
1226
 
 
1227
/*
 
1228
 * +-------------------------------------------------------+
 
1229
 * |                                                       |
 
1230
 * |                  Lookup functions
 
1231
 * |                                                       |
 
1232
 * +-------------------------------------------------------+
 
1233
 */
 
1234
 
 
1235
static char *lookup_map_txtfile(request_rec *r, const char *file, char *key)
 
1236
{
 
1237
    apr_file_t *fp = NULL;
 
1238
    char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
 
1239
    char *value, *keylast;
 
1240
 
 
1241
    if (apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
 
1242
                      r->pool) != APR_SUCCESS) {
 
1243
        return NULL;
 
1244
    }
 
1245
 
 
1246
    keylast = key + strlen(key);
 
1247
    value = NULL;
 
1248
    while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
 
1249
        char *p, *c;
 
1250
 
 
1251
        /* ignore comments and lines starting with whitespaces */
 
1252
        if (*line == '#' || apr_isspace(*line)) {
 
1253
            continue;
 
1254
        }
 
1255
 
 
1256
        p = line;
 
1257
        c = key;
 
1258
        while (c < keylast && *p == *c && !apr_isspace(*p)) {
 
1259
            ++p;
 
1260
            ++c;
 
1261
        }
 
1262
 
 
1263
        /* key doesn't match - ignore. */
 
1264
        if (c != keylast || !apr_isspace(*p)) {
 
1265
            continue;
 
1266
        }
 
1267
 
 
1268
        /* jump to the value */
 
1269
        while (*p && apr_isspace(*p)) {
 
1270
            ++p;
 
1271
        }
 
1272
 
 
1273
        /* no value? ignore */
 
1274
        if (!*p) {
 
1275
            continue;
 
1276
        }
 
1277
 
 
1278
        /* extract the value and return. */
 
1279
        c = p;
 
1280
        while (*p && !apr_isspace(*p)) {
 
1281
            ++p;
 
1282
        }
 
1283
        value = apr_pstrmemdup(r->pool, c, p - c);
 
1284
        break;
 
1285
    }
 
1286
    apr_file_close(fp);
 
1287
 
 
1288
    return value;
 
1289
}
 
1290
 
 
1291
static char *lookup_map_dbmfile(request_rec *r, const char *file,
 
1292
                                const char *dbmtype, char *key)
 
1293
{
 
1294
    apr_dbm_t *dbmfp = NULL;
 
1295
    apr_datum_t dbmkey;
 
1296
    apr_datum_t dbmval;
 
1297
    char *value;
 
1298
 
 
1299
    if (apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY, APR_OS_DEFAULT,
 
1300
                        r->pool) != APR_SUCCESS) {
 
1301
        return NULL;
 
1302
    }
 
1303
 
 
1304
    dbmkey.dptr  = key;
 
1305
    dbmkey.dsize = strlen(key);
 
1306
 
 
1307
    if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) {
 
1308
        value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize);
 
1309
    }
 
1310
    else {
 
1311
        value = NULL;
 
1312
    }
 
1313
 
 
1314
    apr_dbm_close(dbmfp);
 
1315
 
 
1316
    return value;
 
1317
}
 
1318
 
 
1319
static char *lookup_map_program(request_rec *r, apr_file_t *fpin,
 
1320
                                apr_file_t *fpout, char *key)
 
1321
{
 
1322
    char *buf;
 
1323
    char c;
 
1324
    apr_size_t i, nbytes, combined_len = 0;
 
1325
    apr_status_t rv;
 
1326
    const char *eol = APR_EOL_STR;
 
1327
    apr_size_t eolc = 0;
 
1328
    int found_nl = 0;
 
1329
    result_list *buflist = NULL, *curbuf = NULL;
 
1330
 
 
1331
#ifndef NO_WRITEV
 
1332
    struct iovec iova[2];
 
1333
    apr_size_t niov;
 
1334
#endif
 
1335
 
 
1336
    /* when `RewriteEngine off' was used in the per-server
 
1337
     * context then the rewritemap-programs were not spawned.
 
1338
     * In this case using such a map (usually in per-dir context)
 
1339
     * is useless because it is not available.
 
1340
     *
 
1341
     * newlines in the key leave bytes in the pipe and cause
 
1342
     * bad things to happen (next map lookup will use the chars
 
1343
     * after the \n instead of the new key etc etc - in other words,
 
1344
     * the Rewritemap falls out of sync with the requests).
 
1345
     */
 
1346
    if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) {
 
1347
        return NULL;
 
1348
    }
 
1349
 
 
1350
    /* take the lock */
 
1351
    if (rewrite_mapr_lock_acquire) {
 
1352
        rv = apr_global_mutex_lock(rewrite_mapr_lock_acquire);
 
1353
        if (rv != APR_SUCCESS) {
 
1354
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1355
                          "apr_global_mutex_lock(rewrite_mapr_lock_acquire) "
 
1356
                          "failed");
 
1357
            return NULL; /* Maybe this should be fatal? */
 
1358
        }
 
1359
    }
 
1360
 
 
1361
    /* write out the request key */
 
1362
#ifdef NO_WRITEV
 
1363
    nbytes = strlen(key);
 
1364
    apr_file_write(fpin, key, &nbytes);
 
1365
    nbytes = 1;
 
1366
    apr_file_write(fpin, "\n", &nbytes);
 
1367
#else
 
1368
    iova[0].iov_base = key;
 
1369
    iova[0].iov_len = strlen(key);
 
1370
    iova[1].iov_base = "\n";
 
1371
    iova[1].iov_len = 1;
 
1372
 
 
1373
    niov = 2;
 
1374
    apr_file_writev(fpin, iova, niov, &nbytes);
 
1375
#endif
 
1376
 
 
1377
    buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF + 1);
 
1378
 
 
1379
    /* read in the response value */
 
1380
    nbytes = 1;
 
1381
    apr_file_read(fpout, &c, &nbytes);
 
1382
    do {
 
1383
        i = 0;
 
1384
        while (nbytes == 1 && (i < REWRITE_PRG_MAP_BUF)) {
 
1385
            if (c == eol[eolc]) {
 
1386
                if (!eol[++eolc]) {
 
1387
                    /* remove eol from the buffer */
 
1388
                    --eolc;
 
1389
                    if (i < eolc) {
 
1390
                        curbuf->len -= eolc-i;
 
1391
                        i = 0;
 
1392
                    }
 
1393
                    else {
 
1394
                        i -= eolc;
 
1395
                    }
 
1396
                    ++found_nl;
 
1397
                    break;
 
1398
                }
 
1399
            }
 
1400
 
 
1401
            /* only partial (invalid) eol sequence -> reset the counter */
 
1402
            else if (eolc) {
 
1403
                eolc = 0;
 
1404
            }
 
1405
 
 
1406
            /* catch binary mode, e.g. on Win32 */
 
1407
            else if (c == '\n') {
 
1408
                ++found_nl;
 
1409
                break;
 
1410
            }
 
1411
 
 
1412
            buf[i++] = c;
 
1413
            apr_file_read(fpout, &c, &nbytes);
 
1414
        }
 
1415
 
 
1416
        /* well, if there wasn't a newline yet, we need to read further */
 
1417
        if (buflist || (nbytes == 1 && !found_nl)) {
 
1418
            if (!buflist) {
 
1419
                curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist));
 
1420
            }
 
1421
            else if (i) {
 
1422
                curbuf->next = apr_palloc(r->pool, sizeof(*buflist));
 
1423
                curbuf = curbuf->next;
 
1424
 
 
1425
            }
 
1426
            curbuf->next = NULL;
 
1427
 
 
1428
            if (i) {
 
1429
                curbuf->string = buf;
 
1430
                curbuf->len = i;
 
1431
                combined_len += i;
 
1432
                buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF);
 
1433
            }
 
1434
 
 
1435
            if (nbytes == 1 && !found_nl) {
 
1436
                i = 0;
 
1437
                continue;
 
1438
            }
 
1439
        }
 
1440
 
 
1441
        break;
 
1442
    } while (1);
 
1443
 
 
1444
    /* concat the stuff */
 
1445
    if (buflist) {
 
1446
        char *p;
 
1447
 
 
1448
        p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */
 
1449
        while (buflist) {
 
1450
            if (buflist->len) {
 
1451
                memcpy(p, buflist->string, buflist->len);
 
1452
                p += buflist->len;
 
1453
            }
 
1454
            buflist = buflist->next;
 
1455
        }
 
1456
        *p = '\0';
 
1457
        i = combined_len;
 
1458
    }
 
1459
    else {
 
1460
        buf[i] = '\0';
 
1461
    }
 
1462
 
 
1463
    /* give the lock back */
 
1464
    if (rewrite_mapr_lock_acquire) {
 
1465
        rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire);
 
1466
        if (rv != APR_SUCCESS) {
 
1467
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1468
                          "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) "
 
1469
                          "failed");
 
1470
            return NULL; /* Maybe this should be fatal? */
 
1471
        }
 
1472
    }
 
1473
 
 
1474
    /* catch the "failed" case */
 
1475
    if (i == 4 && !strcasecmp(buf, "NULL")) {
 
1476
        return NULL;
 
1477
    }
 
1478
 
 
1479
    return buf;
 
1480
}
 
1481
 
 
1482
/*
 
1483
 * generic map lookup
 
1484
 */
 
1485
static char *lookup_map(request_rec *r, char *name, char *key)
 
1486
{
 
1487
    rewrite_server_conf *conf;
 
1488
    rewritemap_entry *s;
 
1489
    char *value;
 
1490
    apr_finfo_t st;
 
1491
    apr_status_t rv;
 
1492
 
 
1493
    /* get map configuration */
 
1494
    conf = ap_get_module_config(r->server->module_config, &rewrite_module);
 
1495
    s = apr_hash_get(conf->rewritemaps, name, APR_HASH_KEY_STRING);
 
1496
 
 
1497
    /* map doesn't exist */
 
1498
    if (!s) {
 
1499
        return NULL;
 
1500
    }
 
1501
 
 
1502
    switch (s->type) {
 
1503
    /*
 
1504
     * Text file map (perhaps random)
 
1505
     */
 
1506
    case MAPTYPE_RND:
 
1507
    case MAPTYPE_TXT:
 
1508
        rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
 
1509
        if (rv != APR_SUCCESS) {
 
1510
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1511
                          "mod_rewrite: can't access text RewriteMap file %s",
 
1512
                          s->checkfile);
 
1513
            rewritelog((r, 1, NULL,
 
1514
                        "can't open RewriteMap file, see error log"));
 
1515
            return NULL;
 
1516
        }
 
1517
 
 
1518
        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
 
1519
        if (!value) {
 
1520
            rewritelog((r, 6, NULL,
 
1521
                        "cache lookup FAILED, forcing new map lookup"));
 
1522
 
 
1523
            value = lookup_map_txtfile(r, s->datafile, key);
 
1524
            if (!value) {
 
1525
                rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[txt] key=%s",
 
1526
                            name, key));
 
1527
                set_cache_value(s->cachename, st.mtime, key, "");
 
1528
                return NULL;
 
1529
            }
 
1530
 
 
1531
            rewritelog((r, 5, NULL,"map lookup OK: map=%s[txt] key=%s -> val=%s",
 
1532
                        name, key, value));
 
1533
            set_cache_value(s->cachename, st.mtime, key, value);
 
1534
        }
 
1535
        else {
 
1536
            rewritelog((r,5,NULL,"cache lookup OK: map=%s[txt] key=%s -> val=%s",
 
1537
                        name, key, value));
 
1538
        }
 
1539
 
 
1540
        if (s->type == MAPTYPE_RND && *value) {
 
1541
            value = select_random_value_part(r, value);
 
1542
            rewritelog((r, 5, NULL, "randomly chosen the subvalue `%s'",value));
 
1543
        }
 
1544
 
 
1545
        return *value ? value : NULL;
 
1546
 
 
1547
    /*
 
1548
     * DBM file map
 
1549
     */
 
1550
    case MAPTYPE_DBM:
 
1551
        rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
 
1552
        if (rv != APR_SUCCESS) {
 
1553
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1554
                          "mod_rewrite: can't access DBM RewriteMap file %s",
 
1555
                          s->checkfile);
 
1556
            rewritelog((r, 1, NULL,
 
1557
                        "can't open DBM RewriteMap file, see error log"));
 
1558
            return NULL;
 
1559
        }
 
1560
 
 
1561
        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
 
1562
        if (!value) {
 
1563
            rewritelog((r, 6, NULL,
 
1564
                        "cache lookup FAILED, forcing new map lookup"));
 
1565
 
 
1566
            value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key);
 
1567
            if (!value) {
 
1568
                rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s",
 
1569
                            name, key));
 
1570
                set_cache_value(s->cachename, st.mtime, key, "");
 
1571
                return NULL;
 
1572
            }
 
1573
 
 
1574
            rewritelog((r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> "
 
1575
                        "val=%s", name, key, value));
 
1576
 
 
1577
            set_cache_value(s->cachename, st.mtime, key, value);
 
1578
            return value;
 
1579
        }
 
1580
 
 
1581
        rewritelog((r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s",
 
1582
                    name, key, value));
 
1583
        return *value ? value : NULL;
 
1584
 
 
1585
    /*
 
1586
     * Program file map
 
1587
     */
 
1588
    case MAPTYPE_PRG:
 
1589
        value = lookup_map_program(r, s->fpin, s->fpout, key);
 
1590
        if (!value) {
 
1591
            rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
 
1592
                        key));
 
1593
            return NULL;
 
1594
        }
 
1595
 
 
1596
        rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
 
1597
                    name, key, value));
 
1598
        return value;
 
1599
 
 
1600
    /*
 
1601
     * Internal Map
 
1602
     */
 
1603
    case MAPTYPE_INT:
 
1604
        value = s->func(r, key);
 
1605
        if (!value) {
 
1606
            rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name,
 
1607
                        key));
 
1608
            return NULL;
 
1609
        }
 
1610
 
 
1611
        rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
 
1612
                    name, key, value));
 
1613
        return value;
 
1614
    }
 
1615
 
 
1616
    return NULL;
 
1617
}
 
1618
 
 
1619
/*
 
1620
 * lookup a HTTP header and set VARY note
 
1621
 */
 
1622
static const char *lookup_header(const char *name, rewrite_ctx *ctx)
 
1623
{
 
1624
    const char *val = apr_table_get(ctx->r->headers_in, name);
 
1625
 
 
1626
    if (val) {
 
1627
        ctx->vary_this = ctx->vary_this
 
1628
                         ? apr_pstrcat(ctx->r->pool, ctx->vary_this, ", ",
 
1629
                                       name, NULL)
 
1630
                         : apr_pstrdup(ctx->r->pool, name);
 
1631
    }
 
1632
 
 
1633
    return val;
 
1634
}
 
1635
 
 
1636
/*
 
1637
 * lookahead helper function
 
1638
 * Determine the correct URI path in perdir context
 
1639
 */
 
1640
static APR_INLINE const char *la_u(rewrite_ctx *ctx)
 
1641
{
 
1642
    rewrite_perdir_conf *conf;
 
1643
 
 
1644
    if (*ctx->uri == '/') {
 
1645
        return ctx->uri;
 
1646
    }
 
1647
 
 
1648
    conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module);
 
1649
 
 
1650
    return apr_pstrcat(ctx->r->pool, conf->baseurl
 
1651
                                     ? conf->baseurl : conf->directory,
 
1652
                       ctx->uri, NULL);
 
1653
}
 
1654
 
 
1655
/*
 
1656
 * generic variable lookup
 
1657
 */
 
1658
static char *lookup_variable(char *var, rewrite_ctx *ctx)
 
1659
{
 
1660
    const char *result;
 
1661
    request_rec *r = ctx->r;
 
1662
    apr_size_t varlen = strlen(var);
 
1663
 
 
1664
    /* fast exit */
 
1665
    if (varlen < 4) {
 
1666
        return apr_pstrdup(r->pool, "");
 
1667
    }
 
1668
 
 
1669
    result = NULL;
 
1670
 
 
1671
    /* fast tests for variable length variables (sic) first */
 
1672
    if (var[3] == ':') {
 
1673
        if (var[4] && !strncasecmp(var, "ENV", 3)) {
 
1674
            var += 4;
 
1675
            result = apr_table_get(r->notes, var);
 
1676
 
 
1677
            if (!result) {
 
1678
                result = apr_table_get(r->subprocess_env, var);
 
1679
            }
 
1680
            if (!result) {
 
1681
                result = getenv(var);
 
1682
            }
 
1683
        }
 
1684
        else if (var[4] && !strncasecmp(var, "SSL", 3) && rewrite_ssl_lookup) {
 
1685
            result = rewrite_ssl_lookup(r->pool, r->server, r->connection, r,
 
1686
                                        var + 4);
 
1687
        }
 
1688
    }
 
1689
    else if (var[4] == ':') {
 
1690
        if (var[5]) {
 
1691
            request_rec *rr;
 
1692
            const char *path;
 
1693
 
 
1694
            if (!strncasecmp(var, "HTTP", 4)) {
 
1695
                result = lookup_header(var+5, ctx);
 
1696
            }
 
1697
            else if (!strncasecmp(var, "LA-U", 4)) {
 
1698
                if (ctx->uri && subreq_ok(r)) {
 
1699
                    path = ctx->perdir ? la_u(ctx) : ctx->uri;
 
1700
                    rr = ap_sub_req_lookup_uri(path, r, NULL);
 
1701
                    ctx->r = rr;
 
1702
                    result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
 
1703
                    ctx->r = r;
 
1704
                    ap_destroy_sub_req(rr);
 
1705
 
 
1706
                    rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
 
1707
                                "-> val=%s", path, var+5, result));
 
1708
 
 
1709
                    return (char *)result;
 
1710
                }
 
1711
            }
 
1712
            else if (!strncasecmp(var, "LA-F", 4)) {
 
1713
                if (ctx->uri && subreq_ok(r)) {
 
1714
                    path = ctx->uri;
 
1715
                    if (ctx->perdir && *path == '/') {
 
1716
                        /* sigh, the user wants a file based subrequest, but
 
1717
                         * we can't do one, since we don't know what the file
 
1718
                         * path is! In this case behave like LA-U.
 
1719
                         */
 
1720
                        rr = ap_sub_req_lookup_uri(path, r, NULL);
 
1721
                    }
 
1722
                    else {
 
1723
                        if (ctx->perdir) {
 
1724
                            rewrite_perdir_conf *conf;
 
1725
 
 
1726
                            conf = ap_get_module_config(r->per_dir_config,
 
1727
                                                        &rewrite_module);
 
1728
 
 
1729
                            path = apr_pstrcat(r->pool, conf->directory, path,
 
1730
                                               NULL);
 
1731
                        }
 
1732
 
 
1733
                        rr = ap_sub_req_lookup_file(path, r, NULL);
 
1734
                    }
 
1735
 
 
1736
                    ctx->r = rr;
 
1737
                    result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
 
1738
                    ctx->r = r;
 
1739
                    ap_destroy_sub_req(rr);
 
1740
 
 
1741
                    rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s "
 
1742
                                "-> val=%s", path, var+5, result));
 
1743
 
 
1744
                    return (char *)result;
 
1745
                }
 
1746
            }
 
1747
        }
 
1748
    }
 
1749
 
 
1750
    /* well, do it the hard way */
 
1751
    else {
 
1752
        char *p;
 
1753
        apr_time_exp_t tm;
 
1754
 
 
1755
        /* can't do this above, because of the getenv call */
 
1756
        for (p = var; *p; ++p) {
 
1757
            *p = apr_toupper(*p);
 
1758
        }
 
1759
 
 
1760
        switch (varlen) {
 
1761
        case  4:
 
1762
            if (!strcmp(var, "TIME")) {
 
1763
                apr_time_exp_lt(&tm, apr_time_now());
 
1764
                result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
 
1765
                                      tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
 
1766
                                      tm.tm_hour, tm.tm_min, tm.tm_sec);
 
1767
                rewritelog((r, 1, ctx->perdir, "RESULT='%s'", result));
 
1768
                return (char *)result;
 
1769
            }
 
1770
            break;
 
1771
 
 
1772
        case  5:
 
1773
            if (!strcmp(var, "HTTPS")) {
 
1774
                int flag = rewrite_is_https && rewrite_is_https(r->connection);
 
1775
                return apr_pstrdup(r->pool, flag ? "on" : "off");
 
1776
            }
 
1777
            break;
 
1778
 
 
1779
        case  8:
 
1780
            switch (var[6]) {
 
1781
            case 'A':
 
1782
                if (!strcmp(var, "TIME_DAY")) {
 
1783
                    apr_time_exp_lt(&tm, apr_time_now());
 
1784
                    return apr_psprintf(r->pool, "%02d", tm.tm_mday);
 
1785
                }
 
1786
                break;
 
1787
 
 
1788
            case 'E':
 
1789
                if (!strcmp(var, "TIME_SEC")) {
 
1790
                    apr_time_exp_lt(&tm, apr_time_now());
 
1791
                    return apr_psprintf(r->pool, "%02d", tm.tm_sec);
 
1792
                }
 
1793
                break;
 
1794
 
 
1795
            case 'I':
 
1796
                if (!strcmp(var, "TIME_MIN")) {
 
1797
                    apr_time_exp_lt(&tm, apr_time_now());
 
1798
                    return apr_psprintf(r->pool, "%02d", tm.tm_min);
 
1799
                }
 
1800
                break;
 
1801
 
 
1802
            case 'O':
 
1803
                if (!strcmp(var, "TIME_MON")) {
 
1804
                    apr_time_exp_lt(&tm, apr_time_now());
 
1805
                    return apr_psprintf(r->pool, "%02d", tm.tm_mon+1);
 
1806
                }
 
1807
                break;
 
1808
            }
 
1809
            break;
 
1810
 
 
1811
        case  9:
 
1812
            switch (var[7]) {
 
1813
            case 'A':
 
1814
                if (var[8] == 'Y' && !strcmp(var, "TIME_WDAY")) {
 
1815
                    apr_time_exp_lt(&tm, apr_time_now());
 
1816
                    return apr_psprintf(r->pool, "%d", tm.tm_wday);
 
1817
                }
 
1818
                else if (!strcmp(var, "TIME_YEAR")) {
 
1819
                    apr_time_exp_lt(&tm, apr_time_now());
 
1820
                    return apr_psprintf(r->pool, "%04d", tm.tm_year+1900);
 
1821
                }
 
1822
                break;
 
1823
 
 
1824
            case 'E':
 
1825
                if (!strcmp(var, "IS_SUBREQ")) {
 
1826
                    result = (r->main ? "true" : "false");
 
1827
                }
 
1828
                break;
 
1829
 
 
1830
            case 'F':
 
1831
                if (!strcmp(var, "PATH_INFO")) {
 
1832
                    result = r->path_info;
 
1833
                }
 
1834
                break;
 
1835
 
 
1836
            case 'P':
 
1837
                if (!strcmp(var, "AUTH_TYPE")) {
 
1838
                    result = r->ap_auth_type;
 
1839
                }
 
1840
                break;
 
1841
 
 
1842
            case 'S':
 
1843
                if (!strcmp(var, "HTTP_HOST")) {
 
1844
                    result = lookup_header("Host", ctx);
 
1845
                }
 
1846
                break;
 
1847
 
 
1848
            case 'U':
 
1849
                if (!strcmp(var, "TIME_HOUR")) {
 
1850
                    apr_time_exp_lt(&tm, apr_time_now());
 
1851
                    return apr_psprintf(r->pool, "%02d", tm.tm_hour);
 
1852
                }
 
1853
                break;
 
1854
            }
 
1855
            break;
 
1856
 
 
1857
        case 11:
 
1858
            switch (var[8]) {
 
1859
            case 'A':
 
1860
                if (!strcmp(var, "SERVER_NAME")) {
 
1861
                    result = ap_get_server_name(r);
 
1862
                }
 
1863
                break;
 
1864
 
 
1865
            case 'D':
 
1866
                if (*var == 'R' && !strcmp(var, "REMOTE_ADDR")) {
 
1867
                    result = r->connection->remote_ip;
 
1868
                }
 
1869
                else if (!strcmp(var, "SERVER_ADDR")) {
 
1870
                    result = r->connection->local_ip;
 
1871
                }
 
1872
                break;
 
1873
 
 
1874
            case 'E':
 
1875
                if (*var == 'H' && !strcmp(var, "HTTP_ACCEPT")) {
 
1876
                    result = lookup_header("Accept", ctx);
 
1877
                }
 
1878
                else if (!strcmp(var, "THE_REQUEST")) {
 
1879
                    result = r->the_request;
 
1880
                }
 
1881
                break;
 
1882
 
 
1883
            case 'I':
 
1884
                if (!strcmp(var, "API_VERSION")) {
 
1885
                    return apr_psprintf(r->pool, "%d:%d",
 
1886
                                        MODULE_MAGIC_NUMBER_MAJOR,
 
1887
                                        MODULE_MAGIC_NUMBER_MINOR);
 
1888
                }
 
1889
                break;
 
1890
 
 
1891
            case 'K':
 
1892
                if (!strcmp(var, "HTTP_COOKIE")) {
 
1893
                    result = lookup_header("Cookie", ctx);
 
1894
                }
 
1895
                break;
 
1896
 
 
1897
            case 'O':
 
1898
                if (*var == 'S' && !strcmp(var, "SERVER_PORT")) {
 
1899
                    return apr_psprintf(r->pool, "%u", ap_get_server_port(r));
 
1900
                }
 
1901
                else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) {
 
1902
                    result = ap_get_remote_host(r->connection,r->per_dir_config,
 
1903
                                                REMOTE_NAME, NULL);
 
1904
                }
 
1905
                else if (!strcmp(var, "REMOTE_PORT")) {
 
1906
                    return apr_itoa(r->pool, r->connection->remote_addr->port);
 
1907
                }
 
1908
                break;
 
1909
 
 
1910
            case 'S':
 
1911
                if (*var == 'R' && !strcmp(var, "REMOTE_USER")) {
 
1912
                    result = r->user;
 
1913
                }
 
1914
                else if (!strcmp(var, "SCRIPT_USER")) {
 
1915
                    result = "<unknown>";
 
1916
                    if (r->finfo.valid & APR_FINFO_USER) {
 
1917
                        apr_uid_name_get((char **)&result, r->finfo.user,
 
1918
                                         r->pool);
 
1919
                    }
 
1920
                }
 
1921
                break;
 
1922
 
 
1923
            case 'U':
 
1924
                if (!strcmp(var, "REQUEST_URI")) {
 
1925
                    result = r->uri;
 
1926
                }
 
1927
                break;
 
1928
            }
 
1929
            break;
 
1930
 
 
1931
        case 12:
 
1932
            switch (var[3]) {
 
1933
            case 'I':
 
1934
                if (!strcmp(var, "SCRIPT_GROUP")) {
 
1935
                    result = "<unknown>";
 
1936
                    if (r->finfo.valid & APR_FINFO_GROUP) {
 
1937
                        apr_gid_name_get((char **)&result, r->finfo.group,
 
1938
                                         r->pool);
 
1939
                    }
 
1940
                }
 
1941
                break;
 
1942
 
 
1943
            case 'O':
 
1944
                if (!strcmp(var, "REMOTE_IDENT")) {
 
1945
                    result = ap_get_remote_logname(r);
 
1946
                }
 
1947
                break;
 
1948
 
 
1949
            case 'P':
 
1950
                if (!strcmp(var, "HTTP_REFERER")) {
 
1951
                    result = lookup_header("Referer", ctx);
 
1952
                }
 
1953
                break;
 
1954
 
 
1955
            case 'R':
 
1956
                if (!strcmp(var, "QUERY_STRING")) {
 
1957
                    result = r->args;
 
1958
                }
 
1959
                break;
 
1960
 
 
1961
            case 'V':
 
1962
                if (!strcmp(var, "SERVER_ADMIN")) {
 
1963
                    result = r->server->server_admin;
 
1964
                }
 
1965
                break;
 
1966
            }
 
1967
            break;
 
1968
 
 
1969
        case 13:
 
1970
            if (!strcmp(var, "DOCUMENT_ROOT")) {
 
1971
                result = ap_document_root(r);
 
1972
            }
 
1973
            break;
 
1974
 
 
1975
        case 14:
 
1976
            if (*var == 'H' && !strcmp(var, "HTTP_FORWARDED")) {
 
1977
                result = lookup_header("Forwarded", ctx);
 
1978
            }
 
1979
            else if (!strcmp(var, "REQUEST_METHOD")) {
 
1980
                result = r->method;
 
1981
            }
 
1982
            break;
 
1983
 
 
1984
        case 15:
 
1985
            switch (var[7]) {
 
1986
            case 'E':
 
1987
                if (!strcmp(var, "HTTP_USER_AGENT")) {
 
1988
                    result = lookup_header("User-Agent", ctx);
 
1989
                }
 
1990
                break;
 
1991
 
 
1992
            case 'F':
 
1993
                if (!strcmp(var, "SCRIPT_FILENAME")) {
 
1994
                    result = r->filename; /* same as request_filename (16) */
 
1995
                }
 
1996
                break;
 
1997
 
 
1998
            case 'P':
 
1999
                if (!strcmp(var, "SERVER_PROTOCOL")) {
 
2000
                    result = r->protocol;
 
2001
                }
 
2002
                break;
 
2003
 
 
2004
            case 'S':
 
2005
                if (!strcmp(var, "SERVER_SOFTWARE")) {
 
2006
                    result = ap_get_server_version();
 
2007
                }
 
2008
                break;
 
2009
            }
 
2010
            break;
 
2011
 
 
2012
        case 16:
 
2013
            if (!strcmp(var, "REQUEST_FILENAME")) {
 
2014
                result = r->filename; /* same as script_filename (15) */
 
2015
            }
 
2016
            break;
 
2017
 
 
2018
        case 21:
 
2019
            if (!strcmp(var, "HTTP_PROXY_CONNECTION")) {
 
2020
                result = lookup_header("Proxy-Connection", ctx);
 
2021
            }
 
2022
            break;
 
2023
        }
 
2024
    }
 
2025
 
 
2026
    return apr_pstrdup(r->pool, result ? result : "");
 
2027
}
 
2028
 
 
2029
 
 
2030
/*
 
2031
 * +-------------------------------------------------------+
 
2032
 * |                                                       |
 
2033
 * |                 Expansion functions
 
2034
 * |                                                       |
 
2035
 * +-------------------------------------------------------+
 
2036
 */
 
2037
 
 
2038
/*
 
2039
 * Bracketed expression handling
 
2040
 * s points after the opening bracket
 
2041
 */
 
2042
static APR_INLINE char *find_closing_curly(char *s)
 
2043
{
 
2044
    unsigned depth;
 
2045
 
 
2046
    for (depth = 1; *s; ++s) {
 
2047
        if (*s == RIGHT_CURLY && --depth == 0) {
 
2048
            return s;
 
2049
        }
 
2050
        else if (*s == LEFT_CURLY) {
 
2051
            ++depth;
 
2052
        }
 
2053
    }
 
2054
 
 
2055
    return NULL;
 
2056
}
 
2057
 
 
2058
static APR_INLINE char *find_char_in_curlies(char *s, int c)
 
2059
{
 
2060
    unsigned depth;
 
2061
 
 
2062
    for (depth = 1; *s; ++s) {
 
2063
        if (*s == c && depth == 1) {
 
2064
            return s;
 
2065
        }
 
2066
        else if (*s == RIGHT_CURLY && --depth == 0) {
 
2067
            return NULL;
 
2068
        }
 
2069
        else if (*s == LEFT_CURLY) {
 
2070
            ++depth;
 
2071
        }
 
2072
    }
 
2073
 
 
2074
    return NULL;
 
2075
}
 
2076
 
 
2077
/* perform all the expansions on the input string
 
2078
 * putting the result into a new string
 
2079
 *
 
2080
 * for security reasons this expansion must be performed in a
 
2081
 * single pass, otherwise an attacker can arrange for the result
 
2082
 * of an earlier expansion to include expansion specifiers that
 
2083
 * are interpreted by a later expansion, producing results that
 
2084
 * were not intended by the administrator.
 
2085
 */
 
2086
static char *do_expand(char *input, rewrite_ctx *ctx)
 
2087
{
 
2088
    result_list *result, *current;
 
2089
    result_list sresult[SMALL_EXPANSION];
 
2090
    unsigned spc = 0;
 
2091
    apr_size_t span, inputlen, outlen;
 
2092
    char *p, *c;
 
2093
    apr_pool_t *pool = ctx->r->pool;
 
2094
 
 
2095
    span = strcspn(input, "\\$%");
 
2096
    inputlen = strlen(input);
 
2097
 
 
2098
    /* fast exit */
 
2099
    if (inputlen == span) {
 
2100
        return apr_pstrdup(pool, input);
 
2101
    }
 
2102
 
 
2103
    /* well, actually something to do */
 
2104
    result = current = &(sresult[spc++]);
 
2105
 
 
2106
    p = input + span;
 
2107
    current->next = NULL;
 
2108
    current->string = input;
 
2109
    current->len = span;
 
2110
    outlen = span;
 
2111
 
 
2112
    /* loop for specials */
 
2113
    do {
 
2114
        /* prepare next entry */
 
2115
        if (current->len) {
 
2116
            current->next = (spc < SMALL_EXPANSION)
 
2117
                            ? &(sresult[spc++])
 
2118
                            : (result_list *)apr_palloc(pool,
 
2119
                                                        sizeof(result_list));
 
2120
            current = current->next;
 
2121
            current->next = NULL;
 
2122
            current->len = 0;
 
2123
        }
 
2124
 
 
2125
        /* escaped character */
 
2126
        if (*p == '\\') {
 
2127
            current->len = 1;
 
2128
            ++outlen;
 
2129
            if (!p[1]) {
 
2130
                current->string = p;
 
2131
                break;
 
2132
            }
 
2133
            else {
 
2134
                current->string = ++p;
 
2135
                ++p;
 
2136
            }
 
2137
        }
 
2138
 
 
2139
        /* variable or map lookup */
 
2140
        else if (p[1] == '{') {
 
2141
            char *endp;
 
2142
 
 
2143
            endp = find_closing_curly(p+2);
 
2144
            if (!endp) {
 
2145
                current->len = 2;
 
2146
                current->string = p;
 
2147
                outlen += 2;
 
2148
                p += 2;
 
2149
            }
 
2150
 
 
2151
            /* variable lookup */
 
2152
            else if (*p == '%') {
 
2153
                p = lookup_variable(apr_pstrmemdup(pool, p+2, endp-p-2), ctx);
 
2154
 
 
2155
                span = strlen(p);
 
2156
                current->len = span;
 
2157
                current->string = p;
 
2158
                outlen += span;
 
2159
                p = endp + 1;
 
2160
            }
 
2161
 
 
2162
            /* map lookup */
 
2163
            else {     /* *p == '$' */
 
2164
                char *key;
 
2165
 
 
2166
                /*
 
2167
                 * To make rewrite maps useful, the lookup key and
 
2168
                 * default values must be expanded, so we make
 
2169
                 * recursive calls to do the work. For security
 
2170
                 * reasons we must never expand a string that includes
 
2171
                 * verbatim data from the network. The recursion here
 
2172
                 * isn't a problem because the result of expansion is
 
2173
                 * only passed to lookup_map() so it cannot be
 
2174
                 * re-expanded, only re-looked-up. Another way of
 
2175
                 * looking at it is that the recursion is entirely
 
2176
                 * driven by the syntax of the nested curly brackets.
 
2177
                 */
 
2178
 
 
2179
                key = find_char_in_curlies(p+2, ':');
 
2180
                if (!key) {
 
2181
                    current->len = 2;
 
2182
                    current->string = p;
 
2183
                    outlen += 2;
 
2184
                    p += 2;
 
2185
                }
 
2186
                else {
 
2187
                    char *map, *dflt;
 
2188
 
 
2189
                    map = apr_pstrmemdup(pool, p+2, endp-p-2);
 
2190
                    key = map + (key-p-2);
 
2191
                    *key++ = '\0';
 
2192
                    dflt = find_char_in_curlies(key, '|');
 
2193
                    if (dflt) {
 
2194
                        *dflt++ = '\0';
 
2195
                    }
 
2196
 
 
2197
                    /* reuse of key variable as result */
 
2198
                    key = lookup_map(ctx->r, map, do_expand(key, ctx));
 
2199
 
 
2200
                    if (!key && dflt && *dflt) {
 
2201
                        key = do_expand(dflt, ctx);
 
2202
                    }
 
2203
 
 
2204
                    if (key) {
 
2205
                        span = strlen(key);
 
2206
                        current->len = span;
 
2207
                        current->string = key;
 
2208
                        outlen += span;
 
2209
                    }
 
2210
 
 
2211
                    p = endp + 1;
 
2212
                }
 
2213
            }
 
2214
        }
 
2215
 
 
2216
        /* backreference */
 
2217
        else if (apr_isdigit(p[1])) {
 
2218
            int n = p[1] - '0';
 
2219
            backrefinfo *bri = (*p == '$') ? &ctx->briRR : &ctx->briRC;
 
2220
 
 
2221
            /* see ap_pregsub() in server/util.c */
 
2222
            if (bri->source && n < AP_MAX_REG_MATCH
 
2223
                && bri->regmatch[n].rm_eo > bri->regmatch[n].rm_so) {
 
2224
                span = bri->regmatch[n].rm_eo - bri->regmatch[n].rm_so;
 
2225
 
 
2226
                current->len = span;
 
2227
                current->string = bri->source + bri->regmatch[n].rm_so;
 
2228
                outlen += span;
 
2229
            }
 
2230
 
 
2231
            p += 2;
 
2232
        }
 
2233
 
 
2234
        /* not for us, just copy it */
 
2235
        else {
 
2236
            current->len = 1;
 
2237
            current->string = p++;
 
2238
            ++outlen;
 
2239
        }
 
2240
 
 
2241
        /* check the remainder */
 
2242
        if (*p && (span = strcspn(p, "\\$%")) > 0) {
 
2243
            if (current->len) {
 
2244
                current->next = (spc < SMALL_EXPANSION)
 
2245
                                ? &(sresult[spc++])
 
2246
                                : (result_list *)apr_palloc(pool,
 
2247
                                                           sizeof(result_list));
 
2248
                current = current->next;
 
2249
                current->next = NULL;
 
2250
            }
 
2251
 
 
2252
            current->len = span;
 
2253
            current->string = p;
 
2254
            p += span;
 
2255
            outlen += span;
 
2256
        }
 
2257
 
 
2258
    } while (p < input+inputlen);
 
2259
 
 
2260
    /* assemble result */
 
2261
    c = p = apr_palloc(pool, outlen + 1); /* don't forget the \0 */
 
2262
    do {
 
2263
        if (result->len) {
 
2264
            ap_assert(c+result->len <= p+outlen); /* XXX: can be removed after
 
2265
                                                   * extensive testing and
 
2266
                                                   * review
 
2267
                                                   */
 
2268
            memcpy(c, result->string, result->len);
 
2269
            c += result->len;
 
2270
        }
 
2271
        result = result->next;
 
2272
    } while (result);
 
2273
 
 
2274
    p[outlen] = '\0';
 
2275
 
 
2276
    return p;
 
2277
}
 
2278
 
 
2279
/*
 
2280
 * perform all the expansions on the environment variables
 
2281
 */
 
2282
static void do_expand_env(data_item *env, rewrite_ctx *ctx)
 
2283
{
 
2284
    char *name, *val;
 
2285
 
 
2286
    while (env) {
 
2287
        name = do_expand(env->data, ctx);
 
2288
        if ((val = ap_strchr(name, ':')) != NULL) {
 
2289
            *val++ = '\0';
 
2290
 
 
2291
            apr_table_set(ctx->r->subprocess_env, name, val);
 
2292
            rewritelog((ctx->r, 5, NULL, "setting env variable '%s' to '%s'",
 
2293
                        name, val));
 
2294
        }
 
2295
 
 
2296
        env = env->next;
 
2297
    }
 
2298
 
 
2299
    return;
 
2300
}
 
2301
 
 
2302
/*
 
2303
 * perform all the expansions on the cookies
 
2304
 *
 
2305
 * TODO: use cached time similar to how logging does it
 
2306
 */
 
2307
static void add_cookie(request_rec *r, char *s)
 
2308
{
 
2309
    char *var;
 
2310
    char *val;
 
2311
    char *domain;
 
2312
    char *expires;
 
2313
    char *path;
 
2314
 
 
2315
    char *tok_cntx;
 
2316
    char *cookie;
 
2317
 
 
2318
    var = apr_strtok(s, ":", &tok_cntx);
 
2319
    val = apr_strtok(NULL, ":", &tok_cntx);
 
2320
    domain = apr_strtok(NULL, ":", &tok_cntx);
 
2321
 
 
2322
    if (var && val && domain) {
 
2323
        request_rec *rmain = r;
 
2324
        char *notename;
 
2325
        void *data;
 
2326
 
 
2327
        while (rmain->main) {
 
2328
            rmain = rmain->main;
 
2329
        }
 
2330
 
 
2331
        notename = apr_pstrcat(rmain->pool, var, "_rewrite", NULL);
 
2332
        apr_pool_userdata_get(&data, notename, rmain->pool);
 
2333
        if (!data) {
 
2334
            char *exp_time = NULL;
 
2335
 
 
2336
            expires = apr_strtok(NULL, ":", &tok_cntx);
 
2337
            path = expires ? apr_strtok(NULL, ":", &tok_cntx) : NULL;
 
2338
 
 
2339
            if (expires) {
 
2340
                apr_time_exp_t tms;
 
2341
                apr_time_exp_gmt(&tms, r->request_time
 
2342
                                     + apr_time_from_sec((60 * atol(expires))));
 
2343
                exp_time = apr_psprintf(r->pool, "%s, %.2d-%s-%.4d "
 
2344
                                                 "%.2d:%.2d:%.2d GMT",
 
2345
                                        apr_day_snames[tms.tm_wday],
 
2346
                                        tms.tm_mday,
 
2347
                                        apr_month_snames[tms.tm_mon],
 
2348
                                        tms.tm_year+1900,
 
2349
                                        tms.tm_hour, tms.tm_min, tms.tm_sec);
 
2350
            }
 
2351
 
 
2352
            cookie = apr_pstrcat(rmain->pool,
 
2353
                                 var, "=", val,
 
2354
                                 "; path=", path ? path : "/",
 
2355
                                 "; domain=", domain,
 
2356
                                 expires ? "; expires=" : NULL,
 
2357
                                 expires ? exp_time : NULL,
 
2358
                                 NULL);
 
2359
 
 
2360
            apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie);
 
2361
            apr_pool_userdata_set("set", notename, NULL, rmain->pool);
 
2362
            rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie));
 
2363
        }
 
2364
        else {
 
2365
            rewritelog((rmain, 5, NULL, "skipping already set cookie '%s'",
 
2366
                        var));
 
2367
        }
 
2368
    }
 
2369
 
 
2370
    return;
 
2371
}
 
2372
 
 
2373
static void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx)
 
2374
{
 
2375
    while (cookie) {
 
2376
        add_cookie(ctx->r, do_expand(cookie->data, ctx));
 
2377
        cookie = cookie->next;
 
2378
    }
 
2379
 
 
2380
    return;
 
2381
}
 
2382
 
 
2383
#if APR_HAS_USER
 
2384
/*
 
2385
 * Expand tilde-paths (/~user) through Unix /etc/passwd
 
2386
 * database information (or other OS-specific database)
 
2387
 */
 
2388
static char *expand_tildepaths(request_rec *r, char *uri)
 
2389
{
 
2390
    if (uri && *uri == '/' && uri[1] == '~') {
 
2391
        char *p, *user;
 
2392
 
 
2393
        p = user = uri + 2;
 
2394
        while (*p && *p != '/') {
 
2395
            ++p;
 
2396
        }
 
2397
 
 
2398
        if (p > user) {
 
2399
            char *homedir;
 
2400
 
 
2401
            user = apr_pstrmemdup(r->pool, user, p-user);
 
2402
            if (apr_uid_homepath_get(&homedir, user, r->pool) == APR_SUCCESS) {
 
2403
                if (*p) {
 
2404
                    /* reuse of user variable */
 
2405
                    user = homedir + strlen(homedir) - 1;
 
2406
                    if (user >= homedir && *user == '/') {
 
2407
                        *user = '\0';
 
2408
                    }
 
2409
 
 
2410
                    return apr_pstrcat(r->pool, homedir, p, NULL);
 
2411
                }
 
2412
                else {
 
2413
                    return homedir;
 
2414
                }
 
2415
            }
 
2416
        }
 
2417
    }
 
2418
 
 
2419
    return uri;
 
2420
}
 
2421
#endif  /* if APR_HAS_USER */
 
2422
 
 
2423
 
 
2424
/*
 
2425
 * +-------------------------------------------------------+
 
2426
 * |                                                       |
 
2427
 * |              rewriting lockfile support
 
2428
 * |                                                       |
 
2429
 * +-------------------------------------------------------+
 
2430
 */
 
2431
 
 
2432
static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p)
 
2433
{
 
2434
    apr_status_t rc;
 
2435
 
 
2436
    /* only operate if a lockfile is used */
 
2437
    if (lockname == NULL || *(lockname) == '\0') {
 
2438
        return APR_SUCCESS;
 
2439
    }
 
2440
 
 
2441
    /* create the lockfile */
 
2442
    rc = apr_global_mutex_create(&rewrite_mapr_lock_acquire, lockname,
 
2443
                                 APR_LOCK_DEFAULT, p);
 
2444
    if (rc != APR_SUCCESS) {
 
2445
        ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s,
 
2446
                     "mod_rewrite: Parent could not create RewriteLock "
 
2447
                     "file %s", lockname);
 
2448
        return rc;
 
2449
    }
 
2450
 
 
2451
#ifdef AP_NEED_SET_MUTEX_PERMS
 
2452
    rc = unixd_set_global_mutex_perms(rewrite_mapr_lock_acquire);
 
2453
    if (rc != APR_SUCCESS) {
 
2454
        ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s,
 
2455
                     "mod_rewrite: Parent could not set permissions "
 
2456
                     "on RewriteLock; check User and Group directives");
 
2457
        return rc;
 
2458
    }
 
2459
#endif
 
2460
 
 
2461
    return APR_SUCCESS;
 
2462
}
 
2463
 
 
2464
static apr_status_t rewritelock_remove(void *data)
 
2465
{
 
2466
    /* only operate if a lockfile is used */
 
2467
    if (lockname == NULL || *(lockname) == '\0') {
 
2468
        return APR_SUCCESS;
 
2469
    }
 
2470
 
 
2471
    /* destroy the rewritelock */
 
2472
    apr_global_mutex_destroy (rewrite_mapr_lock_acquire);
 
2473
    rewrite_mapr_lock_acquire = NULL;
 
2474
    lockname = NULL;
 
2475
    return(0);
 
2476
}
 
2477
 
 
2478
 
 
2479
/*
 
2480
 * +-------------------------------------------------------+
 
2481
 * |                                                       |
 
2482
 * |           configuration directive handling
 
2483
 * |                                                       |
 
2484
 * +-------------------------------------------------------+
 
2485
 */
 
2486
 
 
2487
/*
 
2488
 * own command line parser for RewriteRule and RewriteCond,
 
2489
 * which doesn't have the '\\' problem.
 
2490
 * (returns true on error)
 
2491
 *
 
2492
 * XXX: what an inclined parser. Seems we have to leave it so
 
2493
 *      for backwards compat. *sigh*
 
2494
 */
 
2495
static int parseargline(char *str, char **a1, char **a2, char **a3)
 
2496
{
 
2497
    char quote;
 
2498
 
 
2499
    while (apr_isspace(*str)) {
 
2500
        ++str;
 
2501
    }
 
2502
 
 
2503
    /*
 
2504
     * determine first argument
 
2505
     */
 
2506
    quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
 
2507
    *a1 = str;
 
2508
 
 
2509
    for (; *str; ++str) {
 
2510
        if ((apr_isspace(*str) && !quote) || (*str == quote)) {
 
2511
            break;
 
2512
        }
 
2513
        if (*str == '\\' && apr_isspace(str[1])) {
 
2514
            ++str;
 
2515
            continue;
 
2516
        }
 
2517
    }
 
2518
 
 
2519
    if (!*str) {
 
2520
        return 1;
 
2521
    }
 
2522
    *str++ = '\0';
 
2523
 
 
2524
    while (apr_isspace(*str)) {
 
2525
        ++str;
 
2526
    }
 
2527
 
 
2528
    /*
 
2529
     * determine second argument
 
2530
     */
 
2531
    quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
 
2532
    *a2 = str;
 
2533
 
 
2534
    for (; *str; ++str) {
 
2535
        if ((apr_isspace(*str) && !quote) || (*str == quote)) {
 
2536
            break;
 
2537
        }
 
2538
        if (*str == '\\' && apr_isspace(str[1])) {
 
2539
            ++str;
 
2540
            continue;
 
2541
        }
 
2542
    }
 
2543
 
 
2544
    if (!*str) {
 
2545
        *a3 = NULL; /* 3rd argument is optional */
 
2546
        return 0;
 
2547
    }
 
2548
    *str++ = '\0';
 
2549
 
 
2550
    while (apr_isspace(*str)) {
 
2551
        ++str;
 
2552
    }
 
2553
 
 
2554
    if (!*str) {
 
2555
        *a3 = NULL; /* 3rd argument is still optional */
 
2556
        return 0;
 
2557
    }
 
2558
 
 
2559
    /*
 
2560
     * determine third argument
 
2561
     */
 
2562
    quote = (*str == '"' || *str == '\'') ? *str++ : '\0';
 
2563
    *a3 = str;
 
2564
    for (; *str; ++str) {
 
2565
        if ((apr_isspace(*str) && !quote) || (*str == quote)) {
 
2566
            break;
 
2567
        }
 
2568
        if (*str == '\\' && apr_isspace(str[1])) {
 
2569
            ++str;
 
2570
            continue;
 
2571
        }
 
2572
    }
 
2573
    *str = '\0';
 
2574
 
 
2575
    return 0;
 
2576
}
 
2577
 
 
2578
static void *config_server_create(apr_pool_t *p, server_rec *s)
 
2579
{
 
2580
    rewrite_server_conf *a;
 
2581
 
 
2582
    a = (rewrite_server_conf *)apr_pcalloc(p, sizeof(rewrite_server_conf));
 
2583
 
 
2584
    a->state           = ENGINE_DISABLED;
 
2585
    a->options         = OPTION_NONE;
 
2586
#ifndef REWRITELOG_DISABLED
 
2587
    a->rewritelogfile  = NULL;
 
2588
    a->rewritelogfp    = NULL;
 
2589
    a->rewriteloglevel = 0;
 
2590
#endif
 
2591
    a->rewritemaps     = apr_hash_make(p);
 
2592
    a->rewriteconds    = apr_array_make(p, 2, sizeof(rewritecond_entry));
 
2593
    a->rewriterules    = apr_array_make(p, 2, sizeof(rewriterule_entry));
 
2594
    a->server          = s;
 
2595
 
 
2596
    return (void *)a;
 
2597
}
 
2598
 
 
2599
static void *config_server_merge(apr_pool_t *p, void *basev, void *overridesv)
 
2600
{
 
2601
    rewrite_server_conf *a, *base, *overrides;
 
2602
 
 
2603
    a         = (rewrite_server_conf *)apr_pcalloc(p,
 
2604
                                                   sizeof(rewrite_server_conf));
 
2605
    base      = (rewrite_server_conf *)basev;
 
2606
    overrides = (rewrite_server_conf *)overridesv;
 
2607
 
 
2608
    a->state   = overrides->state;
 
2609
    a->options = overrides->options;
 
2610
    a->server  = overrides->server;
 
2611
 
 
2612
    if (a->options & OPTION_INHERIT) {
 
2613
        /*
 
2614
         *  local directives override
 
2615
         *  and anything else is inherited
 
2616
         */
 
2617
#ifndef REWRITELOG_DISABLED
 
2618
        a->rewriteloglevel = overrides->rewriteloglevel != 0
 
2619
                             ? overrides->rewriteloglevel
 
2620
                             : base->rewriteloglevel;
 
2621
        a->rewritelogfile  = overrides->rewritelogfile != NULL
 
2622
                             ? overrides->rewritelogfile
 
2623
                             : base->rewritelogfile;
 
2624
        a->rewritelogfp    = overrides->rewritelogfp != NULL
 
2625
                             ? overrides->rewritelogfp
 
2626
                             : base->rewritelogfp;
 
2627
#endif
 
2628
        a->rewritemaps     = apr_hash_overlay(p, overrides->rewritemaps,
 
2629
                                              base->rewritemaps);
 
2630
        a->rewriteconds    = apr_array_append(p, overrides->rewriteconds,
 
2631
                                              base->rewriteconds);
 
2632
        a->rewriterules    = apr_array_append(p, overrides->rewriterules,
 
2633
                                              base->rewriterules);
 
2634
    }
 
2635
    else {
 
2636
        /*
 
2637
         *  local directives override
 
2638
         *  and anything else gets defaults
 
2639
         */
 
2640
#ifndef REWRITELOG_DISABLED
 
2641
        a->rewriteloglevel = overrides->rewriteloglevel;
 
2642
        a->rewritelogfile  = overrides->rewritelogfile;
 
2643
        a->rewritelogfp    = overrides->rewritelogfp;
 
2644
#endif
 
2645
        a->rewritemaps     = overrides->rewritemaps;
 
2646
        a->rewriteconds    = overrides->rewriteconds;
 
2647
        a->rewriterules    = overrides->rewriterules;
 
2648
    }
 
2649
 
 
2650
    return (void *)a;
 
2651
}
 
2652
 
 
2653
static void *config_perdir_create(apr_pool_t *p, char *path)
 
2654
{
 
2655
    rewrite_perdir_conf *a;
 
2656
 
 
2657
    a = (rewrite_perdir_conf *)apr_pcalloc(p, sizeof(rewrite_perdir_conf));
 
2658
 
 
2659
    a->state           = ENGINE_DISABLED;
 
2660
    a->options         = OPTION_NONE;
 
2661
    a->baseurl         = NULL;
 
2662
    a->rewriteconds    = apr_array_make(p, 2, sizeof(rewritecond_entry));
 
2663
    a->rewriterules    = apr_array_make(p, 2, sizeof(rewriterule_entry));
 
2664
 
 
2665
    if (path == NULL) {
 
2666
        a->directory = NULL;
 
2667
    }
 
2668
    else {
 
2669
        /* make sure it has a trailing slash */
 
2670
        if (path[strlen(path)-1] == '/') {
 
2671
            a->directory = apr_pstrdup(p, path);
 
2672
        }
 
2673
        else {
 
2674
            a->directory = apr_pstrcat(p, path, "/", NULL);
 
2675
        }
 
2676
    }
 
2677
 
 
2678
    return (void *)a;
 
2679
}
 
2680
 
 
2681
static void *config_perdir_merge(apr_pool_t *p, void *basev, void *overridesv)
 
2682
{
 
2683
    rewrite_perdir_conf *a, *base, *overrides;
 
2684
 
 
2685
    a         = (rewrite_perdir_conf *)apr_pcalloc(p,
 
2686
                                                  sizeof(rewrite_perdir_conf));
 
2687
    base      = (rewrite_perdir_conf *)basev;
 
2688
    overrides = (rewrite_perdir_conf *)overridesv;
 
2689
 
 
2690
    a->state     = overrides->state;
 
2691
    a->options   = overrides->options;
 
2692
    a->directory = overrides->directory;
 
2693
    a->baseurl   = overrides->baseurl;
 
2694
 
 
2695
    if (a->options & OPTION_INHERIT) {
 
2696
        a->rewriteconds = apr_array_append(p, overrides->rewriteconds,
 
2697
                                           base->rewriteconds);
 
2698
        a->rewriterules = apr_array_append(p, overrides->rewriterules,
 
2699
                                           base->rewriterules);
 
2700
    }
 
2701
    else {
 
2702
        a->rewriteconds = overrides->rewriteconds;
 
2703
        a->rewriterules = overrides->rewriterules;
 
2704
    }
 
2705
 
 
2706
    return (void *)a;
 
2707
}
 
2708
 
 
2709
static const char *cmd_rewriteengine(cmd_parms *cmd,
 
2710
                                     void *in_dconf, int flag)
 
2711
{
 
2712
    rewrite_perdir_conf *dconf = in_dconf;
 
2713
    rewrite_server_conf *sconf;
 
2714
 
 
2715
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
2716
 
 
2717
    if (cmd->path == NULL) { /* is server command */
 
2718
        sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
 
2719
    }
 
2720
    else                   /* is per-directory command */ {
 
2721
        dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
 
2722
    }
 
2723
 
 
2724
    return NULL;
 
2725
}
 
2726
 
 
2727
static const char *cmd_rewriteoptions(cmd_parms *cmd,
 
2728
                                      void *in_dconf, const char *option)
 
2729
{
 
2730
    int options = 0;
 
2731
    char *w;
 
2732
 
 
2733
    while (*option) {
 
2734
        w = ap_getword_conf(cmd->pool, &option);
 
2735
 
 
2736
        if (!strcasecmp(w, "inherit")) {
 
2737
            options |= OPTION_INHERIT;
 
2738
        }
 
2739
        else if (!strncasecmp(w, "MaxRedirects=", 13)) {
 
2740
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
2741
                         "RewriteOptions: MaxRedirects option has been "
 
2742
                         "removed in favor of the global "
 
2743
                         "LimitInternalRecursion directive and will be "
 
2744
                         "ignored.");
 
2745
        }
 
2746
        else {
 
2747
            return apr_pstrcat(cmd->pool, "RewriteOptions: unknown option '",
 
2748
                               w, "'", NULL);
 
2749
        }
 
2750
    }
 
2751
 
 
2752
    /* put it into the appropriate config */
 
2753
    if (cmd->path == NULL) { /* is server command */
 
2754
        rewrite_server_conf *conf =
 
2755
            ap_get_module_config(cmd->server->module_config,
 
2756
                                 &rewrite_module);
 
2757
 
 
2758
        conf->options |= options;
 
2759
    }
 
2760
    else {                  /* is per-directory command */
 
2761
        rewrite_perdir_conf *conf = in_dconf;
 
2762
 
 
2763
        conf->options |= options;
 
2764
    }
 
2765
 
 
2766
    return NULL;
 
2767
}
 
2768
 
 
2769
#ifndef REWRITELOG_DISABLED
 
2770
static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, const char *a1)
 
2771
{
 
2772
    rewrite_server_conf *sconf;
 
2773
 
 
2774
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
2775
    sconf->rewritelogfile = a1;
 
2776
 
 
2777
    return NULL;
 
2778
}
 
2779
 
 
2780
static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf,
 
2781
                                       const char *a1)
 
2782
{
 
2783
    rewrite_server_conf *sconf;
 
2784
 
 
2785
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
2786
    sconf->rewriteloglevel = atoi(a1);
 
2787
 
 
2788
    return NULL;
 
2789
}
 
2790
#endif /* rewritelog */
 
2791
 
 
2792
static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1,
 
2793
                                  const char *a2)
 
2794
{
 
2795
    rewrite_server_conf *sconf;
 
2796
    rewritemap_entry *newmap;
 
2797
    apr_finfo_t st;
 
2798
    const char *fname;
 
2799
 
 
2800
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
2801
 
 
2802
    newmap = apr_palloc(cmd->pool, sizeof(rewritemap_entry));
 
2803
    newmap->func = NULL;
 
2804
 
 
2805
    if (strncasecmp(a2, "txt:", 4) == 0) {
 
2806
        if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) {
 
2807
            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ",
 
2808
                               a2+4, NULL);
 
2809
        }
 
2810
 
 
2811
        newmap->type      = MAPTYPE_TXT;
 
2812
        newmap->datafile  = fname;
 
2813
        newmap->checkfile = fname;
 
2814
        newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
 
2815
                                         (void *)cmd->server, a1);
 
2816
    }
 
2817
    else if (strncasecmp(a2, "rnd:", 4) == 0) {
 
2818
        if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) {
 
2819
            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to rnd map: ",
 
2820
                               a2+4, NULL);
 
2821
        }
 
2822
 
 
2823
        newmap->type      = MAPTYPE_RND;
 
2824
        newmap->datafile  = fname;
 
2825
        newmap->checkfile = fname;
 
2826
        newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
 
2827
                                         (void *)cmd->server, a1);
 
2828
    }
 
2829
    else if (strncasecmp(a2, "dbm", 3) == 0) {
 
2830
        const char *ignored_fname;
 
2831
        apr_status_t rv;
 
2832
 
 
2833
        newmap->type = MAPTYPE_DBM;
 
2834
        fname = NULL;
 
2835
        newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
 
2836
                                         (void *)cmd->server, a1);
 
2837
 
 
2838
        if (a2[3] == ':') {
 
2839
            newmap->dbmtype = "default";
 
2840
            fname = a2+4;
 
2841
        }
 
2842
        else if (a2[3] == '=') {
 
2843
            const char *colon = ap_strchr_c(a2 + 4, ':');
 
2844
 
 
2845
            if (colon) {
 
2846
                newmap->dbmtype = apr_pstrndup(cmd->pool, a2 + 4,
 
2847
                                               colon - (a2 + 3) - 1);
 
2848
                fname = colon + 1;
 
2849
            }
 
2850
        }
 
2851
 
 
2852
        if (!fname) {
 
2853
            return apr_pstrcat(cmd->pool, "RewriteMap: bad map:",
 
2854
                               a2, NULL);
 
2855
        }
 
2856
 
 
2857
        if ((newmap->datafile = ap_server_root_relative(cmd->pool,
 
2858
                                                        fname)) == NULL) {
 
2859
            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to dbm map: ",
 
2860
                               fname, NULL);
 
2861
        }
 
2862
 
 
2863
        rv = apr_dbm_get_usednames_ex(cmd->pool, newmap->dbmtype,
 
2864
                                      newmap->datafile, &newmap->checkfile,
 
2865
                                      &ignored_fname);
 
2866
        if (rv != APR_SUCCESS) {
 
2867
            return apr_pstrcat(cmd->pool, "RewriteMap: dbm type ",
 
2868
                               newmap->dbmtype, " is invalid", NULL);
 
2869
        }
 
2870
    }
 
2871
    else if (strncasecmp(a2, "prg:", 4) == 0) {
 
2872
        apr_tokenize_to_argv(a2 + 4, &newmap->argv, cmd->pool);
 
2873
 
 
2874
        fname = newmap->argv[0];
 
2875
        if ((newmap->argv[0] = ap_server_root_relative(cmd->pool,
 
2876
                                                       fname)) == NULL) {
 
2877
            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to prg map: ",
 
2878
                               fname, NULL);
 
2879
        }
 
2880
 
 
2881
        newmap->type      = MAPTYPE_PRG;
 
2882
        newmap->datafile  = NULL;
 
2883
        newmap->checkfile = newmap->argv[0];
 
2884
        newmap->cachename = NULL;
 
2885
    }
 
2886
    else if (strncasecmp(a2, "int:", 4) == 0) {
 
2887
        newmap->type      = MAPTYPE_INT;
 
2888
        newmap->datafile  = NULL;
 
2889
        newmap->checkfile = NULL;
 
2890
        newmap->cachename = NULL;
 
2891
        newmap->func      = (char *(*)(request_rec *,char *))
 
2892
                            apr_hash_get(mapfunc_hash, a2+4, strlen(a2+4));
 
2893
        if ((sconf->state == ENGINE_ENABLED) && (newmap->func == NULL)) {
 
2894
            return apr_pstrcat(cmd->pool, "RewriteMap: internal map not found:",
 
2895
                               a2+4, NULL);
 
2896
        }
 
2897
    }
 
2898
    else {
 
2899
        if ((fname = ap_server_root_relative(cmd->pool, a2)) == NULL) {
 
2900
            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ",
 
2901
                               a2, NULL);
 
2902
        }
 
2903
 
 
2904
        newmap->type      = MAPTYPE_TXT;
 
2905
        newmap->datafile  = fname;
 
2906
        newmap->checkfile = fname;
 
2907
        newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
 
2908
                                         (void *)cmd->server, a1);
 
2909
    }
 
2910
    newmap->fpin  = NULL;
 
2911
    newmap->fpout = NULL;
 
2912
 
 
2913
    if (newmap->checkfile && (sconf->state == ENGINE_ENABLED)
 
2914
        && (apr_stat(&st, newmap->checkfile, APR_FINFO_MIN,
 
2915
                     cmd->pool) != APR_SUCCESS)) {
 
2916
        return apr_pstrcat(cmd->pool,
 
2917
                           "RewriteMap: file for map ", a1,
 
2918
                           " not found:", newmap->checkfile, NULL);
 
2919
    }
 
2920
 
 
2921
    apr_hash_set(sconf->rewritemaps, a1, APR_HASH_KEY_STRING, newmap);
 
2922
 
 
2923
    return NULL;
 
2924
}
 
2925
 
 
2926
static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, const char *a1)
 
2927
{
 
2928
    const char *error;
 
2929
 
 
2930
    if ((error = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
 
2931
        return error;
 
2932
 
 
2933
    /* fixup the path, especially for rewritelock_remove() */
 
2934
    lockname = ap_server_root_relative(cmd->pool, a1);
 
2935
 
 
2936
    if (!lockname) {
 
2937
        return apr_pstrcat(cmd->pool, "Invalid RewriteLock path ", a1);
 
2938
    }
 
2939
 
 
2940
    return NULL;
 
2941
}
 
2942
 
 
2943
static const char *cmd_rewritebase(cmd_parms *cmd, void *in_dconf,
 
2944
                                   const char *a1)
 
2945
{
 
2946
    rewrite_perdir_conf *dconf = in_dconf;
 
2947
 
 
2948
    if (cmd->path == NULL || dconf == NULL) {
 
2949
        return "RewriteBase: only valid in per-directory config files";
 
2950
    }
 
2951
    if (a1[0] == '\0') {
 
2952
        return "RewriteBase: empty URL not allowed";
 
2953
    }
 
2954
    if (a1[0] != '/') {
 
2955
        return "RewriteBase: argument is not a valid URL";
 
2956
    }
 
2957
 
 
2958
    dconf->baseurl = a1;
 
2959
 
 
2960
    return NULL;
 
2961
}
 
2962
 
 
2963
/*
 
2964
 * generic lexer for RewriteRule and RewriteCond flags.
 
2965
 * The parser will be passed in as a function pointer
 
2966
 * and called if a flag was found
 
2967
 */
 
2968
static const char *cmd_parseflagfield(apr_pool_t *p, void *cfg, char *key,
 
2969
                                      const char *(*parse)(apr_pool_t *,
 
2970
                                                           void *,
 
2971
                                                           char *, char *))
 
2972
{
 
2973
    char *val, *nextp, *endp;
 
2974
    const char *err;
 
2975
 
 
2976
    endp = key + strlen(key) - 1;
 
2977
    if (*key != '[' || *endp != ']') {
 
2978
        return "RewriteCond: bad flag delimiters";
 
2979
    }
 
2980
 
 
2981
    *endp = ','; /* for simpler parsing */
 
2982
    ++key;
 
2983
 
 
2984
    while (*key) {
 
2985
        /* skip leading spaces */
 
2986
        while (apr_isspace(*key)) {
 
2987
            ++key;
 
2988
        }
 
2989
 
 
2990
        if (!*key || (nextp = ap_strchr(key, ',')) == NULL) { /* NULL should not
 
2991
                                                               * happen, but ...
 
2992
                                                               */
 
2993
            break;
 
2994
        }
 
2995
 
 
2996
        /* strip trailing spaces */
 
2997
        endp = nextp - 1;
 
2998
        while (apr_isspace(*endp)) {
 
2999
            --endp;
 
3000
        }
 
3001
        *++endp = '\0';
 
3002
 
 
3003
        /* split key and val */
 
3004
        val = ap_strchr(key, '=');
 
3005
        if (val) {
 
3006
            *val++ = '\0';
 
3007
        }
 
3008
        else {
 
3009
            val = endp;
 
3010
        }
 
3011
 
 
3012
        err = parse(p, cfg, key, val);
 
3013
        if (err) {
 
3014
            return err;
 
3015
        }
 
3016
 
 
3017
        key = nextp + 1;
 
3018
    }
 
3019
 
 
3020
    return NULL;
 
3021
}
 
3022
 
 
3023
static const char *cmd_rewritecond_setflag(apr_pool_t *p, void *_cfg,
 
3024
                                           char *key, char *val)
 
3025
{
 
3026
    rewritecond_entry *cfg = _cfg;
 
3027
 
 
3028
    if (   strcasecmp(key, "nocase") == 0
 
3029
        || strcasecmp(key, "NC") == 0    ) {
 
3030
        cfg->flags |= CONDFLAG_NOCASE;
 
3031
    }
 
3032
    else if (   strcasecmp(key, "ornext") == 0
 
3033
             || strcasecmp(key, "OR") == 0    ) {
 
3034
        cfg->flags |= CONDFLAG_ORNEXT;
 
3035
    }
 
3036
    else {
 
3037
        return apr_pstrcat(p, "RewriteCond: unknown flag '", key, "'", NULL);
 
3038
    }
 
3039
    return NULL;
 
3040
}
 
3041
 
 
3042
static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
 
3043
                                   const char *in_str)
 
3044
{
 
3045
    rewrite_perdir_conf *dconf = in_dconf;
 
3046
    char *str = apr_pstrdup(cmd->pool, in_str);
 
3047
    rewrite_server_conf *sconf;
 
3048
    rewritecond_entry *newcond;
 
3049
    ap_regex_t *regexp;
 
3050
    char *a1;
 
3051
    char *a2;
 
3052
    char *a3;
 
3053
    const char *err;
 
3054
 
 
3055
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
3056
 
 
3057
    /*  make a new entry in the internal temporary rewrite rule list */
 
3058
    if (cmd->path == NULL) {   /* is server command */
 
3059
        newcond = apr_array_push(sconf->rewriteconds);
 
3060
    }
 
3061
    else {                     /* is per-directory command */
 
3062
        newcond = apr_array_push(dconf->rewriteconds);
 
3063
    }
 
3064
 
 
3065
    /* parse the argument line ourself
 
3066
     * a1 .. a3 are substrings of str, which is a fresh copy
 
3067
     * of the argument line. So we can use a1 .. a3 without
 
3068
     * copying them again.
 
3069
     */
 
3070
    if (parseargline(str, &a1, &a2, &a3)) {
 
3071
        return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str,
 
3072
                           "'", NULL);
 
3073
    }
 
3074
 
 
3075
    /* arg1: the input string */
 
3076
    newcond->input = a1;
 
3077
 
 
3078
    /* arg3: optional flags field
 
3079
     * (this has to be parsed first, because we need to
 
3080
     *  know if the regex should be compiled with ICASE!)
 
3081
     */
 
3082
    newcond->flags = CONDFLAG_NONE;
 
3083
    if (a3 != NULL) {
 
3084
        if ((err = cmd_parseflagfield(cmd->pool, newcond, a3,
 
3085
                                      cmd_rewritecond_setflag)) != NULL) {
 
3086
            return err;
 
3087
        }
 
3088
    }
 
3089
 
 
3090
    /* arg2: the pattern */
 
3091
    if (*a2 == '!') {
 
3092
        newcond->flags |= CONDFLAG_NOTMATCH;
 
3093
        ++a2;
 
3094
    }
 
3095
 
 
3096
    /* determine the pattern type */
 
3097
    newcond->ptype = 0;
 
3098
    if (*a2 && a2[1]) {
 
3099
        if (!a2[2] && *a2 == '-') {
 
3100
            switch (a2[1]) {
 
3101
            case 'f': newcond->ptype = CONDPAT_FILE_EXISTS; break;
 
3102
            case 's': newcond->ptype = CONDPAT_FILE_SIZE;   break;
 
3103
            case 'l': newcond->ptype = CONDPAT_FILE_LINK;   break;
 
3104
            case 'd': newcond->ptype = CONDPAT_FILE_DIR;    break;
 
3105
            case 'x': newcond->ptype = CONDPAT_FILE_XBIT;   break;
 
3106
            case 'U': newcond->ptype = CONDPAT_LU_URL;      break;
 
3107
            case 'F': newcond->ptype = CONDPAT_LU_FILE;     break;
 
3108
            }
 
3109
        }
 
3110
        else {
 
3111
            switch (*a2) {
 
3112
            case '>': newcond->ptype = CONDPAT_STR_GT; break;
 
3113
            case '<': newcond->ptype = CONDPAT_STR_LT; break;
 
3114
            case '=': newcond->ptype = CONDPAT_STR_EQ;
 
3115
                /* "" represents an empty string */
 
3116
                if (*++a2 == '"' && a2[1] == '"' && !a2[2]) {
 
3117
                    a2 += 2;
 
3118
                }
 
3119
                break;
 
3120
            }
 
3121
        }
 
3122
    }
 
3123
 
 
3124
    if (newcond->ptype && newcond->ptype != CONDPAT_STR_EQ &&
 
3125
        (newcond->flags & CONDFLAG_NOCASE)) {
 
3126
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
3127
                     "RewriteCond: NoCase option for non-regex pattern '%s' "
 
3128
                     "is not supported and will be ignored.", a2);
 
3129
        newcond->flags &= ~CONDFLAG_NOCASE;
 
3130
    }
 
3131
 
 
3132
    newcond->pattern = a2;
 
3133
 
 
3134
    if (!newcond->ptype) {
 
3135
        regexp = ap_pregcomp(cmd->pool, a2,
 
3136
                             AP_REG_EXTENDED | ((newcond->flags & CONDFLAG_NOCASE)
 
3137
                                             ? AP_REG_ICASE : 0));
 
3138
        if (!regexp) {
 
3139
            return apr_pstrcat(cmd->pool, "RewriteCond: cannot compile regular "
 
3140
                               "expression '", a2, "'", NULL);
 
3141
        }
 
3142
 
 
3143
        newcond->regexp  = regexp;
 
3144
    }
 
3145
 
 
3146
    return NULL;
 
3147
}
 
3148
 
 
3149
static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg,
 
3150
                                           char *key, char *val)
 
3151
{
 
3152
    rewriterule_entry *cfg = _cfg;
 
3153
    int error = 0;
 
3154
 
 
3155
    switch (*key++) {
 
3156
    case 'c':
 
3157
    case 'C':
 
3158
        if (!*key || !strcasecmp(key, "hain")) {           /* chain */
 
3159
            cfg->flags |= RULEFLAG_CHAIN;
 
3160
        }
 
3161
        else if (((*key == 'O' || *key == 'o') && !key[1])
 
3162
                 || !strcasecmp(key, "ookie")) {           /* cookie */
 
3163
            data_item *cp = cfg->cookie;
 
3164
 
 
3165
            if (!cp) {
 
3166
                cp = cfg->cookie = apr_palloc(p, sizeof(*cp));
 
3167
            }
 
3168
            else {
 
3169
                while (cp->next) {
 
3170
                    cp = cp->next;
 
3171
                }
 
3172
                cp->next = apr_palloc(p, sizeof(*cp));
 
3173
                cp = cp->next;
 
3174
            }
 
3175
 
 
3176
            cp->next = NULL;
 
3177
            cp->data = val;
 
3178
        }
 
3179
        else {
 
3180
            ++error;
 
3181
        }
 
3182
        break;
 
3183
 
 
3184
    case 'e':
 
3185
    case 'E':
 
3186
        if (!*key || !strcasecmp(key, "nv")) {             /* env */
 
3187
            data_item *cp = cfg->env;
 
3188
 
 
3189
            if (!cp) {
 
3190
                cp = cfg->env = apr_palloc(p, sizeof(*cp));
 
3191
            }
 
3192
            else {
 
3193
                while (cp->next) {
 
3194
                    cp = cp->next;
 
3195
                }
 
3196
                cp->next = apr_palloc(p, sizeof(*cp));
 
3197
                cp = cp->next;
 
3198
            }
 
3199
 
 
3200
            cp->next = NULL;
 
3201
            cp->data = val;
 
3202
        }
 
3203
        else {
 
3204
            ++error;
 
3205
        }
 
3206
        break;
 
3207
 
 
3208
    case 'f':
 
3209
    case 'F':
 
3210
        if (!*key || !strcasecmp(key, "orbidden")) {       /* forbidden */
 
3211
            cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB);
 
3212
            cfg->forced_responsecode = HTTP_FORBIDDEN;
 
3213
        }
 
3214
        else {
 
3215
            ++error;
 
3216
        }
 
3217
        break;
 
3218
 
 
3219
    case 'g':
 
3220
    case 'G':
 
3221
        if (!*key || !strcasecmp(key, "one")) {            /* gone */
 
3222
            cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB);
 
3223
            cfg->forced_responsecode = HTTP_GONE;
 
3224
        }
 
3225
        else {
 
3226
            ++error;
 
3227
        }
 
3228
        break;
 
3229
 
 
3230
    case 'h':
 
3231
    case 'H':
 
3232
        if (!*key || !strcasecmp(key, "andler")) {         /* handler */
 
3233
            cfg->forced_handler = val;
 
3234
        }
 
3235
        else {
 
3236
            ++error;
 
3237
        }
 
3238
        break;
 
3239
 
 
3240
    case 'l':
 
3241
    case 'L':
 
3242
        if (!*key || !strcasecmp(key, "ast")) {            /* last */
 
3243
            cfg->flags |= RULEFLAG_LASTRULE;
 
3244
        }
 
3245
        else {
 
3246
            ++error;
 
3247
        }
 
3248
        break;
 
3249
 
 
3250
    case 'n':
 
3251
    case 'N':
 
3252
        if (((*key == 'E' || *key == 'e') && !key[1])
 
3253
            || !strcasecmp(key, "oescape")) {              /* noescape */
 
3254
            cfg->flags |= RULEFLAG_NOESCAPE;
 
3255
        }
 
3256
        else if (!*key || !strcasecmp(key, "ext")) {       /* next */
 
3257
            cfg->flags |= RULEFLAG_NEWROUND;
 
3258
        }
 
3259
        else if (((*key == 'S' || *key == 's') && !key[1])
 
3260
            || !strcasecmp(key, "osubreq")) {              /* nosubreq */
 
3261
            cfg->flags |= RULEFLAG_IGNOREONSUBREQ;
 
3262
        }
 
3263
        else if (((*key == 'C' || *key == 'c') && !key[1])
 
3264
            || !strcasecmp(key, "ocase")) {                /* nocase */
 
3265
            cfg->flags |= RULEFLAG_NOCASE;
 
3266
        }
 
3267
        else {
 
3268
            ++error;
 
3269
        }
 
3270
        break;
 
3271
 
 
3272
    case 'p':
 
3273
    case 'P':
 
3274
        if (!*key || !strcasecmp(key, "roxy")) {           /* proxy */
 
3275
            cfg->flags |= RULEFLAG_PROXY;
 
3276
        }
 
3277
        else if (((*key == 'T' || *key == 't') && !key[1])
 
3278
            || !strcasecmp(key, "assthrough")) {           /* passthrough */
 
3279
            cfg->flags |= RULEFLAG_PASSTHROUGH;
 
3280
        }
 
3281
        else {
 
3282
            ++error;
 
3283
        }
 
3284
        break;
 
3285
 
 
3286
    case 'q':
 
3287
    case 'Q':
 
3288
        if (   !strcasecmp(key, "SA")
 
3289
            || !strcasecmp(key, "sappend")) {              /* qsappend */
 
3290
            cfg->flags |= RULEFLAG_QSAPPEND;
 
3291
        }
 
3292
        else {
 
3293
            ++error;
 
3294
        }
 
3295
        break;
 
3296
 
 
3297
    case 'r':
 
3298
    case 'R':
 
3299
        if (!*key || !strcasecmp(key, "edirect")) {        /* redirect */
 
3300
            int status = 0;
 
3301
 
 
3302
            cfg->flags |= RULEFLAG_FORCEREDIRECT;
 
3303
            if (strlen(val) > 0) {
 
3304
                if (strcasecmp(val, "permanent") == 0) {
 
3305
                    status = HTTP_MOVED_PERMANENTLY;
 
3306
                }
 
3307
                else if (strcasecmp(val, "temp") == 0) {
 
3308
                    status = HTTP_MOVED_TEMPORARILY;
 
3309
                }
 
3310
                else if (strcasecmp(val, "seeother") == 0) {
 
3311
                    status = HTTP_SEE_OTHER;
 
3312
                }
 
3313
                else if (apr_isdigit(*val)) {
 
3314
                    status = atoi(val);
 
3315
                    if (status != HTTP_INTERNAL_SERVER_ERROR) {
 
3316
                        int idx =
 
3317
                            ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
 
3318
 
 
3319
                        if (ap_index_of_response(status) == idx) {
 
3320
                            return apr_psprintf(p, "RewriteRule: invalid HTTP "
 
3321
                                                   "response code '%s' for "
 
3322
                                                   "flag 'R'",
 
3323
                                                val);
 
3324
                        }
 
3325
                    }
 
3326
                    if (!ap_is_HTTP_REDIRECT(status)) {
 
3327
                        cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB);
 
3328
                    }
 
3329
                }
 
3330
                cfg->forced_responsecode = status;
 
3331
            }
 
3332
        }
 
3333
        else {
 
3334
            ++error;
 
3335
        }
 
3336
        break;
 
3337
 
 
3338
    case 's':
 
3339
    case 'S':
 
3340
        if (!*key || !strcasecmp(key, "kip")) {            /* skip */
 
3341
            cfg->skip = atoi(val);
 
3342
        }
 
3343
        else {
 
3344
            ++error;
 
3345
        }
 
3346
        break;
 
3347
 
 
3348
    case 't':
 
3349
    case 'T':
 
3350
        if (!*key || !strcasecmp(key, "ype")) {            /* type */
 
3351
            cfg->forced_mimetype = val;
 
3352
        }
 
3353
        else {
 
3354
            ++error;
 
3355
        }
 
3356
        break;
 
3357
 
 
3358
    default:
 
3359
        ++error;
 
3360
        break;
 
3361
    }
 
3362
 
 
3363
    if (error) {
 
3364
        return apr_pstrcat(p, "RewriteRule: unknown flag '", --key, "'", NULL);
 
3365
    }
 
3366
 
 
3367
    return NULL;
 
3368
}
 
3369
 
 
3370
static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
 
3371
                                   const char *in_str)
 
3372
{
 
3373
    rewrite_perdir_conf *dconf = in_dconf;
 
3374
    char *str = apr_pstrdup(cmd->pool, in_str);
 
3375
    rewrite_server_conf *sconf;
 
3376
    rewriterule_entry *newrule;
 
3377
    ap_regex_t *regexp;
 
3378
    char *a1;
 
3379
    char *a2;
 
3380
    char *a3;
 
3381
    const char *err;
 
3382
 
 
3383
    sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module);
 
3384
 
 
3385
    /*  make a new entry in the internal rewrite rule list */
 
3386
    if (cmd->path == NULL) {   /* is server command */
 
3387
        newrule = apr_array_push(sconf->rewriterules);
 
3388
    }
 
3389
    else {                     /* is per-directory command */
 
3390
        newrule = apr_array_push(dconf->rewriterules);
 
3391
    }
 
3392
 
 
3393
    /*  parse the argument line ourself */
 
3394
    if (parseargline(str, &a1, &a2, &a3)) {
 
3395
        return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str,
 
3396
                           "'", NULL);
 
3397
    }
 
3398
 
 
3399
    /* arg3: optional flags field */
 
3400
    newrule->forced_mimetype     = NULL;
 
3401
    newrule->forced_handler      = NULL;
 
3402
    newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY;
 
3403
    newrule->flags  = RULEFLAG_NONE;
 
3404
    newrule->env = NULL;
 
3405
    newrule->cookie = NULL;
 
3406
    newrule->skip   = 0;
 
3407
    if (a3 != NULL) {
 
3408
        if ((err = cmd_parseflagfield(cmd->pool, newrule, a3,
 
3409
                                      cmd_rewriterule_setflag)) != NULL) {
 
3410
            return err;
 
3411
        }
 
3412
    }
 
3413
 
 
3414
    /* arg1: the pattern
 
3415
     * try to compile the regexp to test if is ok
 
3416
     */
 
3417
    if (*a1 == '!') {
 
3418
        newrule->flags |= RULEFLAG_NOTMATCH;
 
3419
        ++a1;
 
3420
    }
 
3421
 
 
3422
    regexp = ap_pregcomp(cmd->pool, a1, AP_REG_EXTENDED |
 
3423
                                        ((newrule->flags & RULEFLAG_NOCASE)
 
3424
                                         ? AP_REG_ICASE : 0));
 
3425
    if (!regexp) {
 
3426
        return apr_pstrcat(cmd->pool,
 
3427
                           "RewriteRule: cannot compile regular expression '",
 
3428
                           a1, "'", NULL);
 
3429
    }
 
3430
 
 
3431
    newrule->pattern = a1;
 
3432
    newrule->regexp  = regexp;
 
3433
 
 
3434
    /* arg2: the output string */
 
3435
    newrule->output = a2;
 
3436
    if (*a2 == '-' && !a2[1]) {
 
3437
        newrule->flags |= RULEFLAG_NOSUB;
 
3438
    }
 
3439
 
 
3440
    /* now, if the server or per-dir config holds an
 
3441
     * array of RewriteCond entries, we take it for us
 
3442
     * and clear the array
 
3443
     */
 
3444
    if (cmd->path == NULL) {  /* is server command */
 
3445
        newrule->rewriteconds   = sconf->rewriteconds;
 
3446
        sconf->rewriteconds = apr_array_make(cmd->pool, 2,
 
3447
                                             sizeof(rewritecond_entry));
 
3448
    }
 
3449
    else {                    /* is per-directory command */
 
3450
        newrule->rewriteconds   = dconf->rewriteconds;
 
3451
        dconf->rewriteconds = apr_array_make(cmd->pool, 2,
 
3452
                                             sizeof(rewritecond_entry));
 
3453
    }
 
3454
 
 
3455
    return NULL;
 
3456
}
 
3457
 
 
3458
 
 
3459
/*
 
3460
 * +-------------------------------------------------------+
 
3461
 * |                                                       |
 
3462
 * |                  the rewriting engine
 
3463
 * |                                                       |
 
3464
 * +-------------------------------------------------------+
 
3465
 */
 
3466
 
 
3467
/* Lexicographic Compare */
 
3468
static APR_INLINE int compare_lexicography(char *a, char *b)
 
3469
{
 
3470
    apr_size_t i, lena, lenb;
 
3471
 
 
3472
    lena = strlen(a);
 
3473
    lenb = strlen(b);
 
3474
 
 
3475
    if (lena == lenb) {
 
3476
        for (i = 0; i < lena; ++i) {
 
3477
            if (a[i] != b[i]) {
 
3478
                return ((unsigned char)a[i] > (unsigned char)b[i]) ? 1 : -1;
 
3479
            }
 
3480
        }
 
3481
 
 
3482
        return 0;
 
3483
    }
 
3484
 
 
3485
    return ((lena > lenb) ? 1 : -1);
 
3486
}
 
3487
 
 
3488
/*
 
3489
 * Apply a single rewriteCond
 
3490
 */
 
3491
static int apply_rewrite_cond(rewritecond_entry *p, rewrite_ctx *ctx)
 
3492
{
 
3493
    char *input = do_expand(p->input, ctx);
 
3494
    apr_finfo_t sb;
 
3495
    request_rec *rsub, *r = ctx->r;
 
3496
    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
 
3497
    int rc = 0;
 
3498
 
 
3499
    switch (p->ptype) {
 
3500
    case CONDPAT_FILE_EXISTS:
 
3501
        if (   apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS
 
3502
            && sb.filetype == APR_REG) {
 
3503
            rc = 1;
 
3504
        }
 
3505
        break;
 
3506
 
 
3507
    case CONDPAT_FILE_SIZE:
 
3508
        if (   apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS
 
3509
            && sb.filetype == APR_REG && sb.size > 0) {
 
3510
            rc = 1;
 
3511
        }
 
3512
        break;
 
3513
 
 
3514
    case CONDPAT_FILE_LINK:
 
3515
#if !defined(OS2)
 
3516
        if (   apr_stat(&sb, input, APR_FINFO_MIN | APR_FINFO_LINK,
 
3517
                        r->pool) == APR_SUCCESS
 
3518
            && sb.filetype == APR_LNK) {
 
3519
            rc = 1;
 
3520
        }
 
3521
#endif
 
3522
        break;
 
3523
 
 
3524
    case CONDPAT_FILE_DIR:
 
3525
        if (   apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS
 
3526
            && sb.filetype == APR_DIR) {
 
3527
            rc = 1;
 
3528
        }
 
3529
        break;
 
3530
 
 
3531
    case CONDPAT_FILE_XBIT:
 
3532
        if (   apr_stat(&sb, input, APR_FINFO_PROT, r->pool) == APR_SUCCESS
 
3533
            && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
 
3534
            rc = 1;
 
3535
        }
 
3536
        break;
 
3537
 
 
3538
    case CONDPAT_LU_URL:
 
3539
        if (*input && subreq_ok(r)) {
 
3540
            rsub = ap_sub_req_lookup_uri(input, r, NULL);
 
3541
            if (rsub->status < 400) {
 
3542
                rc = 1;
 
3543
            }
 
3544
            rewritelog((r, 5, NULL, "RewriteCond URI (-U) check: "
 
3545
                        "path=%s -> status=%d", input, rsub->status));
 
3546
            ap_destroy_sub_req(rsub);
 
3547
        }
 
3548
        break;
 
3549
 
 
3550
    case CONDPAT_LU_FILE:
 
3551
        if (*input && subreq_ok(r)) {
 
3552
            rsub = ap_sub_req_lookup_file(input, r, NULL);
 
3553
            if (rsub->status < 300 &&
 
3554
                /* double-check that file exists since default result is 200 */
 
3555
                apr_stat(&sb, rsub->filename, APR_FINFO_MIN,
 
3556
                         r->pool) == APR_SUCCESS) {
 
3557
                rc = 1;
 
3558
            }
 
3559
            rewritelog((r, 5, NULL, "RewriteCond file (-F) check: path=%s "
 
3560
                        "-> file=%s status=%d", input, rsub->filename,
 
3561
                        rsub->status));
 
3562
            ap_destroy_sub_req(rsub);
 
3563
        }
 
3564
        break;
 
3565
 
 
3566
    case CONDPAT_STR_GT:
 
3567
        rc = (compare_lexicography(input, p->pattern+1) == 1) ? 1 : 0;
 
3568
        break;
 
3569
 
 
3570
    case CONDPAT_STR_LT:
 
3571
        rc = (compare_lexicography(input, p->pattern+1) == -1) ? 1 : 0;
 
3572
        break;
 
3573
 
 
3574
    case CONDPAT_STR_EQ:
 
3575
        if (p->flags & CONDFLAG_NOCASE) {
 
3576
            rc = !strcasecmp(input, p->pattern);
 
3577
        }
 
3578
        else {
 
3579
            rc = !strcmp(input, p->pattern);
 
3580
        }
 
3581
        break;
 
3582
 
 
3583
    default:
 
3584
        /* it is really a regexp pattern, so apply it */
 
3585
        rc = !ap_regexec(p->regexp, input, AP_MAX_REG_MATCH, regmatch, 0);
 
3586
 
 
3587
        /* update briRC backref info */
 
3588
        if (rc && !(p->flags & CONDFLAG_NOTMATCH)) {
 
3589
            ctx->briRC.source = input;
 
3590
            ctx->briRC.nsub   = p->regexp->re_nsub;
 
3591
            memcpy(ctx->briRC.regmatch, regmatch, sizeof(regmatch));
 
3592
        }
 
3593
        break;
 
3594
    }
 
3595
 
 
3596
    if (p->flags & CONDFLAG_NOTMATCH) {
 
3597
        rc = !rc;
 
3598
    }
 
3599
 
 
3600
    rewritelog((r, 4, ctx->perdir, "RewriteCond: input='%s' pattern='%s%s%s'%s "
 
3601
                "=> %s", input, (p->flags & CONDFLAG_NOTMATCH) ? "!" : "",
 
3602
                (p->ptype == CONDPAT_STR_EQ) ? "=" : "", p->pattern,
 
3603
                (p->flags & CONDFLAG_NOCASE) ? " [NC]" : "",
 
3604
                rc ? "matched" : "not-matched"));
 
3605
 
 
3606
    return rc;
 
3607
}
 
3608
 
 
3609
/* check for forced type and handler */
 
3610
static APR_INLINE void force_type_handler(rewriterule_entry *p,
 
3611
                                          rewrite_ctx *ctx)
 
3612
{
 
3613
    char *expanded;
 
3614
 
 
3615
    if (p->forced_mimetype) {
 
3616
        expanded = do_expand(p->forced_mimetype, ctx);
 
3617
 
 
3618
        if (*expanded) {
 
3619
            ap_str_tolower(expanded);
 
3620
 
 
3621
            rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have MIME-type "
 
3622
                        "'%s'", ctx->r->filename, expanded));
 
3623
 
 
3624
            apr_table_setn(ctx->r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
 
3625
                           expanded);
 
3626
        }
 
3627
    }
 
3628
 
 
3629
    if (p->forced_handler) {
 
3630
        expanded = do_expand(p->forced_handler, ctx);
 
3631
 
 
3632
        if (*expanded) {
 
3633
            ap_str_tolower(expanded);
 
3634
 
 
3635
            rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have "
 
3636
                        "Content-handler '%s'", ctx->r->filename, expanded));
 
3637
 
 
3638
            apr_table_setn(ctx->r->notes, REWRITE_FORCED_HANDLER_NOTEVAR,
 
3639
                           expanded);
 
3640
        }
 
3641
    }
 
3642
}
 
3643
 
 
3644
/*
 
3645
 * Apply a single RewriteRule
 
3646
 */
 
3647
static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx)
 
3648
{
 
3649
    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
 
3650
    apr_array_header_t *rewriteconds;
 
3651
    rewritecond_entry *conds;
 
3652
    int i, rc;
 
3653
    char *newuri = NULL;
 
3654
    request_rec *r = ctx->r;
 
3655
    int is_proxyreq = 0;
 
3656
 
 
3657
    ctx->uri = r->filename;
 
3658
 
 
3659
    if (ctx->perdir) {
 
3660
        apr_size_t dirlen = strlen(ctx->perdir);
 
3661
 
 
3662
        /*
 
3663
         * Proxy request?
 
3664
         */
 
3665
        is_proxyreq = (   r->proxyreq && r->filename
 
3666
                       && !strncmp(r->filename, "proxy:", 6));
 
3667
 
 
3668
        /* Since we want to match against the (so called) full URL, we have
 
3669
         * to re-add the PATH_INFO postfix
 
3670
         */
 
3671
        if (r->path_info && *r->path_info) {
 
3672
            rewritelog((r, 3, ctx->perdir, "add path info postfix: %s -> %s%s",
 
3673
                        ctx->uri, ctx->uri, r->path_info));
 
3674
            ctx->uri = apr_pstrcat(r->pool, ctx->uri, r->path_info, NULL);
 
3675
        }
 
3676
 
 
3677
        /* Additionally we strip the physical path from the url to match
 
3678
         * it independent from the underlaying filesystem.
 
3679
         */
 
3680
        if (!is_proxyreq && strlen(ctx->uri) >= dirlen &&
 
3681
            !strncmp(ctx->uri, ctx->perdir, dirlen)) {
 
3682
 
 
3683
            rewritelog((r, 3, ctx->perdir, "strip per-dir prefix: %s -> %s",
 
3684
                        ctx->uri, ctx->uri + dirlen));
 
3685
            ctx->uri = ctx->uri + dirlen;
 
3686
        }
 
3687
    }
 
3688
 
 
3689
    /* Try to match the URI against the RewriteRule pattern
 
3690
     * and exit immediately if it didn't apply.
 
3691
     */
 
3692
    rewritelog((r, 3, ctx->perdir, "applying pattern '%s' to uri '%s'",
 
3693
                p->pattern, ctx->uri));
 
3694
 
 
3695
    rc = !ap_regexec(p->regexp, ctx->uri, AP_MAX_REG_MATCH, regmatch, 0);
 
3696
    if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
 
3697
           (!rc &&  (p->flags & RULEFLAG_NOTMATCH))   ) ) {
 
3698
        return 0;
 
3699
    }
 
3700
 
 
3701
    /* It matched, wow! Now it's time to prepare the context structure for
 
3702
     * further processing
 
3703
     */
 
3704
    ctx->vary_this = NULL;
 
3705
    ctx->briRC.source = NULL;
 
3706
 
 
3707
    if (p->flags & RULEFLAG_NOTMATCH) {
 
3708
        ctx->briRR.source = NULL;
 
3709
    }
 
3710
    else {
 
3711
        ctx->briRR.source = apr_pstrdup(r->pool, ctx->uri);
 
3712
        ctx->briRR.nsub   = p->regexp->re_nsub;
 
3713
        memcpy(ctx->briRR.regmatch, regmatch, sizeof(regmatch));
 
3714
    }
 
3715
 
 
3716
    /* Ok, we already know the pattern has matched, but we now
 
3717
     * additionally have to check for all existing preconditions
 
3718
     * (RewriteCond) which have to be also true. We do this at
 
3719
     * this very late stage to avoid unnessesary checks which
 
3720
     * would slow down the rewriting engine.
 
3721
     */
 
3722
    rewriteconds = p->rewriteconds;
 
3723
    conds = (rewritecond_entry *)rewriteconds->elts;
 
3724
 
 
3725
    for (i = 0; i < rewriteconds->nelts; ++i) {
 
3726
        rewritecond_entry *c = &conds[i];
 
3727
 
 
3728
        rc = apply_rewrite_cond(c, ctx);
 
3729
        if (c->flags & CONDFLAG_ORNEXT) {
 
3730
            if (!rc) {
 
3731
                /* One condition is false, but another can be still true. */
 
3732
                ctx->vary_this = NULL;
 
3733
                continue;
 
3734
            }
 
3735
            else {
 
3736
                /* skip the rest of the chained OR conditions */
 
3737
                while (   i < rewriteconds->nelts
 
3738
                       && c->flags & CONDFLAG_ORNEXT) {
 
3739
                    c = &conds[++i];
 
3740
                }
 
3741
                continue;
 
3742
            }
 
3743
        }
 
3744
        else if (!rc) {
 
3745
            return 0;
 
3746
        }
 
3747
 
 
3748
        /* If some HTTP header was involved in the condition, remember it
 
3749
         * for later use
 
3750
         */
 
3751
        if (ctx->vary_this) {
 
3752
            ctx->vary = ctx->vary
 
3753
                        ? apr_pstrcat(r->pool, ctx->vary, ", ", ctx->vary_this,
 
3754
                                      NULL)
 
3755
                        : ctx->vary_this;
 
3756
            ctx->vary_this = NULL;
 
3757
        }
 
3758
    }
 
3759
 
 
3760
    /* expand the result */
 
3761
    if (!(p->flags & RULEFLAG_NOSUB)) {
 
3762
        newuri = do_expand(p->output, ctx);
 
3763
        rewritelog((r, 2, ctx->perdir, "rewrite '%s' -> '%s'", ctx->uri,
 
3764
                    newuri));
 
3765
    }
 
3766
 
 
3767
    /* expand [E=var:val] and [CO=<cookie>] */
 
3768
    do_expand_env(p->env, ctx);
 
3769
    do_expand_cookie(p->cookie, ctx);
 
3770
 
 
3771
    /* non-substitution rules ('RewriteRule <pat> -') end here. */
 
3772
    if (p->flags & RULEFLAG_NOSUB) {
 
3773
        force_type_handler(p, ctx);
 
3774
 
 
3775
        if (p->flags & RULEFLAG_STATUS) {
 
3776
            rewritelog((r, 2, ctx->perdir, "forcing responsecode %d for %s",
 
3777
                        p->forced_responsecode, r->filename));
 
3778
 
 
3779
            r->status = p->forced_responsecode;
 
3780
        }
 
3781
 
 
3782
        return 2;
 
3783
    }
 
3784
 
 
3785
    /* Now adjust API's knowledge about r->filename and r->args */
 
3786
    r->filename = newuri;
 
3787
    splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
 
3788
 
 
3789
    /* Add the previously stripped per-directory location prefix, unless
 
3790
     * (1) it's an absolute URL path and
 
3791
     * (2) it's a full qualified URL
 
3792
     */
 
3793
    if (   ctx->perdir && !is_proxyreq && *r->filename != '/'
 
3794
        && !is_absolute_uri(r->filename)) {
 
3795
        rewritelog((r, 3, ctx->perdir, "add per-dir prefix: %s -> %s%s",
 
3796
                    r->filename, ctx->perdir, r->filename));
 
3797
 
 
3798
        r->filename = apr_pstrcat(r->pool, ctx->perdir, r->filename, NULL);
 
3799
    }
 
3800
 
 
3801
    /* If this rule is forced for proxy throughput
 
3802
     * (`RewriteRule ... ... [P]') then emulate mod_proxy's
 
3803
     * URL-to-filename handler to be sure mod_proxy is triggered
 
3804
     * for this URL later in the Apache API. But make sure it is
 
3805
     * a fully-qualified URL. (If not it is qualified with
 
3806
     * ourself).
 
3807
     */
 
3808
    if (p->flags & RULEFLAG_PROXY) {
 
3809
        fully_qualify_uri(r);
 
3810
 
 
3811
        rewritelog((r, 2, ctx->perdir, "forcing proxy-throughput with %s",
 
3812
                    r->filename));
 
3813
 
 
3814
        r->filename = apr_pstrcat(r->pool, "proxy:", r->filename, NULL);
 
3815
        return 1;
 
3816
    }
 
3817
 
 
3818
    /* If this rule is explicitly forced for HTTP redirection
 
3819
     * (`RewriteRule .. .. [R]') then force an external HTTP
 
3820
     * redirect. But make sure it is a fully-qualified URL. (If
 
3821
     * not it is qualified with ourself).
 
3822
     */
 
3823
    if (p->flags & RULEFLAG_FORCEREDIRECT) {
 
3824
        fully_qualify_uri(r);
 
3825
 
 
3826
        rewritelog((r, 2, ctx->perdir, "explicitly forcing redirect with %s",
 
3827
                    r->filename));
 
3828
 
 
3829
        r->status = p->forced_responsecode;
 
3830
        return 1;
 
3831
    }
 
3832
 
 
3833
    /* Special Rewriting Feature: Self-Reduction
 
3834
     * We reduce the URL by stripping a possible
 
3835
     * http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which
 
3836
     * corresponds to ourself. This is to simplify rewrite maps
 
3837
     * and to avoid recursion, etc. When this prefix is not a
 
3838
     * coincidence then the user has to use [R] explicitly (see
 
3839
     * above).
 
3840
     */
 
3841
    reduce_uri(r);
 
3842
 
 
3843
    /* If this rule is still implicitly forced for HTTP
 
3844
     * redirection (`RewriteRule .. <scheme>://...') then
 
3845
     * directly force an external HTTP redirect.
 
3846
     */
 
3847
    if (is_absolute_uri(r->filename)) {
 
3848
        rewritelog((r, 2, ctx->perdir, "implicitly forcing redirect (rc=%d) "
 
3849
                    "with %s", p->forced_responsecode, r->filename));
 
3850
 
 
3851
        r->status = p->forced_responsecode;
 
3852
        return 1;
 
3853
    }
 
3854
 
 
3855
    /* Finally remember the forced mime-type */
 
3856
    force_type_handler(p, ctx);
 
3857
 
 
3858
    /* Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_)
 
3859
     * But now we're done for this particular rule.
 
3860
     */
 
3861
    return 1;
 
3862
}
 
3863
 
 
3864
/*
 
3865
 * Apply a complete rule set,
 
3866
 * i.e. a list of rewrite rules
 
3867
 */
 
3868
static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules,
 
3869
                              char *perdir)
 
3870
{
 
3871
    rewriterule_entry *entries;
 
3872
    rewriterule_entry *p;
 
3873
    int i;
 
3874
    int changed;
 
3875
    int rc;
 
3876
    int s;
 
3877
    rewrite_ctx *ctx;
 
3878
 
 
3879
    ctx = apr_palloc(r->pool, sizeof(*ctx));
 
3880
    ctx->perdir = perdir;
 
3881
    ctx->r = r;
 
3882
 
 
3883
    /*
 
3884
     *  Iterate over all existing rules
 
3885
     */
 
3886
    entries = (rewriterule_entry *)rewriterules->elts;
 
3887
    changed = 0;
 
3888
    loop:
 
3889
    for (i = 0; i < rewriterules->nelts; i++) {
 
3890
        p = &entries[i];
 
3891
 
 
3892
        /*
 
3893
         *  Ignore this rule on subrequests if we are explicitly
 
3894
         *  asked to do so or this is a proxy-throughput or a
 
3895
         *  forced redirect rule.
 
3896
         */
 
3897
        if (r->main != NULL &&
 
3898
            (p->flags & RULEFLAG_IGNOREONSUBREQ ||
 
3899
             p->flags & RULEFLAG_FORCEREDIRECT    )) {
 
3900
            continue;
 
3901
        }
 
3902
 
 
3903
        /*
 
3904
         *  Apply the current rule.
 
3905
         */
 
3906
        ctx->vary = NULL;
 
3907
        rc = apply_rewrite_rule(p, ctx);
 
3908
 
 
3909
        if (rc) {
 
3910
            /* Regardless of what we do next, we've found a match. Check to see
 
3911
             * if any of the request header fields were involved, and add them
 
3912
             * to the Vary field of the response.
 
3913
             */
 
3914
            if (ctx->vary) {
 
3915
                apr_table_merge(r->headers_out, "Vary", ctx->vary);
 
3916
            }
 
3917
 
 
3918
            /*
 
3919
             * The rule sets the response code (implies match-only)
 
3920
             */
 
3921
            if (p->flags & RULEFLAG_STATUS) {
 
3922
                return ACTION_STATUS;
 
3923
            }
 
3924
 
 
3925
            /*
 
3926
             * Indicate a change if this was not a match-only rule.
 
3927
             */
 
3928
            if (rc != 2) {
 
3929
                changed = ((p->flags & RULEFLAG_NOESCAPE)
 
3930
                           ? ACTION_NOESCAPE : ACTION_NORMAL);
 
3931
            }
 
3932
 
 
3933
            /*
 
3934
             *  Pass-Through Feature (`RewriteRule .. .. [PT]'):
 
3935
             *  Because the Apache 1.x API is very limited we
 
3936
             *  need this hack to pass the rewritten URL to other
 
3937
             *  modules like mod_alias, mod_userdir, etc.
 
3938
             */
 
3939
            if (p->flags & RULEFLAG_PASSTHROUGH) {
 
3940
                rewritelog((r, 2, perdir, "forcing '%s' to get passed through "
 
3941
                           "to next API URI-to-filename handler", r->filename));
 
3942
                r->filename = apr_pstrcat(r->pool, "passthrough:",
 
3943
                                         r->filename, NULL);
 
3944
                changed = ACTION_NORMAL;
 
3945
                break;
 
3946
            }
 
3947
 
 
3948
            /*
 
3949
             *  Stop processing also on proxy pass-through and
 
3950
             *  last-rule and new-round flags.
 
3951
             */
 
3952
            if (p->flags & (RULEFLAG_PROXY | RULEFLAG_LASTRULE)) {
 
3953
                break;
 
3954
            }
 
3955
 
 
3956
            /*
 
3957
             *  On "new-round" flag we just start from the top of
 
3958
             *  the rewriting ruleset again.
 
3959
             */
 
3960
            if (p->flags & RULEFLAG_NEWROUND) {
 
3961
                goto loop;
 
3962
            }
 
3963
 
 
3964
            /*
 
3965
             *  If we are forced to skip N next rules, do it now.
 
3966
             */
 
3967
            if (p->skip > 0) {
 
3968
                s = p->skip;
 
3969
                while (   i < rewriterules->nelts
 
3970
                       && s > 0) {
 
3971
                    i++;
 
3972
                    p = &entries[i];
 
3973
                    s--;
 
3974
                }
 
3975
            }
 
3976
        }
 
3977
        else {
 
3978
            /*
 
3979
             *  If current rule is chained with next rule(s),
 
3980
             *  skip all this next rule(s)
 
3981
             */
 
3982
            while (   i < rewriterules->nelts
 
3983
                   && p->flags & RULEFLAG_CHAIN) {
 
3984
                i++;
 
3985
                p = &entries[i];
 
3986
            }
 
3987
        }
 
3988
    }
 
3989
    return changed;
 
3990
}
 
3991
 
 
3992
 
 
3993
/*
 
3994
 * +-------------------------------------------------------+
 
3995
 * |                                                       |
 
3996
 * |             Module Initialization Hooks
 
3997
 * |                                                       |
 
3998
 * +-------------------------------------------------------+
 
3999
 */
 
4000
 
 
4001
static int pre_config(apr_pool_t *pconf,
 
4002
                      apr_pool_t *plog,
 
4003
                      apr_pool_t *ptemp)
 
4004
{
 
4005
    APR_OPTIONAL_FN_TYPE(ap_register_rewrite_mapfunc) *map_pfn_register;
 
4006
 
 
4007
    /* register int: rewritemap handlers */
 
4008
    mapfunc_hash = apr_hash_make(pconf);
 
4009
    map_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_rewrite_mapfunc);
 
4010
    if (map_pfn_register) {
 
4011
        map_pfn_register("tolower", rewrite_mapfunc_tolower);
 
4012
        map_pfn_register("toupper", rewrite_mapfunc_toupper);
 
4013
        map_pfn_register("escape", rewrite_mapfunc_escape);
 
4014
        map_pfn_register("unescape", rewrite_mapfunc_unescape);
 
4015
    }
 
4016
    return OK;
 
4017
}
 
4018
 
 
4019
static int post_config(apr_pool_t *p,
 
4020
                       apr_pool_t *plog,
 
4021
                       apr_pool_t *ptemp,
 
4022
                       server_rec *s)
 
4023
{
 
4024
    apr_status_t rv;
 
4025
    void *data;
 
4026
    int first_time = 0;
 
4027
    const char *userdata_key = "rewrite_init_module";
 
4028
 
 
4029
    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
 
4030
    if (!data) {
 
4031
        first_time = 1;
 
4032
        apr_pool_userdata_set((const void *)1, userdata_key,
 
4033
                              apr_pool_cleanup_null, s->process->pool);
 
4034
    }
 
4035
 
 
4036
    /* check if proxy module is available */
 
4037
    proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL);
 
4038
 
 
4039
#ifndef REWRITELOG_DISABLED
 
4040
    /* create the rewriting lockfiles in the parent */
 
4041
    if ((rv = apr_global_mutex_create(&rewrite_log_lock, NULL,
 
4042
                                      APR_LOCK_DEFAULT, p)) != APR_SUCCESS) {
 
4043
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
 
4044
                     "mod_rewrite: could not create rewrite_log_lock");
 
4045
        return HTTP_INTERNAL_SERVER_ERROR;
 
4046
    }
 
4047
 
 
4048
#ifdef AP_NEED_SET_MUTEX_PERMS
 
4049
    rv = unixd_set_global_mutex_perms(rewrite_log_lock);
 
4050
    if (rv != APR_SUCCESS) {
 
4051
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
 
4052
                     "mod_rewrite: Could not set permissions on "
 
4053
                     "rewrite_log_lock; check User and Group directives");
 
4054
        return HTTP_INTERNAL_SERVER_ERROR;
 
4055
    }
 
4056
#endif /* perms */
 
4057
#endif /* rewritelog */
 
4058
 
 
4059
    rv = rewritelock_create(s, p);
 
4060
    if (rv != APR_SUCCESS) {
 
4061
        return HTTP_INTERNAL_SERVER_ERROR;
 
4062
    }
 
4063
 
 
4064
    apr_pool_cleanup_register(p, (void *)s, rewritelock_remove,
 
4065
                              apr_pool_cleanup_null);
 
4066
 
 
4067
    /* step through the servers and
 
4068
     * - open each rewriting logfile
 
4069
     * - open the RewriteMap prg:xxx programs
 
4070
     */
 
4071
    for (; s; s = s->next) {
 
4072
#ifndef REWRITELOG_DISABLED
 
4073
        if (!open_rewritelog(s, p)) {
 
4074
            return HTTP_INTERNAL_SERVER_ERROR;
 
4075
        }
 
4076
#endif
 
4077
 
 
4078
        if (!first_time) {
 
4079
            if (run_rewritemap_programs(s, p) != APR_SUCCESS) {
 
4080
                return HTTP_INTERNAL_SERVER_ERROR;
 
4081
            }
 
4082
        }
 
4083
    }
 
4084
 
 
4085
    rewrite_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
 
4086
    rewrite_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
 
4087
 
 
4088
    return OK;
 
4089
}
 
4090
 
 
4091
static void init_child(apr_pool_t *p, server_rec *s)
 
4092
{
 
4093
    apr_status_t rv = 0; /* get a rid of gcc warning (REWRITELOG_DISABLED) */
 
4094
 
 
4095
    if (lockname != NULL && *(lockname) != '\0') {
 
4096
        rv = apr_global_mutex_child_init(&rewrite_mapr_lock_acquire,
 
4097
                                         lockname, p);
 
4098
        if (rv != APR_SUCCESS) {
 
4099
            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
 
4100
                         "mod_rewrite: could not init rewrite_mapr_lock_acquire"
 
4101
                         " in child");
 
4102
        }
 
4103
    }
 
4104
 
 
4105
#ifndef REWRITELOG_DISABLED
 
4106
    rv = apr_global_mutex_child_init(&rewrite_log_lock, NULL, p);
 
4107
    if (rv != APR_SUCCESS) {
 
4108
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
 
4109
                     "mod_rewrite: could not init rewrite log lock in child");
 
4110
    }
 
4111
#endif
 
4112
 
 
4113
    /* create the lookup cache */
 
4114
    if (!init_cache(p)) {
 
4115
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
 
4116
                     "mod_rewrite: could not init map cache in child");
 
4117
    }
 
4118
}
 
4119
 
 
4120
 
 
4121
/*
 
4122
 * +-------------------------------------------------------+
 
4123
 * |                                                       |
 
4124
 * |                     runtime hooks
 
4125
 * |                                                       |
 
4126
 * +-------------------------------------------------------+
 
4127
 */
 
4128
 
 
4129
/*
 
4130
 * URI-to-filename hook
 
4131
 * [deals with RewriteRules in server context]
 
4132
 */
 
4133
static int hook_uri2file(request_rec *r)
 
4134
{
 
4135
    rewrite_server_conf *conf;
 
4136
    const char *saved_rulestatus;
 
4137
    const char *var;
 
4138
    const char *thisserver;
 
4139
    char *thisport;
 
4140
    const char *thisurl;
 
4141
    unsigned int port;
 
4142
    int rulestatus;
 
4143
 
 
4144
    /*
 
4145
     *  retrieve the config structures
 
4146
     */
 
4147
    conf = ap_get_module_config(r->server->module_config, &rewrite_module);
 
4148
 
 
4149
    /*
 
4150
     *  only do something under runtime if the engine is really enabled,
 
4151
     *  else return immediately!
 
4152
     */
 
4153
    if (conf->state == ENGINE_DISABLED) {
 
4154
        return DECLINED;
 
4155
    }
 
4156
 
 
4157
    /*
 
4158
     *  check for the ugly API case of a virtual host section where no
 
4159
     *  mod_rewrite directives exists. In this situation we became no chance
 
4160
     *  by the API to setup our default per-server config so we have to
 
4161
     *  on-the-fly assume we have the default config. But because the default
 
4162
     *  config has a disabled rewriting engine we are lucky because can
 
4163
     *  just stop operating now.
 
4164
     */
 
4165
    if (conf->server != r->server) {
 
4166
        return DECLINED;
 
4167
    }
 
4168
 
 
4169
    /*
 
4170
     *  add the SCRIPT_URL variable to the env. this is a bit complicated
 
4171
     *  due to the fact that apache uses subrequests and internal redirects
 
4172
     */
 
4173
 
 
4174
    if (r->main == NULL) {
 
4175
         var = apr_table_get(r->subprocess_env, REDIRECT_ENVVAR_SCRIPT_URL);
 
4176
         if (var == NULL) {
 
4177
             apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
 
4178
         }
 
4179
         else {
 
4180
             apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
 
4181
         }
 
4182
    }
 
4183
    else {
 
4184
         var = apr_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
 
4185
         apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
 
4186
    }
 
4187
 
 
4188
    /*
 
4189
     *  create the SCRIPT_URI variable for the env
 
4190
     */
 
4191
 
 
4192
    /* add the canonical URI of this URL */
 
4193
    thisserver = ap_get_server_name(r);
 
4194
    port = ap_get_server_port(r);
 
4195
    if (ap_is_default_port(port, r)) {
 
4196
        thisport = "";
 
4197
    }
 
4198
    else {
 
4199
        thisport = apr_psprintf(r->pool, ":%u", port);
 
4200
    }
 
4201
    thisurl = apr_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL);
 
4202
 
 
4203
    /* set the variable */
 
4204
    var = apr_pstrcat(r->pool, ap_http_scheme(r), "://", thisserver, thisport,
 
4205
                      thisurl, NULL);
 
4206
    apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
 
4207
 
 
4208
    if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) {
 
4209
        /* if filename was not initially set,
 
4210
         * we start with the requested URI
 
4211
         */
 
4212
        if (r->filename == NULL) {
 
4213
            r->filename = apr_pstrdup(r->pool, r->uri);
 
4214
            rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s",
 
4215
                        r->filename));
 
4216
        }
 
4217
        else {
 
4218
            rewritelog((r, 2, NULL, "init rewrite engine with passed filename "
 
4219
                        "%s. Original uri = %s", r->filename, r->uri));
 
4220
        }
 
4221
 
 
4222
        /*
 
4223
         *  now apply the rules ...
 
4224
         */
 
4225
        rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL);
 
4226
        apr_table_set(r->notes,"mod_rewrite_rewritten",
 
4227
                      apr_psprintf(r->pool,"%d",rulestatus));
 
4228
    }
 
4229
    else {
 
4230
        rewritelog((r, 2, NULL, "uri already rewritten. Status %s, Uri %s, "
 
4231
                    "r->filename %s", saved_rulestatus, r->uri, r->filename));
 
4232
 
 
4233
        rulestatus = atoi(saved_rulestatus);
 
4234
    }
 
4235
 
 
4236
    if (rulestatus) {
 
4237
        unsigned skip;
 
4238
        apr_size_t flen;
 
4239
 
 
4240
        if (ACTION_STATUS == rulestatus) {
 
4241
            int n = r->status;
 
4242
 
 
4243
            r->status = HTTP_OK;
 
4244
            return n;
 
4245
        }
 
4246
 
 
4247
        flen = r->filename ? strlen(r->filename) : 0;
 
4248
        if (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0) {
 
4249
            /* it should be go on as an internal proxy request */
 
4250
 
 
4251
            /* check if the proxy module is enabled, so
 
4252
             * we can actually use it!
 
4253
             */
 
4254
            if (!proxy_available) {
 
4255
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
4256
                              "attempt to make remote request from mod_rewrite "
 
4257
                              "without proxy enabled: %s", r->filename);
 
4258
                return HTTP_FORBIDDEN;
 
4259
            }
 
4260
 
 
4261
            /* make sure the QUERY_STRING and
 
4262
             * PATH_INFO parts get incorporated
 
4263
             */
 
4264
            if (r->path_info != NULL) {
 
4265
                r->filename = apr_pstrcat(r->pool, r->filename,
 
4266
                                          r->path_info, NULL);
 
4267
            }
 
4268
            if (r->args != NULL &&
 
4269
                r->uri == r->unparsed_uri) {
 
4270
                /* see proxy_http:proxy_http_canon() */
 
4271
                r->filename = apr_pstrcat(r->pool, r->filename,
 
4272
                                          "?", r->args, NULL);
 
4273
            }
 
4274
 
 
4275
            /* now make sure the request gets handled by the proxy handler */
 
4276
            if (PROXYREQ_NONE == r->proxyreq) {
 
4277
                r->proxyreq = PROXYREQ_REVERSE;
 
4278
            }
 
4279
            r->handler  = "proxy-server";
 
4280
 
 
4281
            rewritelog((r, 1, NULL, "go-ahead with proxy request %s [OK]",
 
4282
                        r->filename));
 
4283
            return OK;
 
4284
        }
 
4285
        else if ((skip = is_absolute_uri(r->filename)) > 0) {
 
4286
            int n;
 
4287
 
 
4288
            /* it was finally rewritten to a remote URL */
 
4289
 
 
4290
            if (rulestatus != ACTION_NOESCAPE) {
 
4291
                rewritelog((r, 1, NULL, "escaping %s for redirect",
 
4292
                            r->filename));
 
4293
                r->filename = escape_absolute_uri(r->pool, r->filename, skip);
 
4294
            }
 
4295
 
 
4296
            /* append the QUERY_STRING part */
 
4297
            if (r->args) {
 
4298
                r->filename = apr_pstrcat(r->pool, r->filename, "?",
 
4299
                                          (rulestatus == ACTION_NOESCAPE)
 
4300
                                            ? r->args
 
4301
                                            : ap_escape_uri(r->pool, r->args),
 
4302
                                          NULL);
 
4303
            }
 
4304
 
 
4305
            /* determine HTTP redirect response code */
 
4306
            if (ap_is_HTTP_REDIRECT(r->status)) {
 
4307
                n = r->status;
 
4308
                r->status = HTTP_OK; /* make Apache kernel happy */
 
4309
            }
 
4310
            else {
 
4311
                n = HTTP_MOVED_TEMPORARILY;
 
4312
            }
 
4313
 
 
4314
            /* now do the redirection */
 
4315
            apr_table_setn(r->headers_out, "Location", r->filename);
 
4316
            rewritelog((r, 1, NULL, "redirect to %s [REDIRECT/%d]", r->filename,
 
4317
                        n));
 
4318
 
 
4319
            return n;
 
4320
        }
 
4321
        else if (flen > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
 
4322
            /*
 
4323
             * Hack because of underpowered API: passing the current
 
4324
             * rewritten filename through to other URL-to-filename handlers
 
4325
             * just as it were the requested URL. This is to enable
 
4326
             * post-processing by mod_alias, etc.  which always act on
 
4327
             * r->uri! The difference here is: We do not try to
 
4328
             * add the document root
 
4329
             */
 
4330
            r->uri = apr_pstrdup(r->pool, r->filename+12);
 
4331
            return DECLINED;
 
4332
        }
 
4333
        else {
 
4334
            /* it was finally rewritten to a local path */
 
4335
 
 
4336
            /* expand "/~user" prefix */
 
4337
#if APR_HAS_USER
 
4338
            r->filename = expand_tildepaths(r, r->filename);
 
4339
#endif
 
4340
            rewritelog((r, 2, NULL, "local path result: %s", r->filename));
 
4341
 
 
4342
            /* the filename must be either an absolute local path or an
 
4343
             * absolute local URL.
 
4344
             */
 
4345
            if (   *r->filename != '/'
 
4346
                && !ap_os_is_path_absolute(r->pool, r->filename)) {
 
4347
                return HTTP_BAD_REQUEST;
 
4348
            }
 
4349
 
 
4350
            /* if there is no valid prefix, we call
 
4351
             * the translator from the core and
 
4352
             * prefix the filename with document_root
 
4353
             *
 
4354
             * NOTICE:
 
4355
             * We cannot leave out the prefix_stat because
 
4356
             * - when we always prefix with document_root
 
4357
             *   then no absolute path can be created, e.g. via
 
4358
             *   emulating a ScriptAlias directive, etc.
 
4359
             * - when we always NOT prefix with document_root
 
4360
             *   then the files under document_root have to
 
4361
             *   be references directly and document_root
 
4362
             *   gets never used and will be a dummy parameter -
 
4363
             *   this is also bad
 
4364
             *
 
4365
             * BUT:
 
4366
             * Under real Unix systems this is no problem,
 
4367
             * because we only do stat() on the first directory
 
4368
             * and this gets cached by the kernel for along time!
 
4369
             */
 
4370
            if (!prefix_stat(r->filename, r->pool)) {
 
4371
                int res;
 
4372
                char *tmp = r->uri;
 
4373
 
 
4374
                r->uri = r->filename;
 
4375
                res = ap_core_translate(r);
 
4376
                r->uri = tmp;
 
4377
 
 
4378
                if (res != OK) {
 
4379
                    rewritelog((r, 1, NULL, "prefixing with document_root of %s"
 
4380
                                " FAILED", r->filename));
 
4381
 
 
4382
                    return res;
 
4383
                }
 
4384
 
 
4385
                rewritelog((r, 2, NULL, "prefixed with document_root to %s",
 
4386
                            r->filename));
 
4387
            }
 
4388
 
 
4389
            rewritelog((r, 1, NULL, "go-ahead with %s [OK]", r->filename));
 
4390
            return OK;
 
4391
        }
 
4392
    }
 
4393
    else {
 
4394
        rewritelog((r, 1, NULL, "pass through %s", r->filename));
 
4395
        return DECLINED;
 
4396
    }
 
4397
}
 
4398
 
 
4399
/*
 
4400
 * Fixup hook
 
4401
 * [RewriteRules in directory context]
 
4402
 */
 
4403
static int hook_fixup(request_rec *r)
 
4404
{
 
4405
    rewrite_perdir_conf *dconf;
 
4406
    char *cp;
 
4407
    char *cp2;
 
4408
    const char *ccp;
 
4409
    apr_size_t l;
 
4410
    int rulestatus;
 
4411
    int n;
 
4412
    char *ofilename;
 
4413
    int is_proxyreq;
 
4414
 
 
4415
    dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config,
 
4416
                                                        &rewrite_module);
 
4417
 
 
4418
    /* if there is no per-dir config we return immediately */
 
4419
    if (dconf == NULL) {
 
4420
        return DECLINED;
 
4421
    }
 
4422
 
 
4423
    /* if there are no real (i.e. no RewriteRule directives!)
 
4424
       per-dir config of us, we return also immediately */
 
4425
    if (dconf->directory == NULL) {
 
4426
        return DECLINED;
 
4427
    }
 
4428
 
 
4429
    /*
 
4430
     * Proxy request?
 
4431
     */
 
4432
    is_proxyreq = (   r->proxyreq && r->filename
 
4433
                   && !strncmp(r->filename, "proxy:", 6));
 
4434
 
 
4435
    /*
 
4436
     *  .htaccess file is called before really entering the directory, i.e.:
 
4437
     *  URL: http://localhost/foo  and .htaccess is located in foo directory
 
4438
     *  Ignore such attempts, since they may lead to undefined behaviour.
 
4439
     */
 
4440
    if (!is_proxyreq) {
 
4441
        l = strlen(dconf->directory) - 1;
 
4442
        if (r->filename && strlen(r->filename) == l &&
 
4443
            (dconf->directory)[l] == '/' &&
 
4444
            !strncmp(r->filename, dconf->directory, l)) {
 
4445
            return DECLINED;
 
4446
        }
 
4447
    }
 
4448
 
 
4449
    /*
 
4450
     *  only do something under runtime if the engine is really enabled,
 
4451
     *  for this directory, else return immediately!
 
4452
     */
 
4453
    if (dconf->state == ENGINE_DISABLED) {
 
4454
        return DECLINED;
 
4455
    }
 
4456
 
 
4457
    /*
 
4458
     *  Do the Options check after engine check, so
 
4459
     *  the user is able to explicitely turn RewriteEngine Off.
 
4460
     */
 
4461
    if (!(ap_allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) {
 
4462
        /* FollowSymLinks is mandatory! */
 
4463
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
4464
                     "Options FollowSymLinks or SymLinksIfOwnerMatch is off "
 
4465
                     "which implies that RewriteRule directive is forbidden: "
 
4466
                     "%s", r->filename);
 
4467
        return HTTP_FORBIDDEN;
 
4468
    }
 
4469
 
 
4470
    /*
 
4471
     *  remember the current filename before rewriting for later check
 
4472
     *  to prevent deadlooping because of internal redirects
 
4473
     *  on final URL/filename which can be equal to the inital one.
 
4474
     *  also, we'll restore original r->filename if we decline this
 
4475
     *  request
 
4476
     */
 
4477
    ofilename = r->filename;
 
4478
 
 
4479
    if (r->filename == NULL) {
 
4480
        r->filename = apr_pstrdup(r->pool, r->uri);
 
4481
        rewritelog((r, 2, "init rewrite engine with requested uri %s",
 
4482
                    r->filename));
 
4483
    }
 
4484
 
 
4485
    /*
 
4486
     *  now apply the rules ...
 
4487
     */
 
4488
    rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory);
 
4489
    if (rulestatus) {
 
4490
        unsigned skip;
 
4491
 
 
4492
        if (ACTION_STATUS == rulestatus) {
 
4493
            int n = r->status;
 
4494
 
 
4495
            r->status = HTTP_OK;
 
4496
            return n;
 
4497
        }
 
4498
 
 
4499
        l = strlen(r->filename);
 
4500
        if (l > 6 && strncmp(r->filename, "proxy:", 6) == 0) {
 
4501
            /* it should go on as an internal proxy request */
 
4502
 
 
4503
            /* make sure the QUERY_STRING and
 
4504
             * PATH_INFO parts get incorporated
 
4505
             * (r->path_info was already appended by the
 
4506
             * rewriting engine because of the per-dir context!)
 
4507
             */
 
4508
            if (r->args != NULL) {
 
4509
                r->filename = apr_pstrcat(r->pool, r->filename,
 
4510
                                          "?", r->args, NULL);
 
4511
            }
 
4512
 
 
4513
            /* now make sure the request gets handled by the proxy handler */
 
4514
            if (PROXYREQ_NONE == r->proxyreq) {
 
4515
                r->proxyreq = PROXYREQ_REVERSE;
 
4516
            }
 
4517
            r->handler  = "proxy-server";
 
4518
 
 
4519
            rewritelog((r, 1, dconf->directory, "go-ahead with proxy request "
 
4520
                        "%s [OK]", r->filename));
 
4521
            return OK;
 
4522
        }
 
4523
        else if ((skip = is_absolute_uri(r->filename)) > 0) {
 
4524
            /* it was finally rewritten to a remote URL */
 
4525
 
 
4526
            /* because we are in a per-dir context
 
4527
             * first try to replace the directory with its base-URL
 
4528
             * if there is a base-URL available
 
4529
             */
 
4530
            if (dconf->baseurl != NULL) {
 
4531
                /* skip 'scheme://' */
 
4532
                cp = r->filename + skip;
 
4533
 
 
4534
                if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) {
 
4535
                    rewritelog((r, 2, dconf->directory,
 
4536
                                "trying to replace prefix %s with %s",
 
4537
                                dconf->directory, dconf->baseurl));
 
4538
 
 
4539
                    /* I think, that hack needs an explanation:
 
4540
                     * well, here is it:
 
4541
                     * mod_rewrite was written for unix systems, were
 
4542
                     * absolute file-system paths start with a slash.
 
4543
                     * URL-paths _also_ start with slashes, so they
 
4544
                     * can be easily compared with system paths.
 
4545
                     *
 
4546
                     * the following assumes, that the actual url-path
 
4547
                     * may be prefixed by the current directory path and
 
4548
                     * tries to replace the system path with the RewriteBase
 
4549
                     * URL.
 
4550
                     * That assumption is true if we use a RewriteRule like
 
4551
                     *
 
4552
                     * RewriteRule ^foo bar [R]
 
4553
                     *
 
4554
                     * (see apply_rewrite_rule function)
 
4555
                     * However on systems that don't have a / as system
 
4556
                     * root this will never match, so we skip the / after the
 
4557
                     * hostname and compare/substitute only the stuff after it.
 
4558
                     *
 
4559
                     * (note that cp was already increased to the right value)
 
4560
                     */
 
4561
                    cp2 = subst_prefix_path(r, cp, (*dconf->directory == '/')
 
4562
                                                   ? dconf->directory + 1
 
4563
                                                   : dconf->directory,
 
4564
                                            dconf->baseurl + 1);
 
4565
                    if (strcmp(cp2, cp) != 0) {
 
4566
                        *cp = '\0';
 
4567
                        r->filename = apr_pstrcat(r->pool, r->filename,
 
4568
                                                  cp2, NULL);
 
4569
                    }
 
4570
                }
 
4571
            }
 
4572
 
 
4573
            /* now prepare the redirect... */
 
4574
            if (rulestatus != ACTION_NOESCAPE) {
 
4575
                rewritelog((r, 1, dconf->directory, "escaping %s for redirect",
 
4576
                            r->filename));
 
4577
                r->filename = escape_absolute_uri(r->pool, r->filename, skip);
 
4578
            }
 
4579
 
 
4580
            /* append the QUERY_STRING part */
 
4581
            if (r->args) {
 
4582
                r->filename = apr_pstrcat(r->pool, r->filename, "?",
 
4583
                                          (rulestatus == ACTION_NOESCAPE)
 
4584
                                            ? r->args
 
4585
                                            : ap_escape_uri(r->pool, r->args),
 
4586
                                          NULL);
 
4587
            }
 
4588
 
 
4589
            /* determine HTTP redirect response code */
 
4590
            if (ap_is_HTTP_REDIRECT(r->status)) {
 
4591
                n = r->status;
 
4592
                r->status = HTTP_OK; /* make Apache kernel happy */
 
4593
            }
 
4594
            else {
 
4595
                n = HTTP_MOVED_TEMPORARILY;
 
4596
            }
 
4597
 
 
4598
            /* now do the redirection */
 
4599
            apr_table_setn(r->headers_out, "Location", r->filename);
 
4600
            rewritelog((r, 1, dconf->directory, "redirect to %s [REDIRECT/%d]",
 
4601
                        r->filename, n));
 
4602
            return n;
 
4603
        }
 
4604
        else {
 
4605
            /* it was finally rewritten to a local path */
 
4606
 
 
4607
            /* if someone used the PASSTHROUGH flag in per-dir
 
4608
             * context we just ignore it. It is only useful
 
4609
             * in per-server context
 
4610
             */
 
4611
            if (l > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
 
4612
                r->filename = apr_pstrdup(r->pool, r->filename+12);
 
4613
            }
 
4614
 
 
4615
            /* the filename must be either an absolute local path or an
 
4616
             * absolute local URL.
 
4617
             */
 
4618
            if (   *r->filename != '/'
 
4619
                && !ap_os_is_path_absolute(r->pool, r->filename)) {
 
4620
                return HTTP_BAD_REQUEST;
 
4621
            }
 
4622
 
 
4623
            /* Check for deadlooping:
 
4624
             * At this point we KNOW that at least one rewriting
 
4625
             * rule was applied, but when the resulting URL is
 
4626
             * the same as the initial URL, we are not allowed to
 
4627
             * use the following internal redirection stuff because
 
4628
             * this would lead to a deadloop.
 
4629
             */
 
4630
            if (ofilename != NULL && strcmp(r->filename, ofilename) == 0) {
 
4631
                rewritelog((r, 1, dconf->directory, "initial URL equal rewritten"
 
4632
                            " URL: %s [IGNORING REWRITE]", r->filename));
 
4633
                return OK;
 
4634
            }
 
4635
 
 
4636
            /* if there is a valid base-URL then substitute
 
4637
             * the per-dir prefix with this base-URL if the
 
4638
             * current filename still is inside this per-dir
 
4639
             * context. If not then treat the result as a
 
4640
             * plain URL
 
4641
             */
 
4642
            if (dconf->baseurl != NULL) {
 
4643
                rewritelog((r, 2, dconf->directory, "trying to replace prefix "
 
4644
                            "%s with %s", dconf->directory, dconf->baseurl));
 
4645
 
 
4646
                r->filename = subst_prefix_path(r, r->filename,
 
4647
                                                dconf->directory,
 
4648
                                                dconf->baseurl);
 
4649
            }
 
4650
            else {
 
4651
                /* if no explicit base-URL exists we assume
 
4652
                 * that the directory prefix is also a valid URL
 
4653
                 * for this webserver and only try to remove the
 
4654
                 * document_root if it is prefix
 
4655
                 */
 
4656
                if ((ccp = ap_document_root(r)) != NULL) {
 
4657
                    /* strip trailing slash */
 
4658
                    l = strlen(ccp);
 
4659
                    if (ccp[l-1] == '/') {
 
4660
                        --l;
 
4661
                    }
 
4662
                    if (!strncmp(r->filename, ccp, l) &&
 
4663
                        r->filename[l] == '/') {
 
4664
                        rewritelog((r, 2,dconf->directory, "strip document_root"
 
4665
                                    " prefix: %s -> %s", r->filename,
 
4666
                                    r->filename+l));
 
4667
 
 
4668
                        r->filename = apr_pstrdup(r->pool, r->filename+l);
 
4669
                    }
 
4670
                }
 
4671
            }
 
4672
 
 
4673
            /* now initiate the internal redirect */
 
4674
            rewritelog((r, 1, dconf->directory, "internal redirect with %s "
 
4675
                        "[INTERNAL REDIRECT]", r->filename));
 
4676
            r->filename = apr_pstrcat(r->pool, "redirect:", r->filename, NULL);
 
4677
            r->handler = "redirect-handler";
 
4678
            return OK;
 
4679
        }
 
4680
    }
 
4681
    else {
 
4682
        rewritelog((r, 1, dconf->directory, "pass through %s", r->filename));
 
4683
        r->filename = ofilename;
 
4684
        return DECLINED;
 
4685
    }
 
4686
}
 
4687
 
 
4688
/*
 
4689
 * MIME-type hook
 
4690
 * [T=...,H=...] execution
 
4691
 */
 
4692
static int hook_mimetype(request_rec *r)
 
4693
{
 
4694
    const char *t;
 
4695
 
 
4696
    /* type */
 
4697
    t = apr_table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR);
 
4698
    if (t && *t) {
 
4699
        rewritelog((r, 1, NULL, "force filename %s to have MIME-type '%s'",
 
4700
                    r->filename, t));
 
4701
 
 
4702
        ap_set_content_type(r, t);
 
4703
    }
 
4704
 
 
4705
    /* handler */
 
4706
    t = apr_table_get(r->notes, REWRITE_FORCED_HANDLER_NOTEVAR);
 
4707
    if (t && *t) {
 
4708
        rewritelog((r, 1, NULL, "force filename %s to have the "
 
4709
                    "Content-handler '%s'", r->filename, t));
 
4710
 
 
4711
        r->handler = t;
 
4712
    }
 
4713
 
 
4714
    return OK;
 
4715
}
 
4716
 
 
4717
 
 
4718
/*
 
4719
 * "content" handler for internal redirects
 
4720
 */
 
4721
static int handler_redirect(request_rec *r)
 
4722
{
 
4723
    if (strcmp(r->handler, "redirect-handler")) {
 
4724
        return DECLINED;
 
4725
    }
 
4726
 
 
4727
    /* just make sure that we are really meant! */
 
4728
    if (strncmp(r->filename, "redirect:", 9) != 0) {
 
4729
        return DECLINED;
 
4730
    }
 
4731
 
 
4732
    /* now do the internal redirect */
 
4733
    ap_internal_redirect(apr_pstrcat(r->pool, r->filename+9,
 
4734
                                     r->args ? "?" : NULL, r->args, NULL), r);
 
4735
 
 
4736
    /* and return gracefully */
 
4737
    return OK;
 
4738
}
 
4739
 
 
4740
 
 
4741
/*
 
4742
 * +-------------------------------------------------------+
 
4743
 * |                                                       |
 
4744
 * |                Module paraphernalia
 
4745
 * |                                                       |
 
4746
 * +-------------------------------------------------------+
 
4747
 */
 
4748
 
 
4749
#ifdef REWRITELOG_DISABLED
 
4750
static const char *fake_rewritelog(cmd_parms *cmd, void *dummy, const char *a1)
 
4751
{
 
4752
    return "RewriteLog and RewriteLogLevel are not supported by this build "
 
4753
           "of mod_rewrite because it was compiled using the "
 
4754
           "-DREWRITELOG_DISABLED compiler option. You have to recompile "
 
4755
           "mod_rewrite WITHOUT this option in order to use the rewrite log.";
 
4756
}
 
4757
#endif
 
4758
 
 
4759
static const command_rec command_table[] = {
 
4760
    AP_INIT_FLAG(    "RewriteEngine",   cmd_rewriteengine,  NULL, OR_FILEINFO,
 
4761
                     "On or Off to enable or disable (default) the whole "
 
4762
                     "rewriting engine"),
 
4763
    AP_INIT_ITERATE( "RewriteOptions",  cmd_rewriteoptions,  NULL, OR_FILEINFO,
 
4764
                     "List of option strings to set"),
 
4765
    AP_INIT_TAKE1(   "RewriteBase",     cmd_rewritebase,     NULL, OR_FILEINFO,
 
4766
                     "the base URL of the per-directory context"),
 
4767
    AP_INIT_RAW_ARGS("RewriteCond",     cmd_rewritecond,     NULL, OR_FILEINFO,
 
4768
                     "an input string and a to be applied regexp-pattern"),
 
4769
    AP_INIT_RAW_ARGS("RewriteRule",     cmd_rewriterule,     NULL, OR_FILEINFO,
 
4770
                     "an URL-applied regexp-pattern and a substitution URL"),
 
4771
    AP_INIT_TAKE2(   "RewriteMap",      cmd_rewritemap,      NULL, RSRC_CONF,
 
4772
                     "a mapname and a filename"),
 
4773
    AP_INIT_TAKE1(   "RewriteLock",     cmd_rewritelock,     NULL, RSRC_CONF,
 
4774
                     "the filename of a lockfile used for inter-process "
 
4775
                     "synchronization"),
 
4776
#ifndef REWRITELOG_DISABLED
 
4777
    AP_INIT_TAKE1(   "RewriteLog",      cmd_rewritelog,      NULL, RSRC_CONF,
 
4778
                     "the filename of the rewriting logfile"),
 
4779
    AP_INIT_TAKE1(   "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF,
 
4780
                     "the level of the rewriting logfile verbosity "
 
4781
                     "(0=none, 1=std, .., 9=max)"),
 
4782
#else
 
4783
    AP_INIT_TAKE1(   "RewriteLog", fake_rewritelog, NULL, RSRC_CONF,
 
4784
                     "[DISABLED] the filename of the rewriting logfile"),
 
4785
    AP_INIT_TAKE1(   "RewriteLogLevel", fake_rewritelog, NULL, RSRC_CONF,
 
4786
                     "[DISABLED] the level of the rewriting logfile verbosity"),
 
4787
#endif
 
4788
    { NULL }
 
4789
};
 
4790
 
 
4791
static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func)
 
4792
{
 
4793
    apr_hash_set(mapfunc_hash, name, strlen(name), (const void *)func);
 
4794
}
 
4795
 
 
4796
static void register_hooks(apr_pool_t *p)
 
4797
{
 
4798
    /* fixup after mod_proxy, so that the proxied url will not
 
4799
     * escaped accidentally by mod_proxy's fixup.
 
4800
     */
 
4801
    static const char * const aszPre[]={ "mod_proxy.c", NULL };
 
4802
 
 
4803
    APR_REGISTER_OPTIONAL_FN(ap_register_rewrite_mapfunc);
 
4804
 
 
4805
    ap_hook_handler(handler_redirect, NULL, NULL, APR_HOOK_MIDDLE);
 
4806
    ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
 
4807
    ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE);
 
4808
    ap_hook_child_init(init_child, NULL, NULL, APR_HOOK_MIDDLE);
 
4809
 
 
4810
    ap_hook_fixups(hook_fixup, aszPre, NULL, APR_HOOK_FIRST);
 
4811
    ap_hook_fixups(hook_mimetype, NULL, NULL, APR_HOOK_LAST);
 
4812
    ap_hook_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST);
 
4813
}
 
4814
 
 
4815
    /* the main config structure */
 
4816
module AP_MODULE_DECLARE_DATA rewrite_module = {
 
4817
   STANDARD20_MODULE_STUFF,
 
4818
   config_perdir_create,        /* create per-dir    config structures */
 
4819
   config_perdir_merge,         /* merge  per-dir    config structures */
 
4820
   config_server_create,        /* create per-server config structures */
 
4821
   config_server_merge,         /* merge  per-server config structures */
 
4822
   command_table,               /* table of config file commands       */
 
4823
   register_hooks               /* register hooks                      */
 
4824
};
 
4825
 
 
4826
/*EOF*/