Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/modules/mappers/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 21.6.2025 mit Größe 184 kB image not shown  

SSL mod_rewrite.c   Sprache: C

 
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/*                       _                            _ _
 *   _ __ ___   ___   __| |    _ __ _____      ___ __(_) |_ ___
 *  | '_ ` _ \ / _ \ / _` |   | '__/ _ \ \ /\ / / '__| | __/ _ \
 *  | | | | | | (_) | (_| |   | | |  __/\ V  V /| |  | | ||  __/
 *  |_| |_| |_|\___/ \__,_|___|_|  \___| \_/\_/ |_|  |_|\__\___|
 *                       |_____|
 *
 *  URL Rewriting Module
 *
 *  This module uses a rule-based rewriting engine (based on a
 *  regular-expression parser) to rewrite requested URLs on the fly.
 *
 *  It supports an unlimited number of additional rule conditions (which can
 *  operate on a lot of variables, even on HTTP headers) for granular
 *  matching and even external database lookups (either via plain text
 *  tables, DBM hash files or even external processes) for advanced URL
 *  substitution.
 *
 *  It operates on the full URLs (including the PATH_INFO part) both in
 *  per-server context (httpd.conf) and per-dir context (.htaccess) and even
 *  can generate QUERY_STRING parts on result.   The rewriting result finally
 *  can lead to internal subprocessing, external request redirection or even
 *  to internal proxy throughput.
 *
 *  This module was originally written in April 1996 and
 *  gifted exclusively to the The Apache Software Foundation in July 1997 by
 *
 *      Ralf S. Engelschall
 *      rse engelschall.com
 *      www.engelschall.com
 */


#include "apr.h"
#include "apr_strings.h"
#include "apr_hash.h"
#include "apr_user.h"
#include "apr_lib.h"
#include "apr_signal.h"
#include "apr_global_mutex.h"
#include "apr_dbm.h"
#include "apr_dbd.h"

#include "apr_version.h"
#if !APR_VERSION_AT_LEAST(2,0,0)
#include "apu_version.h"
#endif

#include "mod_dbd.h"

#if APR_HAS_THREADS
#include "apr_thread_mutex.h"
#endif

#define APR_WANT_MEMFUNC
#define APR_WANT_STRFUNC
#define APR_WANT_IOVEC
#include "apr_want.h"

/* XXX: Do we really need these headers? */
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if APR_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if APR_HAVE_STDARG_H
#include <stdarg.h>
#endif
#if APR_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if APR_HAVE_CTYPE_H
#include <ctype.h>
#endif
#if APR_HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_ssl.h"
#include "http_vhost.h"
#include "util_mutex.h"

#include "mod_rewrite.h"
#include "ap_expr.h"

#include "test_char.h"

static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL;
static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
static const char* really_last_key = "rewrite_really_last";

/*
 * in order to improve performance on running production systems, you
 * may strip all rewritelog code entirely from mod_rewrite by using the
 * -DREWRITELOG_DISABLED compiler option.
 *
 * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are
 * responsible for answering all the mod_rewrite questions out there.
 */

/* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */
#ifdef  APLOG_MAX_LOGLEVEL
#if     APLOG_MAX_LOGLEVEL < APLOG_TRACE1
#ifndef REWRITELOG_DISABLED
#define REWRITELOG_DISABLED
#endif
#endif
#endif

#ifndef REWRITELOG_DISABLED
#ifdef AP_HAVE_C99
#define rewritelog(...) do_rewritelog(__LINE__, __VA_ARGS__)
#else
#define rewritelog do_rewritelog
#endif
#define REWRITELOG_MODE  ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE )

#else /* !REWRITELOG_DISABLED */

#ifdef AP_HAVE_C99
#define rewritelog(...)
#else
#define rewritelog do_rewritelog
#endif
#endif /* REWRITELOG_DISABLED */

/* remembered mime-type for [T=...] */
#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
#define REWRITE_FORCED_HANDLER_NOTEVAR  "rewrite-forced-handler"

#define ENVVAR_SCRIPT_URL "SCRIPT_URL"
#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL
#define ENVVAR_SCRIPT_URI "SCRIPT_URI"

#define CONDFLAG_NONE               (1<<0)
#define CONDFLAG_NOCASE             (1<<1)
#define CONDFLAG_NOTMATCH           (1<<2)
#define CONDFLAG_ORNEXT             (1<<3)
#define CONDFLAG_NOVARY             (1<<4)

#define RULEFLAG_NONE               (1<<0)
#define RULEFLAG_FORCEREDIRECT      (1<<1)
#define RULEFLAG_LASTRULE           (1<<2)
#define RULEFLAG_NEWROUND           (1<<3)
#define RULEFLAG_CHAIN              (1<<4)
#define RULEFLAG_IGNOREONSUBREQ     (1<<5)
#define RULEFLAG_NOTMATCH           (1<<6)
#define RULEFLAG_PROXY              (1<<7)
#define RULEFLAG_PASSTHROUGH        (1<<8)
#define RULEFLAG_QSAPPEND           (1<<9)
#define RULEFLAG_NOCASE             (1<<10)
#define RULEFLAG_NOESCAPE           (1<<11)
#define RULEFLAG_NOSUB              (1<<12)
#define RULEFLAG_STATUS             (1<<13)
#define RULEFLAG_ESCAPEBACKREF      (1<<14)
#define RULEFLAG_DISCARDPATHINFO    (1<<15)
#define RULEFLAG_QSDISCARD          (1<<16)
#define RULEFLAG_END                (1<<17)
#define RULEFLAG_ESCAPENOPLUS       (1<<18)
#define RULEFLAG_QSLAST             (1<<19)
#define RULEFLAG_QSNONE             (1<<20) /* programattic only */
#define RULEFLAG_ESCAPECTLS         (1<<21)
#define RULEFLAG_UNSAFE_PREFIX_STAT (1<<22)
#define RULEFLAG_UNSAFE_ALLOW3F     (1<<23)
#define RULEFLAG_UNC                (1<<24)

/* return code of the rewrite rule
 * the result may be escaped - or not
 */

#define ACTION_NORMAL               (1<<0)
#define ACTION_NOESCAPE             (1<<1)
#define ACTION_STATUS               (1<<2)
#define ACTION_STATUS_SET           (1<<3)

#define MAPTYPE_TXT                 (1<<0)
#define MAPTYPE_DBM                 (1<<1)
#define MAPTYPE_PRG                 (1<<2)
#define MAPTYPE_INT                 (1<<3)
#define MAPTYPE_RND                 (1<<4)
#define MAPTYPE_DBD                 (1<<5)
#define MAPTYPE_DBD_CACHE           (1<<6)

#define ENGINE_DISABLED             (1<<0)
#define ENGINE_ENABLED              (1<<1)

#define OPTION_NONE                 (1<<0)
#define OPTION_INHERIT              (1<<1)
#define OPTION_INHERIT_BEFORE       (1<<2)
#define OPTION_NOSLASH              (1<<3)
#define OPTION_ANYURI               (1<<4)
#define OPTION_MERGEBASE            (1<<5)
#define OPTION_INHERIT_DOWN         (1<<6)
#define OPTION_INHERIT_DOWN_BEFORE  (1<<7)
#define OPTION_IGNORE_INHERIT       (1<<8)
#define OPTION_IGNORE_CONTEXT_INFO  (1<<9)
#define OPTION_LEGACY_PREFIX_DOCROOT (1<<10)
#define OPTION_UNSAFE_PREFIX_STAT   (1<<12)

