/* 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. */ #ifndef MOD_REWRITE_H #define MOD_REWRITE_H 1 /* ** _ _ _ ** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ ** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ ** | | | | | | (_) | (_| | | | | __/\ 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" #define APR_WANT_STRFUNC #define APR_WANT_MEMFUNC #include "apr_want.h" /* Include from the underlaying Unix system ... */ #if APR_HAVE_STDARG_H #include #endif #if APR_HAVE_STDLIB_H #include #endif #if APR_HAVE_CTYPE_H #include #endif #if APR_HAVE_SYS_TYPES_H #include #endif #if APR_HAS_THREADS #include "apr_thread_mutex.h" #endif #include "apr_optional.h" #include "apr_dbm.h" #include "ap_config.h" /* Include from the Apache server ... */ #define CORE_PRIVATE #include "httpd.h" #include "http_config.h" #include "http_request.h" #include "http_core.h" #include "http_log.h" #include "http_vhost.h" /* * The key in the r->notes apr_table_t wherein we store our accumulated * Vary values, and the one used for per-condition checks in a chain. */ #define VARY_KEY "rewrite-Vary" #define VARY_KEY_THIS "rewrite-Vary-this" /* ** ** Some defines ** */ #define ENVVAR_SCRIPT_URL "SCRIPT_URL" #define ENVVAR_SCRIPT_URI "SCRIPT_URI" #define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype" #define CONDFLAG_NONE 1<<0 #define CONDFLAG_NOCASE 1<<1 #define CONDFLAG_NOTMATCH 1<<2 #define CONDFLAG_ORNEXT 1<<3 #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_FORBIDDEN 1<<9 #define RULEFLAG_GONE 1<<10 #define RULEFLAG_QSAPPEND 1<<11 #define RULEFLAG_NOCASE 1<<12 #define RULEFLAG_NOESCAPE 1<<13 #define ACTION_NORMAL 1<<0 #define ACTION_NOESCAPE 1<<1 #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 ENGINE_DISABLED 1<<0 #define ENGINE_ENABLED 1<<1 #define OPTION_NONE 1<<0 #define OPTION_INHERIT 1<<1 #define CACHEMODE_TS 1<<0 #define CACHEMODE_TTL 1<<1 #define CACHE_TLB_ROWS 1024 #define CACHE_TLB_COLS 4 #ifndef FALSE #define FALSE 0 #define TRUE !FALSE #endif #ifndef NO #define NO FALSE #define YES TRUE #endif #ifndef RAND_MAX #define RAND_MAX 32767 #endif #ifndef LONG_STRING_LEN #define LONG_STRING_LEN 2048 #endif #define MAX_ENV_FLAGS 15 #define MAX_COOKIE_FLAGS 15 /*** max cookie size in rfc 2109 ***/ #define MAX_COOKIE_LEN 4096 /* default maximum number of internal redirects */ #define REWRITE_REDIRECT_LIMIT 10 /* ** ** our private data structures we handle with ** */ /* the list structures for holding the mapfile information * and the rewrite rules */ typedef struct { const char *name; /* the name of the map */ 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 */ 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; char *cachename; /* name for the cache */ } rewritemap_entry; typedef struct { char *input; /* Input string of RewriteCond */ char *pattern; /* the RegExp pattern string */ regex_t *regexp; int flags; /* Flags which control the match */ } rewritecond_entry; typedef struct { apr_array_header_t *rewriteconds; /* the corresponding RewriteCond entries */ char *pattern; /* the RegExp pattern string */ 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 */ int forced_responsecode; /* forced HTTP redirect response status */ char *env[MAX_ENV_FLAGS+1]; /* added environment variables */ char *cookie[MAX_COOKIE_FLAGS+1]; /* added cookies */ int skip; /* number of next rules to skip */ } rewriterule_entry; /* the per-server or per-virtual-server configuration * statically generated once on startup for every server */ typedef struct { int state; /* the RewriteEngine state */ int options; /* the RewriteOption state */ const char *rewritelogfile; /* the RewriteLog filename */ apr_file_t *rewritelogfp; /* the RewriteLog open filepointer */ int rewriteloglevel; /* the RewriteLog level of verbosity */ apr_array_header_t *rewritemaps; /* the RewriteMap entries */ apr_array_header_t *rewriteconds; /* the RewriteCond entries (temporary) */ apr_array_header_t *rewriterules; /* the RewriteRule entries */ server_rec *server; /* the corresponding server indicator */ int redirect_limit; /* maximum number of internal redirects */ } rewrite_server_conf; /* the per-directory configuration * generated on-the-fly by Apache server for current request */ typedef struct { int state; /* the RewriteEngine state */ int options; /* the RewriteOption state */ apr_array_header_t *rewriteconds; /* the RewriteCond entries (temporary) */ apr_array_header_t *rewriterules; /* the RewriteRule entries */ char *directory; /* the directory where it applies */ const char *baseurl; /* the base-URL where it applies */ int redirect_limit; /* maximum number of internal redirects */ } rewrite_perdir_conf; /* the per-request configuration */ typedef struct { int redirects; /* current number of redirects */ int redirect_limit; /* maximum number of redirects */ } rewrite_request_conf; /* the cache structures, * a 4-way hash apr_table_t with LRU functionality */ typedef struct cacheentry { apr_time_t time; char *key; char *value; } cacheentry; typedef struct tlbentry { int t[CACHE_TLB_COLS]; } cachetlbentry; typedef struct cachelist { char *resource; apr_array_header_t *entries; apr_array_header_t *tlb; } cachelist; typedef struct cache { apr_pool_t *pool; apr_array_header_t *lists; #if APR_HAS_THREADS apr_thread_mutex_t *lock; #endif } cache; /* the regex structure for the * substitution of backreferences */ typedef struct backrefinfo { char *source; int nsub; regmatch_t regmatch[AP_MAX_REG_MATCH]; } backrefinfo; /* ** ** forward declarations ** */ /* config structure handling */ static void *config_server_create(apr_pool_t *p, server_rec *s); static void *config_server_merge (apr_pool_t *p, void *basev, void *overridesv); static void *config_perdir_create(apr_pool_t *p, char *path); static void *config_perdir_merge (apr_pool_t *p, void *basev, void *overridesv); /* config directive handling */ static const char *cmd_rewriteengine(cmd_parms *cmd, void *dconf, int flag); static const char *cmd_rewriteoptions(cmd_parms *cmd, void *dconf, const char *option); static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, const char *a1); static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, const char *a1); static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, const char *a1, const char *a2); static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, const char *a1); static const char *cmd_rewritebase(cmd_parms *cmd, void *dconf, const char *a1); static const char *cmd_rewritecond(cmd_parms *cmd, void *dconf, const char *str); static const char *cmd_rewritecond_parseflagfield(apr_pool_t *p, rewritecond_entry *new, char *str); static const char *cmd_rewritecond_setflag(apr_pool_t *p, rewritecond_entry *cfg, char *key, char *val); static const char *cmd_rewriterule(cmd_parms *cmd, void *dconf, const char *str); static const char *cmd_rewriterule_parseflagfield(apr_pool_t *p, rewriterule_entry *new, char *str); static const char *cmd_rewriterule_setflag(apr_pool_t *p, rewriterule_entry *cfg, char *key, char *val); /* initialisation */ static int pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp); static int post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); static void init_child(apr_pool_t *p, server_rec *s); /* runtime hooks */ static int hook_uri2file (request_rec *r); static int hook_mimetype (request_rec *r); static int hook_fixup (request_rec *r); static int handler_redirect(request_rec *r); /* rewriting engine */ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, char *perdir); static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir); static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, char *perdir, backrefinfo *briRR, backrefinfo *briRC); static void do_expand(request_rec *r, char *input, char *buffer, int nbuf, backrefinfo *briRR, backrefinfo *briRC); static void do_expand_env(request_rec *r, char *env[], backrefinfo *briRR, backrefinfo *briRC); static void do_expand_cookie(request_rec *r, char *cookie[], backrefinfo *briRR, backrefinfo *briRC); /* URI transformation function */ static void splitout_queryargs(request_rec *r, int qsappend); static void fully_qualify_uri(request_rec *r); static void reduce_uri(request_rec *r); static unsigned is_absolute_uri(char *uri); static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme); static char *expand_tildepaths(request_rec *r, char *uri); /* rewrite map support functions */ static char *lookup_map(request_rec *r, char *name, char *key); static char *lookup_map_txtfile(request_rec *r, const char *file, char *key); static char *lookup_map_dbmfile(request_rec *r, const char *file, const char *dbmtype, char *key); static char *lookup_map_program(request_rec *r, apr_file_t *fpin, apr_file_t *fpout, char *key); typedef char *(rewrite_mapfunc_t)(request_rec *r, char *key); static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func); APR_DECLARE_OPTIONAL_FN(void, ap_register_rewrite_mapfunc, (char *name, rewrite_mapfunc_t *func)); static char *rewrite_mapfunc_toupper(request_rec *r, char *key); static char *rewrite_mapfunc_tolower(request_rec *r, char *key); static char *rewrite_mapfunc_escape(request_rec *r, char *key); static char *rewrite_mapfunc_unescape(request_rec *r, char *key); static char *select_random_value_part(request_rec *r, char *value); static void rewrite_rand_init(void); static int rewrite_rand(int l, int h); /* rewriting logfile support */ static int open_rewritelog(server_rec *s, apr_pool_t *p); static void rewritelog(request_rec *r, int level, const char *text, ...) __attribute__((format(printf,3,4))); static char *current_logtime(request_rec *r); /* rewriting lockfile support */ static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p); static apr_status_t rewritelock_remove(void *data); /* program map support */ static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p); static apr_status_t rewritemap_program_child(apr_pool_t *p, const char *progname, char **argv, apr_file_t **fpout, apr_file_t **fpin); /* env variable support */ static char *lookup_variable(request_rec *r, char *var); static char *lookup_header(request_rec *r, const char *name); /* caching functions */ static cache *init_cache(apr_pool_t *p); static char *get_cache_string(cache *c, const char *res, int mode, apr_time_t mtime, char *key); static void set_cache_string(cache *c, const char *res, int mode, apr_time_t mtime, char *key, char *value); static cacheentry *retrieve_cache_string(cache *c, const char *res, char *key); static void store_cache_string(cache *c, const char *res, cacheentry *ce); /* misc functions */ static char *subst_prefix_path(request_rec *r, char *input, char *match, const char *subst); static int parseargline(char *str, char **a1, char **a2, char **a3); static int prefix_stat(const char *path, apr_pool_t *pool); static void add_env_variable(request_rec *r, char *s); static void add_cookie(request_rec *r, char *s); static int subreq_ok(request_rec *r); static int is_redirect_limit_exceeded(request_rec *r); /* Lexicographic Comparison */ static int compare_lexicography(char *cpNum1, char *cpNum2); /* Bracketed expression handling */ static char *find_closing_bracket(char *s, int left, int right); static char *find_char_in_brackets(char *s, int c, int left, int right); #endif /* MOD_REWRITE_H */