#ifndef RAND_MAX
#define RAND_MAX 32767
#endif

/* max cookie size in rfc 2109 */
/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */
#define MAX_COOKIE_LEN 4096

/* max line length (incl.\n) in text rewrite maps */
#ifndef REWRITE_MAX_TXT_MAP_LINE
#define REWRITE_MAX_TXT_MAP_LINE 1024
#endif

/* buffer length for prg rewrite maps */
#ifndef REWRITE_PRG_MAP_BUF
#define REWRITE_PRG_MAP_BUF 1024
#endif

/* for better readbility */
#define LEFT_CURLY  '{'
#define RIGHT_CURLY '}'

/*
 * expansion result items on the stack to save some cycles
 *
 * (5 == about 2 variables like "foo%{var}bar%{var}baz")
 */

#define SMALL_EXPANSION 5

/*
 * check that a subrequest won't cause infinite recursion
 *
 * either not in a subrequest, or in a subrequest
 * and URIs aren't NULL and sub/main URIs differ
 */

#define subreq_ok(r) (!r->main || \
    (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))

#ifndef REWRITE_MAX_ROUNDS
#define REWRITE_MAX_ROUNDS 32000
#endif

/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                 Types and Structures
 * |                                                       |
 * +-------------------------------------------------------+
 */


typedef struct {
    const char *datafile;          /* filename for map data files         */
    const char *dbmtype;           /* dbm type for dbm map data files     */
    const char *checkfile;         /* filename to check for map existence */
    const char *cachename;         /* for cached maps (txt/rnd/dbm)       */
    int   type;                    /* the type of the map                 */
    apr_file_t *fpin;              /* in  file pointer for program maps   */
    apr_file_t *fpout;             /* out file pointer for program maps   */
    apr_file_t *fperr;             /* err file pointer for program maps   */
    char *(*func)(request_rec *,   /* function pointer for internal maps  */
                  char *);
    char **argv;                   /* argv of the external rewrite map    */
    const char *dbdq;              /* SQL SELECT statement for rewritemap */
    const char *checkfile2;        /* filename to check for map existence
                                      NULL if only one file               */

    const char *user;              /* run RewriteMap program as this user */
    const char *group;             /* run RewriteMap program as this group */
} rewritemap_entry;

/* special pattern types for RewriteCond */
typedef enum {
    CONDPAT_REGEX = 0,
    CONDPAT_FILE_EXISTS,
    CONDPAT_FILE_SIZE,
    CONDPAT_FILE_LINK,
    CONDPAT_FILE_DIR,
    CONDPAT_FILE_XBIT,
    CONDPAT_LU_URL,
    CONDPAT_LU_FILE,
    CONDPAT_STR_LT,
    CONDPAT_STR_LE,
    CONDPAT_STR_EQ,
    CONDPAT_STR_GT,
    CONDPAT_STR_GE,
    CONDPAT_INT_LT,
    CONDPAT_INT_LE,
    CONDPAT_INT_EQ,
    CONDPAT_INT_GT,
    CONDPAT_INT_GE,
    CONDPAT_AP_EXPR
} pattern_type;

typedef enum {
  RULE_RC_NOMATCH = 0,      /* the rule didn't match                        */
  RULE_RC_MATCH = 1,        /* a matching rule w/ substitution              */
  RULE_RC_NOSUB = 2,        /* a matching rule w/ no substitution           */
  RULE_RC_STATUS_SET = 3    /* a matching rule that has set an HTTP error
                               to be returned in r->status */

} rule_return_type;

typedef enum {
  COND_RC_NOMATCH = 0,      /* the cond didn't match                        */
  COND_RC_MATCH = 1,        /* the cond matched                             */
  COND_RC_STATUS_SET = 3    /* The condition eval set a final r->status     */
} cond_return_type;

typedef struct {
    char           *input;   /* Input string of RewriteCond   */
    char           *pattern; /* the RegExp pattern string     */
    ap_regex_t     *regexp;  /* the precompiled regexp        */
    ap_expr_info_t *expr;    /* the compiled ap_expr          */
    int             flags;   /* Flags which control the match */
    pattern_type    ptype;   /* pattern type                  */
    int             pskip;   /* back-index to display pattern */
} rewritecond_entry;

/* single linked list for env vars and cookies */
typedef struct data_item {
    struct data_item *next;
    char *data;
} data_item;

typedef struct {
    apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */
    char      *pattern;              /* the RegExp pattern string             */
    ap_regex_t *regexp;              /* the RegExp pattern compilation        */
    char      *output;               /* the Substitution string               */
    int        flags;                /* Flags which control the substitution  */
    char      *forced_mimetype;      /* forced MIME type of substitution      */
    char      *forced_handler;       /* forced content handler of subst.      */
    int        forced_responsecode;  /* forced HTTP response status           */
    data_item *env;                  /* added environment variables           */
    data_item *cookie;               /* added cookies                         */
    int        skip;                 /* number of next rules to skip          */
    int        maxrounds;            /* limit on number of loops with N flag  */
    const char *escapes;             /* specific backref escapes              */
    const char *noescapes;           /* specific backref chars not to escape  */
} rewriterule_entry;

typedef struct {
    int           state;              /* the RewriteEngine state            */
    int           options;            /* the RewriteOption state            */
    apr_hash_t         *rewritemaps;  /* the RewriteMap entries             */
    apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.)    */
    apr_array_header_t *rewriterules; /* the RewriteRule entries            */
    server_rec   *server;             /* the corresponding server indicator */
    unsigned int state_set:1;
    unsigned int options_set:1;
} rewrite_server_conf;

typedef struct {
    int           state;              /* the RewriteEngine state           */
    int           options;            /* the RewriteOption state           */
    apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.)   */
    apr_array_header_t *rewriterules; /* the RewriteRule entries           */
    char         *directory;          /* the directory where it applies    */
    const char   *baseurl;            /* the base-URL  where it applies    */
    unsigned int state_set:1;
    unsigned int options_set:1;
    unsigned int baseurl_set:1;
} rewrite_perdir_conf;

/* the (per-child) cache structures.
 */

typedef struct cache {
    apr_pool_t         *pool;
    apr_hash_t         *maps;
#if APR_HAS_THREADS
    apr_thread_mutex_t *lock;
#endif
} cache;

/* cached maps contain an mtime for the whole map and live in a subpool
 * of the cachep->pool. That makes it easy to forget them if necessary.
 */

typedef struct {
    apr_time_t mtime;
    apr_pool_t *pool;
    apr_hash_t *entries;
} cachedmap;

/* the regex structure for the
 * substitution of backreferences
 */

typedef struct backrefinfo {
    const char *source;
    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
} backrefinfo;

/* single linked list used for
 * variable expansion
 */

typedef struct result_list {
    struct result_list *next;
    apr_size_t len;
    const char *string;
} result_list;

/* context structure for variable lookup and expansion
 */

typedef struct {
    request_rec *r;
    const char  *uri;
    const char  *vary_this;
    const char  *vary;
    char        *perdir;
    backrefinfo briRR;
    backrefinfo briRC;
} rewrite_ctx;

/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                 static module data
 * |                                                       |
 * +-------------------------------------------------------+
 */


/* the global module structure */
module AP_MODULE_DECLARE_DATA rewrite_module;

/* rewritemap int: handler function registry */
static apr_hash_t *mapfunc_hash;

/* the cache */
static cache *cachep;

/* whether proxy module is available or not */
static int proxy_available;

/* Locks/Mutexes */
static int rewrite_lock_needed = 0;
static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
static const char *rewritemap_mutex_type = "rewrite-map";

/* Optional functions imported from mod_ssl when loaded: */
static char *escape_backref(apr_pool_t *p, const char *path,
                            const char *escapeme, const char *noescapeme,
                            int flags);

/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |              rewriting logfile support
 * |                                                       |
 * +-------------------------------------------------------+
 */


/* Slightly complicated definitions follow here to support
 * compile-time choice of enabled/disabled of rewritelog both with or
 * without C99 support, where varargs macros are only available with
 * C99.
 * 
 * do_rewritelog is defined for (rewritelog enabled) or (rewritelog
 * disabled AND no C99 support) but is an noop for the latter case and
 * should get optimized away.
 *
 * For the (rewritelog disabled) case the function is defined
 * differently for C99/non-C99. For C99, the rewritelog() macro passes
 * __LINE__, allowing accurate logging of line numbers. For non-C99
 * the line number used for rewritelog() tracing is always
 * constant. */


#if !defined(REWRITELOG_DISABLED) || !defined(AP_HAVE_C99)

#ifdef AP_HAVE_C99
static void do_rewritelog(int line,
                          request_rec *r, int level, char *perdir,
                          const char *fmt, ...)
        __attribute__((format(printf,5,6)));
#else
static void do_rewritelog(request_rec *r, int level, char *perdir,
                          const char *fmt, ...)
        __attribute__((format(printf,4,5)));
#endif

static void do_rewritelog(
#ifdef AP_HAVE_C99
                          int line,
#endif
                          request_rec *r, int level, char *perdir,
                          const char *fmt, ...)
{
#ifndef REWRITELOG_DISABLED
    char *logline, *text;
    const char *rhost, *rname;
    int redir;
    request_rec *req;
    va_list ap;

    if (!APLOG_R_IS_LEVEL(r, APLOG_DEBUG + level))
        return;

    rhost = ap_get_useragent_host(r, REMOTE_NOLOOKUP, NULL);
    rname = ap_get_remote_logname(r);

    for (redir=0, req=r; req->prev; req = req->prev) {
        ++redir;
    }

    va_start(ap, fmt);
    text = apr_pvsprintf(r->pool, fmt, ap);
    va_end(ap);

    logline = apr_psprintf(r->pool, "%s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] "
                                    "%s%s%s%s",
                           rhost ? rhost : "UNKNOWN-HOST",
                           rname ? rname : "-",
                           r->user ? (*r->user ? r->user : "\"\"") : "-",
                           ap_get_server_name(r),
                           (void *)(r->server),
                           (void *)r,
                           r->main ? "subreq" : "initial",
                           redir ? "/redir#" : "",
                           redir ? apr_itoa(r->pool, redir) : "",
                           perdir ? "[perdir " : "",
                           perdir ? perdir : "",
                           perdir ? "] """,
                           text);

    AP_REWRITE_LOG((uintptr_t)r, level, r->main ? 0 : 1, (char *)ap_get_server_name(r), logline);

    /* Intentional no APLOGNO */
#ifdef AP_HAVE_C99
    ap_log_rerror(__FILE__, line, APLOG_MODULE_INDEX, APLOG_DEBUG + level, 0, r, "%s", logline);
#else
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG + level, 0, r, "%s", logline);
#endif
    
#endif /* !REWRITELOG_DISABLED */
}
#endif /* !defined(REWRITELOG_DISABLED) || !defined(AP_HAVE_C99) */


/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                URI and path functions
 * |                                                       |
 * +-------------------------------------------------------+
 */


/* return number of chars of the scheme (incl. '://')
 * if the URI is absolute (includes a scheme etc.)
 * otherwise 0.
 * If supportqs is not NULL, we return a whether or not
 * the scheme supports a query string or not.
 *
 * NOTE: If you add new schemes here, please have a
 *       look at escape_absolute_uri and splitout_queryargs.
 *       Not every scheme takes query strings and some schemes
 *       may be handled in a special way.
 *
 * XXX: we may consider a scheme registry, perhaps with
 *      appropriate escape callbacks to allow other modules
 *      to extend mod_rewrite at runtime.
 */

static unsigned is_absolute_uri(char *uri, int *supportsqs)
{
    int dummy, *sqs;

    sqs = (supportsqs ? supportsqs : &dummy);
    *sqs = 0;
    /* fast exit */
    if (*uri == '/' || strlen(uri) <= 5) {
        return 0;
    }

    switch (*uri++) {
    case 'a':
    case 'A':
        if (!ap_cstr_casecmpn(uri, "jp://", 5)) { /* ajp:// */
          *sqs = 1;
          return 6;
        }
        break;

    case 'b':
    case 'B':
        if (!ap_cstr_casecmpn(uri, "alancer://", 10)) { /* balancer:// */
          *sqs = 1;
          return 11;
        }
        break;

    case 'f':
    case 'F':
        if (!ap_cstr_casecmpn(uri, "tp://", 5)) { /* ftp:// */
            return 6;
        }
        if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* fcgi:// */
            *sqs = 1;
            return 7;
        }
        break;

    case 'g':
    case 'G':
        if (!ap_cstr_casecmpn(uri, "opher://", 8)) { /* gopher:// */
            return 9;
        }
        break;

    case 'h':
    case 'H':
        if (!ap_cstr_casecmpn(uri, "ttp://", 6)) { /* http:// */
            *sqs = 1;
            return 7;
        }
        else if (!ap_cstr_casecmpn(uri, "ttps://", 7)) { /* https:// */
            *sqs = 1;
            return 8;
        }
        else if (!ap_cstr_casecmpn(uri, "2://", 4)) { /* h2:// */
            *sqs = 1;
            return 5;
        }
        else if (!ap_cstr_casecmpn(uri, "2c://", 5)) { /* h2c:// */
            *sqs = 1;
            return 6;
        }
        break;

    case 'l':
    case 'L':
        if (!ap_cstr_casecmpn(uri, "dap://", 6)) { /* ldap:// */
            return 7;
        }
        break;

    case 'm':
    case 'M':
        if (!ap_cstr_casecmpn(uri, "ailto:", 6)) {       /* mailto:   */
            *sqs = 1;
            return 7;
        }
        break;

    case 'n':
    case 'N':
        if (!ap_cstr_casecmpn(uri, "ews:", 4)) {         /* news:     */
            return 5;
        }
        else if (!ap_cstr_casecmpn(uri, "ntp://", 6)) { /* nntp:// */
            return 7;
        }
        break;

    case 's':
    case 'S':
        if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* scgi:// */
            *sqs = 1;
            return 7;
        }
        break;

    case 'w':
    case 'W':
        if (!ap_cstr_casecmpn(uri, "s://", 4)) { /* ws:// */
            *sqs = 1;
            return 5;
        }
        else if (!ap_cstr_casecmpn(uri, "ss://", 5)) { /* wss:// */
            *sqs = 1;
            return 6;
        }
        break;

    case 'u':
    case 'U':
        if (!ap_cstr_casecmpn(uri, "nix:", 4)) {        /* unix:     */
            *sqs = 1;
            return (uri[4] == '/' && uri[5] == '/') ? 7 : 5;
        }
    }

    return 0;
}

static int is_absolute_path(const char *path)
{
#ifndef CASE_BLIND_FILESYSTEM
    return (path[0] == '/');
#else
    return ((AP_IS_SLASH(path[0]) && path[1] == path[0])
            || (apr_isalpha(path[0]) && path[1] == ':' && AP_IS_SLASH(path[2])));
#endif
}

static const char c2x_table[] = "0123456789abcdef";

static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
                                     unsigned char *where)
{
#if APR_CHARSET_EBCDIC
    what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
#endif /*APR_CHARSET_EBCDIC*/
    *where++ = prefix;
    *where++ = c2x_table[what >> 4];
    *where++ = c2x_table[what & 0xf];
    return where;
}

/*
 * Escapes a backreference in a similar way as php's urlencode does.
 * Based on ap_os_escape_path in server/util.c
 */

static char *escape_backref(apr_pool_t *p, const char *path,
                            const char *escapeme, const char *noescapeme,
                            int flags)
{
    char *copy = apr_palloc(p, 3 * strlen(path) + 1);
    const unsigned char *s = (const unsigned char *)path;
    unsigned char *d = (unsigned char *)copy;
    int noplus = (flags & RULEFLAG_ESCAPENOPLUS) != 0;
    int ctls = (flags & RULEFLAG_ESCAPECTLS) != 0;
    unsigned char c;

    while ((c = *s)) {
        if (((ctls ? !TEST_CHAR(c, T_VCHAR_OBSTEXT) : !escapeme)
             || (escapeme && ap_strchr_c(escapeme, c)))
            && (!noescapeme || !ap_strchr_c(noescapeme, c))) {
            if (apr_isalnum(c) || c == '_') {
                *d++ = c;
            }
            else if (c == ' ' && !noplus) {
                *d++ = '+';
            }
            else {
                d = c2x(c, '%', d);
            }
        }
        else {
            *d++ = c;
        }
        ++s;
    }
    *d = '\0';
    return copy;
}

/*
 * escape absolute uri, which may or may not be path oriented.
 * So let's handle them differently.
 */

static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme)
{
    char *cp;

    /* be safe.
     * NULL should indicate elsewhere, that something's wrong
     */

    if (!scheme || strlen(uri) < scheme) {
        return NULL;
    }

    cp = uri + scheme;

    /* scheme with authority part? */
    if (cp[-1] == '/') {
        /* skip host part */
        while (*cp && *cp != '/') {
            ++cp;
        }

        /* nothing after the hostpart. ready! */
        if (!*cp || !*++cp) {
            return apr_pstrdup(p, uri);
        }

        /* remember the hostname stuff */
        scheme = cp - uri;

        /* special thing for ldap.
         * The parts are separated by question marks. From RFC 2255:
         *     ldapurl = scheme "://" [hostport] ["/"
         *               [dn ["?" [attributes] ["?" [scope]
         *               ["?" [filter] ["?" extensions]]]]]]
         */

        if (!ap_cstr_casecmpn(uri, "ldap", 4)) {
            char *token[5];
            int c = 0;

            token[0] = cp = apr_pstrdup(p, cp);
            while (*cp && c < 4) {
                if (*cp == '?') {
                    token[++c] = cp + 1;
                    *cp = '\0';
                }
                ++cp;
            }

            return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
                                          ap_escape_uri(p, token[0]),
                               (c >= 1) ? "?" : NULL,
                               (c >= 1) ? ap_escape_uri(p, token[1]) : NULL,
                               (c >= 2) ? "?" : NULL,
                               (c >= 2) ? ap_escape_uri(p, token[2]) : NULL,
                               (c >= 3) ? "?" : NULL,
                               (c >= 3) ? ap_escape_uri(p, token[3]) : NULL,
                               (c >= 4) ? "?" : NULL,
                               (c >= 4) ? ap_escape_uri(p, token[4]) : NULL,
                               NULL);
        }
    }

    /* Nothing special here. Apply normal escaping. */
    return apr_pstrcat(p, apr_pstrndup(p, uri, scheme),
                       ap_escape_uri(p, cp), NULL);
}

/*
 * split out a QUERY_STRING part from
 * the current URI string
 */

static void splitout_queryargs(request_rec *r, int flags)
{
    char *q;
    int split, skip;
    int qsappend = flags & RULEFLAG_QSAPPEND;
    int qsdiscard = flags & RULEFLAG_QSDISCARD;
    int qslast = flags & RULEFLAG_QSLAST;

    if (flags & RULEFLAG_QSNONE) {
        rewritelog(r, 2, NULL, "discarding query string, no parse from substitution");
        r->args = NULL;
        return;
    }

    /* don't touch, unless it's a scheme for which a query string makes sense.
     * See RFC 1738 and RFC 2368.
     */

    if ((skip = is_absolute_uri(r->filename, &split))
        && !split) {
        r->args = NULL; /* forget the query that's still flying around */
        return;
    }

    if (qsdiscard) {
        r->args = NULL; /* Discard query string */
        rewritelog(r, 2, NULL, "discarding query string");
    }

    q = qslast ? ap_strrchr(r->filename + skip, '?') : ap_strchr(r->filename + skip, '?');

    if (q != NULL) {
        char *olduri;
        apr_size_t len;

        olduri = apr_pstrdup(r->pool, r->filename);
        *q++ = '\0';
        if (qsappend) {
            if (*q) {
                r->args = apr_pstrcat(r->pool, q, "&" , r->args, NULL);
            }
        }
        else {
            r->args = apr_pstrdup(r->pool, q);
        }

        if (r->args) {
           len = strlen(r->args);

           if (!len) {
               r->args = NULL;
           }
           else if (r->args[len-1] == '&') {
               r->args[len-1] = '\0';
           }
        }

        rewritelog(r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri,
                   r->filename, r->args ? r->args : "");
    }
}

/*
 * strip 'http[s]://ourhost/' from URI
 */

static void reduce_uri(request_rec *r)
{
    char *cp;
    apr_size_t l;

    cp = (char *)ap_http_scheme(r);
    l  = strlen(cp);
    if (   strlen(r->filename) > l+3
        && ap_cstr_casecmpn(r->filename, cp, l) == 0
        && r->filename[l]   == ':'
        && r->filename[l+1] == '/'
        && r->filename[l+2] == '/' ) {

        unsigned short port;
        char *portp, *host, *url, *scratch;

        scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */

        /* cut the hostname and port out of the URI */
        cp = host = scratch + l + 3;    /* 3 == strlen("://") */
        while (*cp && *cp != '/' && *cp != ':') {
            ++cp;
        }

        if (*cp == ':') {      /* additional port given */
            *cp++ = '\0';
            portp = cp;
            while (*cp && *cp != '/') {
                ++cp;
            }
            *cp = '\0';

            port = atoi(portp);
            url = r->filename + (cp - scratch);
            if (!*url) {
                url = "/";
            }
        }
        else if (*cp == '/') { /* default port */
            *cp = '\0';

            port = ap_default_port(r);
            url = r->filename + (cp - scratch);
        }
        else {
            port = ap_default_port(r);
            url = "/";
        }

        /* now check whether we could reduce it to a local path... */
        if (ap_matches_request_vhost(r, host, port)) {
            rewrite_server_conf *conf = 
                ap_get_module_config(r->server->module_config, &rewrite_module);
            rewritelog(r, 3, NULL, "reduce %s -> %s", r->filename, url);
            r->filename = apr_pstrdup(r->pool, url);

            /* remember that the uri was reduced */
            if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
                apr_table_setn(r->notes, "mod_rewrite_uri_reduced""true");
            }
        }
    }

    return;
}

/*
 * add 'http[s]://ourhost[:ourport]/' to URI
 * if URI is still not fully qualified
 */

static void fully_qualify_uri(request_rec *r)
{
    if (r->method_number == M_CONNECT) {
        return;
    }
    else if (!is_absolute_uri(r->filename, NULL)) {
        const char *thisserver;
        char *thisport;
        int port;

        thisserver = ap_get_server_name_for_url(r);
        port = ap_get_server_port(r);
        thisport = ap_is_default_port(port, r)
                   ? ""
                   : apr_psprintf(r->pool, ":%u", port);

        r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s",
                                   ap_http_scheme(r), thisserver, thisport,
                                   (*r->filename == '/') ? "" : "/",
                                   r->filename);
    }

    return;
}

static int startsWith(request_rec *r, const char *haystack, const char *needle) {
    int rc = (ap_strstr_c(haystack, needle) == haystack);
    rewritelog(r, 5, NULL, "prefix_stat startsWith(%s, %s) %d", haystack, needle, rc);
    return rc;
}
/*
 * stat() only the first segment of a path, and only if it matches the output of the last matching rule
 */

static int prefix_stat(request_rec *r, const char *path, apr_pool_t *pool, rewriterule_entry *lastsub)
{
    const char *curpath = path;
    const char *root;
    const char *slash;
    char *statpath;
    apr_status_t rv;

    rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool);

    if (rv != APR_SUCCESS) {
        return 0;
    }

    /* let's recognize slashes only, the mod_rewrite semantics are opaque
     * enough.
     */

    if ((slash = ap_strchr_c(curpath, '/')) != NULL) {
        rv = apr_filepath_merge(&statpath, root,
                                apr_pstrndup(pool, curpath,
                                             (apr_size_t)(slash - curpath)),
                                APR_FILEPATH_NOTABOVEROOT |
                                APR_FILEPATH_NOTRELATIVE, pool);
    }
    else {
        rv = apr_filepath_merge(&statpath, root, curpath,
                                APR_FILEPATH_NOTABOVEROOT |
                                APR_FILEPATH_NOTRELATIVE, pool);
    }

    if (rv == APR_SUCCESS) {
        apr_finfo_t sb;

        if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) {
            if (!lastsub) {
                rewritelog(r, 3, NULL, "prefix_stat no lastsub subst prefix %s", statpath);
                return 1;
            }

            rewritelog(r, 3, NULL, "prefix_stat compare statpath %s and lastsub output %s STATOK %d ",
                    statpath, lastsub->output, lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT);
            if (lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT) {
                return 1;
            }
            else {
                const char *docroot = ap_document_root(r);
                const char *context_docroot = ap_context_document_root(r);
                /*
                 * As an example, path (r->filename) is /var/foo/bar/baz.html
                 * even if the flag is not set,  we can accept a rule that
                 * began with a literal /var (stapath), or if the entire path
                 * starts with the docroot or context document root
                 */

                if (startsWith(r, lastsub->output, statpath) ||
                        startsWith(r, path, docroot) ||
                        ((docroot != context_docroot) &&
                          startsWith(r, path, context_docroot))) {
                    return 1;
                }
            }
        }
    }

    /* prefix will be added */
    return 0;
}

/*
 * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase)
 */

static char *subst_prefix_path(request_rec *r, char *input, const char *match,
                               const char *subst)
{
    apr_size_t len = strlen(match);

    if (len && match[len - 1] == '/') {
        --len;
    }

    if (!strncmp(input, match, len) && input[len++] == '/') {
        apr_size_t slen, outlen;
        char *output;

        rewritelog(r, 5, NULL, "strip matching prefix: %s -> %s", input,
                   input+len);

        slen = strlen(subst);
        if (slen && subst[slen - 1] != '/') {
            ++slen;
        }

        outlen = strlen(input) + slen - len;
        output = apr_palloc(r->pool, outlen + 1); /* don't forget the \0 */

        memcpy(output, subst, slen);
        if (slen && !output[slen-1]) {
            output[slen-1] = '/';
        }
        memcpy(output+slen, input+len, outlen - slen);
        output[outlen] = '\0';

        rewritelog(r, 4, NULL, "add subst prefix: %s -> %s", input+len,
                   output);

        return output;
    }

    /* prefix didn't match */
    return input;
}


/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                    caching support
 * |                                                       |
 * +-------------------------------------------------------+
 */


static void set_cache_value(const char *name, apr_time_t t, char *key,
                            char *val)
{
    cachedmap *map;

    if (cachep) {
#if APR_HAS_THREADS
        apr_thread_mutex_lock(cachep->lock);
#endif
        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);

        if (!map) {
            apr_pool_t *p;

            if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
#if APR_HAS_THREADS
                apr_thread_mutex_unlock(cachep->lock);
#endif
                return;
            }
            apr_pool_tag(p, "rewrite_cachedmap");

            map = apr_palloc(cachep->pool, sizeof(cachedmap));
            map->pool = p;
            map->entries = apr_hash_make(map->pool);
            map->mtime = t;

            apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
        }
        else if (map->mtime != t) {
            apr_pool_clear(map->pool);
            map->entries = apr_hash_make(map->pool);
            map->mtime = t;
        }

        /* Now we should have a valid map->entries hash, where we
         * can store our value.
         *
         * We need to copy the key and the value into OUR pool,
         * so that we don't leave it during the r->pool cleanup.
         */

        apr_hash_set(map->entries,
                     apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING,
                     apr_pstrdup(map->pool, val));

#if APR_HAS_THREADS
        apr_thread_mutex_unlock(cachep->lock);
#endif
    }

    return;
}

static char *get_cache_value(const char *name, apr_time_t t, char *key,
                             apr_pool_t *p)
{
    cachedmap *map;
    char *val = NULL;

    if (cachep) {
#if APR_HAS_THREADS
        apr_thread_mutex_lock(cachep->lock);
#endif
        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);

        if (map) {
            /* if this map is outdated, forget it. */
            if (map->mtime != t) {
                apr_pool_clear(map->pool);
                map->entries = apr_hash_make(map->pool);
                map->mtime = t;
            }
            else {
                val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING);
                if (val) {
                    /* copy the cached value into the supplied pool,
                     * where it belongs (r->pool usually)
                     */

                    val = apr_pstrdup(p, val);
                }
            }
        }

#if APR_HAS_THREADS
        apr_thread_mutex_unlock(cachep->lock);
#endif
    }

    return val;
}

static int init_cache(apr_pool_t *p)
{
    cachep = apr_palloc(p, sizeof(cache));
    if (apr_pool_create(&cachep->pool, p) != APR_SUCCESS) {
        cachep = NULL; /* turns off cache */
        return 0;
    }
    apr_pool_tag(cachep->pool, "rewrite_cachep");

    cachep->maps = apr_hash_make(cachep->pool);
#if APR_HAS_THREADS
    (void)apr_thread_mutex_create(&(cachep->lock), APR_THREAD_MUTEX_DEFAULT, p);
#endif

    return 1;
}


/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                    Map Functions
 * |                                                       |
 * +-------------------------------------------------------+
 */


/*
 * General Note: key is already a fresh string, created (expanded) just
 * for the purpose to be passed in here. So one can modify key itself.
 */


static char *rewrite_mapfunc_toupper(request_rec *r, char *key)
{
    ap_str_toupper(key);

    return key;
}

static char *rewrite_mapfunc_tolower(request_rec *r, char *key)
{
    ap_str_tolower(key);

    return key;
}

static char *rewrite_mapfunc_escape(request_rec *r, char *key)
{
    return ap_escape_uri(r->pool, key);
}

static char *rewrite_mapfunc_unescape(request_rec *r, char *key)
{
    ap_unescape_url(key);

    return key;
}

static char *select_random_value_part(request_rec *r, char *value)
{
    char *p = value;
    unsigned n = 1;

    /* count number of distinct values */
    while ((p = ap_strchr(p, '|')) != NULL) {
        ++n;
        ++p;
    }

    if (n > 1) {
        n = ap_random_pick(1, n);

        /* extract it from the whole string */
        while (--n && (value = ap_strchr(value, '|')) != NULL) {
            ++value;
        }

        if (value) { /* should not be NULL, but ... */
            p = ap_strchr(value, '|');
            if (p) {
                *p = '\0';
            }
        }
    }

    return value;
}

/* child process code */
static void rewrite_child_errfn(apr_pool_t *p, apr_status_t err,
                                const char *desc)
{
    ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, APLOGNO(00653) "%s", desc);
}

static apr_status_t rewritemap_program_child(apr_pool_t *p,
                                             const char *progname, char **argv,
                                             const char *user, const char *group,
                                             apr_file_t **fpout,
                                             apr_file_t **fpin)
{
    apr_status_t rc;
    apr_procattr_t *procattr;
    apr_proc_t *procnew;

    if (   APR_SUCCESS == (rc=apr_procattr_create(&procattr, p))
        && APR_SUCCESS == (rc=apr_procattr_io_set(procattr, APR_FULL_BLOCK,
                                                  APR_FULL_BLOCK, APR_NO_PIPE))
        && APR_SUCCESS == (rc=apr_procattr_dir_set(procattr,
                                             ap_make_dirstr_parent(p, argv[0])))
        && (!user || APR_SUCCESS == (rc=apr_procattr_user_set(procattr, user, "")))
        && (!group || APR_SUCCESS == (rc=apr_procattr_group_set(procattr, group)))
        && APR_SUCCESS == (rc=apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
        && APR_SUCCESS == (rc=apr_procattr_child_errfn_set(procattr,
                                                           rewrite_child_errfn))
        && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) {

        procnew = apr_pcalloc(p, sizeof(*procnew));
        rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL,
                             procattr, p);

        if (rc == APR_SUCCESS) {
            apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);

            if (fpin) {
                (*fpin) = procnew->in;
            }

            if (fpout) {
                (*fpout) = procnew->out;
            }
        }
    }

    return (rc);
}

static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p)
{
    rewrite_server_conf *conf;
    apr_hash_index_t *hi;
    apr_status_t rc;

    conf = ap_get_module_config(s->module_config, &rewrite_module);

    /*  If the engine isn't turned on,
     *  don't even try to do anything.
     */

    if (conf->state == ENGINE_DISABLED) {
        return APR_SUCCESS;
    }

    for (hi = apr_hash_first(p, conf->rewritemaps); hi; hi = apr_hash_next(hi)){
        apr_file_t *fpin = NULL;
        apr_file_t *fpout = NULL;
        rewritemap_entry *map;
        void *val;

        apr_hash_this(hi, NULL, NULL, &val);
        map = val;

        if (map->type != MAPTYPE_PRG) {
            continue;
        }
        if (!(map->argv[0]) || !*(map->argv[0]) || map->fpin || map->fpout) {
            continue;
        }

        rc = rewritemap_program_child(p, map->argv[0], map->argv,
                                      map->user, map->group,
                                      &fpout, &fpin);
        if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) {
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, APLOGNO(00654)
                         "mod_rewrite: could not start RewriteMap "
                         "program %s", map->checkfile);
            return rc;
        }
        map->fpin  = fpin;
        map->fpout = fpout;
    }

    return APR_SUCCESS;
}


/*
 * +-------------------------------------------------------+
 * |                                                       |
 * |                  Lookup functions
 * |                                                       |
 * +-------------------------------------------------------+
 */


static char *lookup_map_txtfile(request_rec *r, const char *file, char *key)
{
    apr_file_t *fp = NULL;
    char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
    char *value, *keylast;
    apr_status_t rv;

    if ((rv = apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
                            r->pool)) != APR_SUCCESS)
    {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00655)
                      "mod_rewrite: can't open text RewriteMap file %s", file);
        return NULL;
    }

    keylast = key + strlen(key);
    value = NULL;
    while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
        char *p, *c;

        /* ignore comments and lines starting with whitespaces */
        if (*line == '#' || apr_isspace(*line)) {
            continue;
        }

        p = line;
        c = key;
        while (c < keylast && *p == *c && !apr_isspace(*p)) {
            ++p;
            ++c;
        }

        /* key doesn't match - ignore. */
        if (c != keylast || !apr_isspace(*p)) {
            continue;
        }

        /* jump to the value */
        while (apr_isspace(*p)) {
            ++p;
        }

        /* no value? ignore */
        if (!*p) {
            continue;
        }

        /* extract the value and return. */
        c = p;
        while (*p && !apr_isspace(*p)) {
            ++p;
        }
        value = apr_pstrmemdup(r->pool, c, p - c);
        break;
    }
    apr_file_close(fp);

    return value;
}

static char *lookup_map_dbmfile(request_rec *r, const char *file,
                                const char *dbmtype, char *key)
{
#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
    const apr_dbm_driver_t *driver;
    const apu_err_t *err;
#endif
    apr_dbm_t *dbmfp = NULL;
    apr_datum_t dbmkey;
    apr_datum_t dbmval;
    char *value;
    apr_status_t rv;

#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
    if ((rv = apr_dbm_get_driver(&driver, dbmtype, &err,
            r->pool)) != APR_SUCCESS) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10287)
                "mod_rewrite: can't load DBM library '%s': %s",
                     err->reason, err->msg);
        return NULL;
    }
    if ((rv = apr_dbm_open2(&dbmfp, driver, file, APR_DBM_READONLY,
                              APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00656)
                      "mod_rewrite: can't open DBM RewriteMap %s", file);
        return NULL;
    }
#else
    if ((rv = apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY,
                              APR_OS_DEFAULT, r->pool)) != APR_SUCCESS)
    {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00656)
                      "mod_rewrite: can't open DBM RewriteMap %s", file);
        return NULL;
    }
#endif

    dbmkey.dptr  = key;
    dbmkey.dsize = strlen(key);

    if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) {
        value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize);
    }
    else {
        value = NULL;
    }

    apr_dbm_close(dbmfp);

    return value;
}
static char *lookup_map_dbd(request_rec *r, char *key, const char *label)
{
    apr_status_t rv;
    apr_dbd_prepared_t *stmt;
    const char *errmsg;
    apr_dbd_results_t *res = NULL;
    apr_dbd_row_t *row = NULL;
    char *ret = NULL;
    int n = 0;
    ap_dbd_t *db = dbd_acquire(r);
    
    if (db == NULL) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02963)
                      "rewritemap: No db handle available! "
                      "Check your database access");
        return NULL;
   }

    stmt = apr_hash_get(db->prepared, label, APR_HASH_KEY_STRING);

    rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res,
                          stmt, 0, key, NULL);
    if (rv != 0) {
        errmsg = apr_dbd_error(db->driver, db->handle, rv);
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00657)
                      "rewritemap: error %s querying for %s", errmsg, key);
        return NULL;
    }
    while ((rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1)) == 0) {
        ++n;
        if (ret == NULL) {
            ret = apr_pstrdup(r->pool,
                              apr_dbd_get_entry(db->driver, row, 0));
        }
        else {
            /* randomise crudely amongst multiple results */
            if ((double)rand() < (double)RAND_MAX/(double)n) {
                ret = apr_pstrdup(r->pool,
                                  apr_dbd_get_entry(db->driver, row, 0));
            }
        }
    }
    if (rv != -1) {
        errmsg = apr_dbd_error(db->driver, db->handle, rv);
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00658)
                      "rewritemap: error %s looking up %s", errmsg, key);
    }
    switch (n) {
    case 0:
        return NULL;
    case 1:
        return ret;
    default:
        /* what's a fair rewritelog level for this? */
        rewritelog(r, 3, NULL, "Multiple values found for %s", key);
        return ret;
    }
}

static char *lookup_map_program(request_rec *r, apr_file_t *fpin,
                                apr_file_t *fpout, char *key)
{
    char *buf;
    char c;
    apr_size_t i, nbytes, combined_len = 0;
    apr_status_t rv;
    const char *eol = APR_EOL_STR;
    apr_size_t eolc = 0;
    int found_nl = 0;
    result_list *buflist = NULL, *curbuf = NULL;

#ifndef NO_WRITEV
    struct iovec iova[2];
    apr_size_t niov;
#endif

    /* when `RewriteEngine off' was used in the per-server
     * context then the rewritemap-programs were not spawned.
     * In this case using such a map (usually in per-dir context)
     * is useless because it is not available.
     *
     * newlines in the key leave bytes in the pipe and cause
     * bad things to happen (next map lookup will use the chars
     * after the \n instead of the new key etc etc - in other words,
     * the Rewritemap falls out of sync with the requests).
     */

    if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) {
        return NULL;
    }

    /* take the lock */
    if (rewrite_mapr_lock_acquire) {
        rv = apr_global_mutex_lock(rewrite_mapr_lock_acquire);
        if (rv != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00659)
                          "apr_global_mutex_lock(rewrite_mapr_lock_acquire) "
                          "failed");
            return NULL; /* Maybe this should be fatal? */
        }
    }

    /* write out the request key */
#ifdef NO_WRITEV
    nbytes = strlen(key);
    /* XXX: error handling */
    apr_file_write_full(fpin, key, nbytes, NULL);
    nbytes = 1;
    apr_file_write_full(fpin, "\n", nbytes, NULL);
#else
    iova[0].iov_base = key;
    iova[0].iov_len = strlen(key);
    iova[1].iov_base = "\n";
    iova[1].iov_len = 1;

    niov = 2;
    /* XXX: error handling */
    apr_file_writev_full(fpin, iova, niov, &nbytes);
#endif

    buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF + 1);

    /* read in the response value */
    nbytes = 1;
    apr_file_read(fpout, &c, &nbytes);
    do {
        i = 0;
        while (nbytes == 1 && (i < REWRITE_PRG_MAP_BUF)) {
            if (c == eol[eolc]) {
                if (!eol[++eolc]) {
                    /* remove eol from the buffer */
                    --eolc;
                    if (i < eolc) {
                        curbuf->len -= eolc-i;
                        i = 0;
                    }
                    else {
                        i -= eolc;
                    }
                    ++found_nl;
                    break;
                }
            }

            /* only partial (invalid) eol sequence -> reset the counter */
            else if (eolc) {
                eolc = 0;
            }

            /* catch binary mode, e.g. on Win32 */
            else if (c == '\n') {
                ++found_nl;
                break;
            }

            buf[i++] = c;
            apr_file_read(fpout, &c, &nbytes);
        }

        /* well, if there wasn't a newline yet, we need to read further */
        if (buflist || (nbytes == 1 && !found_nl)) {
            if (!buflist) {
                curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist));
            }
            else if (i) {
                curbuf->next = apr_palloc(r->pool, sizeof(*buflist));
                curbuf = curbuf->next;

            }
            curbuf->next = NULL;

            if (i) {
                curbuf->string = buf;
                curbuf->len = i;
                combined_len += i;
                buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF);
            }

            if (nbytes == 1 && !found_nl) {
                continue;
            }
        }

        break;
    } while (1);

    /* concat the stuff */
    if (buflist) {
        char *p;

        p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */
        while (buflist) {
            if (buflist->len) {
                memcpy(p, buflist->string, buflist->len);
                p += buflist->len;
            }
            buflist = buflist->next;
        }
        *p = '\0';
        i = combined_len;
    }
    else {
        buf[i] = '\0';
    }

    /* give the lock back */
    if (rewrite_mapr_lock_acquire) {
        rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire);
        if (rv != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00660)
                          "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) "
                          "failed");
            return NULL; /* Maybe this should be fatal? */
        }
    }

    /* catch the "failed" case */
    if (i == 4 && !strcasecmp(buf, "NULL")) {
        return NULL;
    }

    return buf;
}

/*
 * generic map lookup
 */

static char *lookup_map(request_rec *r, char *name, char *key)
{
    rewrite_server_conf *conf;
    rewritemap_entry *s;
    char *value;
    apr_finfo_t st;
    apr_status_t rv;

    /* get map configuration */
    conf = ap_get_module_config(r->server->module_config, &rewrite_module);
    s = apr_hash_get(conf->rewritemaps, name, APR_HASH_KEY_STRING);

    /* map doesn't exist */
    if (!s) {
        return NULL;
    }

    switch (s->type) {
    /*
     * Text file map (perhaps random)
     */

    case MAPTYPE_RND:
    case MAPTYPE_TXT:
        rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
        if (rv != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00661)
                          "mod_rewrite: can't access text RewriteMap file %s",
                          s->checkfile);
            return NULL;
        }

        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
        if (!value) {
            rewritelog(r, 6, NULL,
                       "cache lookup FAILED, forcing new map lookup");
            
            value = lookup_map_txtfile(r, s->datafile, key);
            if (!value) {
                rewritelog(r, 5, NULL, "map lookup FAILED: map=%s[txt] key=%s",
                           name, key);
                set_cache_value(s->cachename, st.mtime, key, "");
                return NULL;
            }

            rewritelog(r, 5, NULL, "map lookup OK: map=%s[txt] key=%s -> val=%s",
                       name, key, value);
            set_cache_value(s->cachename, st.mtime, key, value);
        }
        else {
            rewritelog(r, 5, NULL, "cache lookup OK: map=%s[txt] key=%s -> val=%s",
                       name, key, value);
        }

        if (s->type == MAPTYPE_RND && *value) {
            value = select_random_value_part(r, value);
            rewritelog(r, 5, NULL, "randomly chosen the subvalue `%s'",value);
        }

        return *value ? value : NULL;

    /*
     * DBM file map
     */

    case MAPTYPE_DBM:
        rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
        if (rv != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00662)
                          "mod_rewrite: can't access DBM RewriteMap file %s",
                          s->checkfile);
        }
        else if(s->checkfile2 != NULL) {
            apr_finfo_t st2;

            rv = apr_stat(&st2, s->checkfile2, APR_FINFO_MIN, r->pool);
            if (rv != APR_SUCCESS) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00663)
                              "mod_rewrite: can't access DBM RewriteMap "
                              "file %s", s->checkfile2);
            }
            else if(st2.mtime > st.mtime) {
                st.mtime = st2.mtime;
            }
        }
        if(rv != APR_SUCCESS) {
            return NULL;
        }

        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
        if (!value) {
            rewritelog(r, 6, NULL,
                       "cache lookup FAILED, forcing new map lookup");

            value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key);
            if (!value) {
                rewritelog(r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s",
                           name, key);
                set_cache_value(s->cachename, st.mtime, key, "");
                return NULL;
            }

            rewritelog(r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> "
                       "val=%s", name, key, value);

            set_cache_value(s->cachename, st.mtime, key, value);
            return value;
        }

        rewritelog(r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s",
                   name, key, value);
        return *value ? value : NULL;

    /*
     * SQL map without cache
     */

    case MAPTYPE_DBD:
        value = lookup_map_dbd(r, key, s->dbdq);
        if (!value) {
            rewritelog(r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
                       name, key);
            return NULL;
        }

        rewritelog(r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
                   name, key, value);

        return value;

    /*
     * SQL map with cache
     */

    case MAPTYPE_DBD_CACHE:
        value = get_cache_value(s->cachename, 0, key, r->pool);
        if (!value) {
            rewritelog(r, 6, NULL,
                       "cache lookup FAILED, forcing new map lookup");

            value = lookup_map_dbd(r, key, s->dbdq);
            if (!value) {
                rewritelog(r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
                           name, key);
                set_cache_value(s->cachename, 0, key, "");
                return NULL;
            }

            rewritelog(r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
                       name, key, value);

            set_cache_value(s->cachename, 0, key, value);
            return value;
        }

        rewritelog(r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s",
                   name, key, value);
        return *value ? value : NULL;

    /*
     * Program file map
     */

    case MAPTYPE_PRG:
        value = lookup_map_program(r, s->fpin, s->fpout, key);
        if (!value) {
            rewritelog(r, 5, NULL, "map lookup FAILED: map=%s key=%s", name,
                       key);
            return NULL;
        }

        rewritelog(r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
                   name, key, value);
        return value;

    /*
     * Internal Map
     */

    case MAPTYPE_INT:
        value = s->func(r, key);
        if (!value) {
            rewritelog(r, 5, NULL, "map lookup FAILED: map=%s key=%s", name,
                       key);
            return NULL;
        }

        rewritelog(r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s",
                   name, key, value);
        return value;
    }

    return NULL;
}

/*
 * lookup a HTTP header and set VARY note
 */

static const char *lookup_header(const char *name, rewrite_ctx *ctx)
{
    const char *val = apr_table_get(ctx->r->headers_in, name);

    /* Skip the 'Vary: Host' header combination
     * as indicated in rfc7231 section-7.1.4
     */

    if (val && strcasecmp(name, "Host") != 0) {
        ctx->vary_this = ctx->vary_this
                         ? apr_pstrcat(ctx->r->pool, ctx->vary_this, ", ",
                                       name, NULL)
                         : apr_pstrdup(ctx->r->pool, name);
    }

    return val;
}

/*
 * lookahead helper function
 * Determine the correct URI path in perdir context
 */

static APR_INLINE const char *la_u(rewrite_ctx *ctx)
{
    rewrite_perdir_conf *conf;

    if (*ctx->uri == '/') {
        return ctx->uri;
    }

    conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module);

    return apr_pstrcat(ctx->r->pool, conf->baseurl
                                     ? conf->baseurl : conf->directory,
                       ctx->uri, NULL);
}

/*
 * generic variable lookup
 */

static char *lookup_variable(char *var, rewrite_ctx *ctx)
{
    const char *result;
    request_rec *r = ctx->r;
    apr_size_t varlen = strlen(var);

    /* fast exit */
    if (varlen < 4) {
        return "";
    }

    result = NULL;

    /* fast tests for variable length variables (sic) first */
    if (var[3] == ':') {
        if (var[4] && !strncasecmp(var, "ENV", 3)) {
            var += 4;
            result = apr_table_get(r->notes, var);

            if (!result) {
                result = apr_table_get(r->subprocess_env, var);
            }
            if (!result) {
                result = getenv(var);
            }
        }
        else if (var[4] && !strncasecmp(var, "SSL", 3)) {
            result = ap_ssl_var_lookup(r->pool, r->server, r->connection, r,
                                        var + 4);
        }
    }
    else if (var[4] == ':') {
        if (var[5]) {
            request_rec *rr;
            const char *path;

            if (!strncasecmp(var, "HTTP", 4)) {
                result = lookup_header(var+5, ctx);
            }
            else if (!strncasecmp(var, "LA-U", 4)) {
                if (ctx->uri && subreq_ok(r)) {
                    path = ctx->perdir ? la_u(ctx) : ctx->uri;
                    rr = ap_sub_req_lookup_uri(path, r, NULL);
                    ctx->r = rr;
                    result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
                    ctx->r = r;
                    ap_destroy_sub_req(rr);

                    rewritelog(r, 5, ctx->perdir, "lookahead: path=%s var=%s "
                                "-> val=%s", path, var+5, result);

                    return (char *)result;
                }
            }
            else if (!strncasecmp(var, "LA-F", 4)) {
                if (ctx->uri && subreq_ok(r)) {
                    path = ctx->uri;
                    if (ctx->perdir && *path == '/') {
                        /* sigh, the user wants a file based subrequest, but
                         * we can't do one, since we don't know what the file
                         * path is! In this case behave like LA-U.
                         */

                        rr = ap_sub_req_lookup_uri(path, r, NULL);
                    }
                    else {
                        if (ctx->perdir) {
                            rewrite_perdir_conf *conf;

                            conf = ap_get_module_config(r->per_dir_config,
                                                        &rewrite_module);

                            path = apr_pstrcat(r->pool, conf->directory, path,
                                               NULL);
                        }

                        rr = ap_sub_req_lookup_file(path, r, NULL);
                    }

                    ctx->r = rr;
                    result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx));
                    ctx->r = r;
                    ap_destroy_sub_req(rr);

                    rewritelog(r, 5, ctx->perdir, "lookahead: path=%s var=%s "
                                "-> val=%s", path, var+5, result);

                    return (char *)result;
                }
            }
        }
    }

    /* well, do it the hard way */
    else {
        apr_time_exp_t tm;

        /* can't do this above, because of the getenv call */
        ap_str_toupper(var);

        switch (varlen) {
        case  4:
            if (!strcmp(var, "TIME")) {
                apr_time_exp_lt(&tm, apr_time_now());
                result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
                                      tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
                                      tm.tm_hour, tm.tm_min, tm.tm_sec);
                rewritelog(r, 1, ctx->perdir, "RESULT='%s'", result);
                return (char *)result;
            }
            else if (!strcmp(var, "IPV6")) {
                int flag = FALSE;
#if APR_HAVE_IPV6
                apr_sockaddr_t *addr = r->useragent_addr;
                flag = (addr->family == AF_INET6 &&
                        !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr));
                rewritelog(r, 1, ctx->perdir, "IPV6='%s'", flag ? "on" : "off");
#else
                rewritelog(r, 1, ctx->perdir, "IPV6='off' (IPv6 is not enabled)");
#endif
                result = (flag ? "on" : "off");
            }
--> --------------------

--> maximum size reached

--> --------------------

91%


¤ Dauer der Verarbeitung: 0.55 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.