home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_rewrite.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  130.3 KB  |  4,197 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1996-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58.  
  59. /*                       _                            _ _
  60. **   _ __ ___   ___   __| |    _ __ _____      ___ __(_) |_ ___
  61. **  | '_ ` _ \ / _ \ / _` |   | '__/ _ \ \ /\ / / '__| | __/ _ \
  62. **  | | | | | | (_) | (_| |   | | |  __/\ V  V /| |  | | ||  __/
  63. **  |_| |_| |_|\___/ \__,_|___|_|  \___| \_/\_/ |_|  |_|\__\___|
  64. **                       |_____|
  65. **
  66. **  URL Rewriting Module
  67. **
  68. **  This module uses a rule-based rewriting engine (based on a
  69. **  regular-expression parser) to rewrite requested URLs on the fly.
  70. **
  71. **  It supports an unlimited number of additional rule conditions (which can
  72. **  operate on a lot of variables, even on HTTP headers) for granular
  73. **  matching and even external database lookups (either via plain text
  74. **  tables, DBM hash files or even external processes) for advanced URL
  75. **  substitution.
  76. **
  77. **  It operates on the full URLs (including the PATH_INFO part) both in
  78. **  per-server context (httpd.conf) and per-dir context (.htaccess) and even
  79. **  can generate QUERY_STRING parts on result.   The rewriting result finally
  80. **  can lead to internal subprocessing, external request redirection or even
  81. **  to internal proxy throughput.
  82. **
  83. **  This module was originally written in April 1996 and
  84. **  gifted exclusively to the The Apache Group in July 1997 by
  85. **
  86. **      Ralf S. Engelschall
  87. **      rse@engelschall.com
  88. **      www.engelschall.com
  89. */
  90.  
  91.  
  92. #include "mod_rewrite.h"
  93.  
  94.  
  95. /*
  96. ** +-------------------------------------------------------+
  97. ** |                                                       |
  98. ** |             static module configuration
  99. ** |                                                       |
  100. ** +-------------------------------------------------------+
  101. */
  102.  
  103.  
  104. /*
  105. **  Our interface to the Apache server kernel:
  106. **
  107. **  o  Runtime logic of a request is as following:
  108. **       while(request or subrequest)
  109. **           foreach(stage #0...#9)
  110. **               foreach(module) (**)
  111. **                   try to run hook
  112. **
  113. **  o  the order of modules at (**) is the inverted order as
  114. **     given in the "Configuration" file, i.e. the last module
  115. **     specified is the first one called for each hook!
  116. **     The core module is always the last!
  117. **
  118. **  o  there are two different types of result checking and
  119. **     continue processing:
  120. **     for hook #0,#1,#4,#5,#6,#8:
  121. **         hook run loop stops on first modules which gives
  122. **         back a result != DECLINED, i.e. it usually returns OK
  123. **         which says "OK, module has handled this _stage_" and for #1
  124. **         this have not to mean "Ok, the filename is now valid".
  125. **     for hook #2,#3,#7,#9:
  126. **         all hooks are run, independend of result
  127. **
  128. **  o  at the last stage, the core module always
  129. **       - says "BAD_REQUEST" if r->filename does not begin with "/"
  130. **       - prefix URL with document_root or replaced server_root
  131. **         with document_root and sets r->filename
  132. **       - always return a "OK" independed if the file really exists
  133. **         or not!
  134. */
  135.  
  136.     /* The section for the Configure script:
  137.      * MODULE-DEFINITION-START
  138.      * Name: rewrite_module
  139.      * ConfigStart
  140.     . ./helpers/find-dbm-lib
  141.     if [ "x$found_dbm" = "x1" ]; then
  142.         echo "      enabling DBM support for mod_rewrite"
  143.     else
  144.         echo "      disabling DBM support for mod_rewrite"
  145.         echo "      (perhaps you need to add -ldbm, -lndbm or -lgdbm to EXTRA_LIBS)"
  146.         CFLAGS="$CFLAGS -DNO_DBM_REWRITEMAP"
  147.     fi
  148.      * ConfigEnd
  149.      * MODULE-DEFINITION-END
  150.      */
  151.  
  152.     /* the table of commands we provide */
  153. static const command_rec command_table[] = {
  154.     { "RewriteEngine",   cmd_rewriteengine,   NULL, OR_FILEINFO, FLAG,
  155.       "On or Off to enable or disable (default) the whole rewriting engine" },
  156.     { "RewriteOptions",  cmd_rewriteoptions,  NULL, OR_FILEINFO, ITERATE,
  157.       "List of option strings to set" },
  158.     { "RewriteBase",     cmd_rewritebase,     NULL, OR_FILEINFO, TAKE1,
  159.       "the base URL of the per-directory context" },
  160.     { "RewriteCond",     cmd_rewritecond,     NULL, OR_FILEINFO, RAW_ARGS,
  161.       "a input string and a to be applied regexp-pattern" },
  162.     { "RewriteRule",     cmd_rewriterule,     NULL, OR_FILEINFO, RAW_ARGS,
  163.       "a URL-applied regexp-pattern and a substitution URL" },
  164.     { "RewriteMap",      cmd_rewritemap,      NULL, RSRC_CONF,   TAKE2,
  165.       "a mapname and a filename" },
  166.     { "RewriteLock",     cmd_rewritelock,     NULL, RSRC_CONF,   TAKE1,
  167.       "the filename of a lockfile used for inter-process synchronization"},
  168.     { "RewriteLog",      cmd_rewritelog,      NULL, RSRC_CONF,   TAKE1,
  169.       "the filename of the rewriting logfile" },
  170.     { "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF,   TAKE1,
  171.       "the level of the rewriting logfile verbosity "
  172.       "(0=none, 1=std, .., 9=max)" },
  173.     { NULL }
  174. };
  175.  
  176.     /* the table of content handlers we provide */
  177. static const handler_rec handler_table[] = {
  178.     { "redirect-handler", handler_redirect },
  179.     { NULL }
  180. };
  181.  
  182.     /* the main config structure */
  183. module MODULE_VAR_EXPORT rewrite_module = {
  184.    STANDARD_MODULE_STUFF,
  185.    init_module,                 /* module initializer                  */
  186.    config_perdir_create,        /* create per-dir    config structures */
  187.    config_perdir_merge,         /* merge  per-dir    config structures */
  188.    config_server_create,        /* create per-server config structures */
  189.    config_server_merge,         /* merge  per-server config structures */
  190.    command_table,               /* table of config file commands       */
  191.    handler_table,               /* [#8] MIME-typed-dispatched handlers */
  192.    hook_uri2file,               /* [#1] URI to filename translation    */
  193.    NULL,                        /* [#4] validate user id from request  */
  194.    NULL,                        /* [#5] check if the user is ok _here_ */
  195.    NULL,                        /* [#3] check access by host address   */
  196.    hook_mimetype,               /* [#6] determine MIME type            */
  197.    hook_fixup,                  /* [#7] pre-run fixups                 */
  198.    NULL,                        /* [#9] log a transaction              */
  199.    NULL,                        /* [#2] header parser                  */
  200.    init_child,                  /* child_init                          */
  201.    NULL,                        /* child_exit                          */
  202.    NULL                         /* [#0] post read-request              */
  203. };
  204.  
  205.     /* the cache */
  206. static cache *cachep;
  207.  
  208.     /* whether proxy module is available or not */
  209. static int proxy_available;
  210.  
  211.     /* the txt mapfile parsing stuff */
  212. static regex_t   *lookup_map_txtfile_regexp = NULL;
  213. static regmatch_t lookup_map_txtfile_regmatch[MAX_NMATCH];
  214.  
  215.  
  216. /*
  217. ** +-------------------------------------------------------+
  218. ** |                                                       |
  219. ** |           configuration directive handling
  220. ** |                                                       |
  221. ** +-------------------------------------------------------+
  222. */
  223.  
  224. /*
  225. **
  226. **  per-server configuration structure handling
  227. **
  228. */
  229.  
  230. static void *config_server_create(pool *p, server_rec *s)
  231. {
  232.     rewrite_server_conf *a;
  233.  
  234.     a = (rewrite_server_conf *)ap_pcalloc(p, sizeof(rewrite_server_conf));
  235.  
  236.     a->state           = ENGINE_DISABLED;
  237.     a->options         = OPTION_NONE;
  238.     a->rewritelogfile  = NULL;
  239.     a->rewritelogfp    = -1;
  240.     a->rewriteloglevel = 0;
  241.     a->rewritelockfile = NULL;
  242.     a->rewritelockfp   = -1;
  243.     a->rewritemaps     = ap_make_array(p, 2, sizeof(rewritemap_entry));
  244.     a->rewriteconds    = ap_make_array(p, 2, sizeof(rewritecond_entry));
  245.     a->rewriterules    = ap_make_array(p, 2, sizeof(rewriterule_entry));
  246.     a->server          = s;
  247.  
  248.     return (void *)a;
  249. }
  250.  
  251. static void *config_server_merge(pool *p, void *basev, void *overridesv)
  252. {
  253.     rewrite_server_conf *a, *base, *overrides;
  254.  
  255.     a         = (rewrite_server_conf *)ap_pcalloc(p, sizeof(rewrite_server_conf));
  256.     base      = (rewrite_server_conf *)basev;
  257.     overrides = (rewrite_server_conf *)overridesv;
  258.  
  259.     a->state   = overrides->state;
  260.     a->options = overrides->options;
  261.     a->server  = overrides->server;
  262.  
  263.     if (a->options & OPTION_INHERIT) {
  264.         /*
  265.          *  local directives override
  266.          *  and anything else is inherited
  267.          */
  268.         a->rewriteloglevel = overrides->rewriteloglevel != 0 
  269.                              ? overrides->rewriteloglevel
  270.                              : base->rewriteloglevel;
  271.         a->rewritelogfile  = overrides->rewritelogfile != NULL 
  272.                              ? overrides->rewritelogfile
  273.                              : base->rewritelogfile;
  274.         a->rewritelogfp    = overrides->rewritelogfp != -1 
  275.                              ? overrides->rewritelogfp 
  276.                              : base->rewritelogfp;
  277.         a->rewritelockfile = overrides->rewritelockfile != NULL
  278.                              ? overrides->rewritelockfile
  279.                              : base->rewritelockfile;
  280.         a->rewritelockfp   = overrides->rewritelockfp != -1
  281.                              ? overrides->rewritelockfp
  282.                              : base->rewritelockfp;
  283.         a->rewritemaps     = ap_append_arrays(p, overrides->rewritemaps,
  284.                                               base->rewritemaps);
  285.         a->rewriteconds    = ap_append_arrays(p, overrides->rewriteconds,
  286.                                               base->rewriteconds);
  287.         a->rewriterules    = ap_append_arrays(p, overrides->rewriterules,
  288.                                               base->rewriterules);
  289.     }
  290.     else {
  291.         /*
  292.          *  local directives override
  293.          *  and anything else gets defaults
  294.          */
  295.         a->rewriteloglevel = overrides->rewriteloglevel;
  296.         a->rewritelogfile  = overrides->rewritelogfile;
  297.         a->rewritelogfp    = overrides->rewritelogfp;
  298.         a->rewritelockfile = overrides->rewritelockfile;
  299.         a->rewritelockfp   = overrides->rewritelockfp;
  300.         a->rewritemaps     = overrides->rewritemaps;
  301.         a->rewriteconds    = overrides->rewriteconds;
  302.         a->rewriterules    = overrides->rewriterules;
  303.     }
  304.  
  305.     return (void *)a;
  306. }
  307.  
  308.  
  309. /*
  310. **
  311. **  per-directory configuration structure handling
  312. **
  313. */
  314.  
  315. static void *config_perdir_create(pool *p, char *path)
  316. {
  317.     rewrite_perdir_conf *a;
  318.  
  319.     a = (rewrite_perdir_conf *)ap_pcalloc(p, sizeof(rewrite_perdir_conf));
  320.  
  321.     a->state           = ENGINE_DISABLED;
  322.     a->options         = OPTION_NONE;
  323.     a->baseurl         = NULL;
  324.     a->rewriteconds    = ap_make_array(p, 2, sizeof(rewritecond_entry));
  325.     a->rewriterules    = ap_make_array(p, 2, sizeof(rewriterule_entry));
  326.  
  327.     if (path == NULL) {
  328.         a->directory = NULL;
  329.     }
  330.     else {
  331.         /* make sure it has a trailing slash */
  332.         if (path[strlen(path)-1] == '/') {
  333.             a->directory = ap_pstrdup(p, path);
  334.         }
  335.         else {
  336.             a->directory = ap_pstrcat(p, path, "/", NULL);
  337.         }
  338.     }
  339.  
  340.     return (void *)a;
  341. }
  342.  
  343. static void *config_perdir_merge(pool *p, void *basev, void *overridesv)
  344. {
  345.     rewrite_perdir_conf *a, *base, *overrides;
  346.  
  347.     a         = (rewrite_perdir_conf *)ap_pcalloc(p,
  348.                                                   sizeof(rewrite_perdir_conf));
  349.     base      = (rewrite_perdir_conf *)basev;
  350.     overrides = (rewrite_perdir_conf *)overridesv;
  351.  
  352.     a->state     = overrides->state;
  353.     a->options   = overrides->options;
  354.     a->directory = overrides->directory;
  355.     a->baseurl   = overrides->baseurl;
  356.  
  357.     if (a->options & OPTION_INHERIT) {
  358.         a->rewriteconds = ap_append_arrays(p, overrides->rewriteconds,
  359.                                            base->rewriteconds);
  360.         a->rewriterules = ap_append_arrays(p, overrides->rewriterules,
  361.                                            base->rewriterules);
  362.     }
  363.     else {
  364.         a->rewriteconds = overrides->rewriteconds;
  365.         a->rewriterules = overrides->rewriterules;
  366.     }
  367.  
  368.     return (void *)a;
  369. }
  370.  
  371.  
  372. /*
  373. **
  374. **  the configuration commands
  375. **
  376. */
  377.  
  378. static const char *cmd_rewriteengine(cmd_parms *cmd,
  379.                                      rewrite_perdir_conf *dconf, int flag)
  380. {
  381.     rewrite_server_conf *sconf;
  382.  
  383.     sconf = 
  384.         (rewrite_server_conf *)ap_get_module_config(cmd->server->module_config,
  385.                                                     &rewrite_module);
  386.  
  387.     if (cmd->path == NULL) { /* is server command */
  388.         sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
  389.     }
  390.     else                   /* is per-directory command */ {
  391.         dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
  392.     }
  393.  
  394.     return NULL;
  395. }
  396.  
  397. static const char *cmd_rewriteoptions(cmd_parms *cmd,
  398.                                       rewrite_perdir_conf *dconf, char *option)
  399. {
  400.     rewrite_server_conf *sconf;
  401.     const char *err;
  402.  
  403.     sconf = (rewrite_server_conf *)
  404.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  405.  
  406.     if (cmd->path == NULL) { /* is server command */
  407.         err = cmd_rewriteoptions_setoption(cmd->pool,
  408.                                            &(sconf->options), option);
  409.     }
  410.     else {                 /* is per-directory command */
  411.         err = cmd_rewriteoptions_setoption(cmd->pool,
  412.                                            &(dconf->options), option);
  413.     }
  414.  
  415.     return err;
  416. }
  417.  
  418. static const char *cmd_rewriteoptions_setoption(pool *p, int *options,
  419.                                                 char *name)
  420. {
  421.     if (strcasecmp(name, "inherit") == 0) {
  422.         *options |= OPTION_INHERIT;
  423.     }
  424.     else {
  425.         return ap_pstrcat(p, "RewriteOptions: unknown option '",
  426.                           name, "'\n", NULL);
  427.     }
  428.     return NULL;
  429. }
  430.  
  431. static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, char *a1)
  432. {
  433.     rewrite_server_conf *sconf;
  434.  
  435.     sconf = (rewrite_server_conf *)
  436.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  437.  
  438.     sconf->rewritelogfile = a1;
  439.  
  440.     return NULL;
  441. }
  442.  
  443. static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1)
  444. {
  445.     rewrite_server_conf *sconf;
  446.  
  447.     sconf = (rewrite_server_conf *)
  448.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  449.  
  450.     sconf->rewriteloglevel = atoi(a1);
  451.  
  452.     return NULL;
  453. }
  454.  
  455. static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, char *a1,
  456.                                   char *a2)
  457. {
  458.     rewrite_server_conf *sconf;
  459.     rewritemap_entry *new;
  460.     struct stat st;
  461.  
  462.     sconf = (rewrite_server_conf *)
  463.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  464.  
  465.     new = ap_push_array(sconf->rewritemaps);
  466.  
  467.     new->name = a1;
  468.     new->func = NULL;
  469.     if (strncmp(a2, "txt:", 4) == 0) {
  470.         new->type      = MAPTYPE_TXT;
  471.         new->datafile  = a2+4;
  472.         new->checkfile = a2+4;
  473.     }
  474.     else if (strncmp(a2, "rnd:", 4) == 0) {
  475.         new->type      = MAPTYPE_RND;
  476.         new->datafile  = a2+4;
  477.         new->checkfile = a2+4;
  478.     }
  479.     else if (strncmp(a2, "dbm:", 4) == 0) {
  480. #ifndef NO_DBM_REWRITEMAP
  481.         new->type      = MAPTYPE_DBM;
  482.         new->datafile  = a2+4;
  483.         new->checkfile = ap_pstrcat(cmd->pool, a2+4, NDBM_FILE_SUFFIX, NULL);
  484. #else
  485.         return ap_pstrdup(cmd->pool, "RewriteMap: cannot use NDBM mapfile, "
  486.                           "because no NDBM support is compiled in");
  487. #endif
  488.     }
  489.     else if (strncmp(a2, "prg:", 4) == 0) {
  490.         new->type = MAPTYPE_PRG;
  491.         new->datafile = a2+4;
  492.         new->checkfile = a2+4;
  493.     }
  494.     else if (strncmp(a2, "int:", 4) == 0) {
  495.         new->type      = MAPTYPE_INT;
  496.         new->datafile  = NULL;
  497.         new->checkfile = NULL;
  498.         if (strcmp(a2+4, "tolower") == 0) {
  499.             new->func = rewrite_mapfunc_tolower;
  500.         }
  501.         else if (strcmp(a2+4, "toupper") == 0) {
  502.             new->func = rewrite_mapfunc_toupper;
  503.         }
  504.         else if (strcmp(a2+4, "escape") == 0) {
  505.             new->func = rewrite_mapfunc_escape;
  506.         }
  507.         else if (strcmp(a2+4, "unescape") == 0) {
  508.             new->func = rewrite_mapfunc_unescape;
  509.         }
  510.         else if (sconf->state == ENGINE_ENABLED) {
  511.             return ap_pstrcat(cmd->pool, "RewriteMap: internal map not found:",
  512.                               a2+4, NULL);
  513.         }
  514.     }
  515.     else {
  516.         new->type      = MAPTYPE_TXT;
  517.         new->datafile  = a2;
  518.         new->checkfile = a2;
  519.     }
  520.     new->fpin  = -1;
  521.     new->fpout = -1;
  522.  
  523.     if (new->checkfile && (sconf->state == ENGINE_ENABLED)
  524.         && (stat(new->checkfile, &st) == -1)) {
  525.         return ap_pstrcat(cmd->pool,
  526.                           "RewriteMap: map file or program not found:",
  527.                           new->checkfile, NULL);
  528.     }
  529.  
  530.     return NULL;
  531. }
  532.  
  533. static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, char *a1)
  534. {
  535.     rewrite_server_conf *sconf;
  536.  
  537.     sconf = (rewrite_server_conf *)
  538.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  539.  
  540.     sconf->rewritelockfile = a1;
  541.  
  542.     return NULL;
  543. }
  544.  
  545. static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf,
  546.                                    char *a1)
  547. {
  548.     if (cmd->path == NULL || dconf == NULL) {
  549.         return "RewriteBase: only valid in per-directory config files";
  550.     }
  551.     if (a1[0] == '\0') {
  552.         return "RewriteBase: empty URL not allowed";
  553.     }
  554.     if (a1[0] != '/') {
  555.         return "RewriteBase: argument is not a valid URL";
  556.     }
  557.  
  558.     dconf->baseurl = a1;
  559.  
  560.     return NULL;
  561. }
  562.  
  563. static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf,
  564.                                    char *str)
  565. {
  566.     rewrite_server_conf *sconf;
  567.     rewritecond_entry *new;
  568.     regex_t *regexp;
  569.     char *a1;
  570.     char *a2;
  571.     char *a3;
  572.     char *cp;
  573.     const char *err;
  574.     int rc;
  575.  
  576.     sconf = (rewrite_server_conf *)
  577.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  578.  
  579.     /*  make a new entry in the internal temporary rewrite rule list */
  580.     if (cmd->path == NULL) {   /* is server command */
  581.         new = ap_push_array(sconf->rewriteconds);
  582.     }
  583.     else {                     /* is per-directory command */
  584.         new = ap_push_array(dconf->rewriteconds);
  585.     }
  586.  
  587.     /*  parse the argument line ourself */
  588.     if (parseargline(str, &a1, &a2, &a3)) {
  589.         return ap_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str,
  590.                           "'\n", NULL);
  591.     }
  592.  
  593.     /*  arg1: the input string */
  594.     new->input = ap_pstrdup(cmd->pool, a1);
  595.  
  596.     /* arg3: optional flags field
  597.        (this have to be first parsed, because we need to
  598.         know if the regex should be compiled with ICASE!) */
  599.     new->flags = CONDFLAG_NONE;
  600.     if (a3 != NULL) {
  601.         if ((err = cmd_rewritecond_parseflagfield(cmd->pool, new,
  602.                                                   a3)) != NULL) {
  603.             return err;
  604.         }
  605.     }
  606.  
  607.     /*  arg2: the pattern
  608.         try to compile the regexp to test if is ok */
  609.     cp = a2;
  610.     if (cp[0] == '!') {
  611.         new->flags |= CONDFLAG_NOTMATCH;
  612.         cp++;
  613.     }
  614.  
  615.     /* now be careful: Under the POSIX regex library
  616.        we can compile the pattern for case-insensitive matching,
  617.        under the old V8 library we have to do it self via a hack */
  618.     if (new->flags & CONDFLAG_NOCASE) {
  619.         rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED|REG_ICASE))
  620.               == NULL);
  621.     }
  622.     else {
  623.         rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL);
  624.     }
  625.     if (rc) {
  626.         return ap_pstrcat(cmd->pool,
  627.                           "RewriteCond: cannot compile regular expression '",
  628.                           a2, "'\n", NULL);
  629.     }
  630.  
  631.     new->pattern = ap_pstrdup(cmd->pool, cp);
  632.     new->regexp  = regexp;
  633.  
  634.     return NULL;
  635. }
  636.  
  637. static const char *cmd_rewritecond_parseflagfield(pool *p,
  638.                                                   rewritecond_entry *cfg,
  639.                                                   char *str)
  640. {
  641.     char *cp;
  642.     char *cp1;
  643.     char *cp2;
  644.     char *cp3;
  645.     char *key;
  646.     char *val;
  647.     const char *err;
  648.  
  649.     if (str[0] != '[' || str[strlen(str)-1] != ']') {
  650.         return "RewriteCond: bad flag delimiters";
  651.     }
  652.  
  653.     cp = str+1;
  654.     str[strlen(str)-1] = ','; /* for simpler parsing */
  655.     for ( ; *cp != '\0'; ) {
  656.         /* skip whitespaces */
  657.         for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++)
  658.             ;
  659.         if (*cp == '\0') {
  660.             break;
  661.         }
  662.         cp1 = cp;
  663.         if ((cp2 = strchr(cp, ',')) != NULL) {
  664.             cp = cp2+1;
  665.             for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--)
  666.                 ;
  667.             *cp2 = '\0';
  668.             if ((cp3 = strchr(cp1, '=')) != NULL) {
  669.                 *cp3 = '\0';
  670.                 key = cp1;
  671.                 val = cp3+1;
  672.             }
  673.             else {
  674.                 key = cp1;
  675.                 val = "";
  676.             }
  677.             if ((err = cmd_rewritecond_setflag(p, cfg, key, val)) != NULL) {
  678.                 return err;
  679.             }
  680.         }
  681.         else {
  682.             break;
  683.         }
  684.     }
  685.  
  686.     return NULL;
  687. }
  688.  
  689. static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg,
  690.                                            char *key, char *val)
  691. {
  692.     if (   strcasecmp(key, "nocase") == 0
  693.         || strcasecmp(key, "NC") == 0    ) {
  694.         cfg->flags |= CONDFLAG_NOCASE;
  695.     }
  696.     else if (   strcasecmp(key, "ornext") == 0
  697.              || strcasecmp(key, "OR") == 0    ) {
  698.         cfg->flags |= CONDFLAG_ORNEXT;
  699.     }
  700.     else {
  701.         return ap_pstrcat(p, "RewriteCond: unknown flag '", key, "'\n", NULL);
  702.     }
  703.     return NULL;
  704. }
  705.  
  706. static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
  707.                                    char *str)
  708. {
  709.     rewrite_server_conf *sconf;
  710.     rewriterule_entry *new;
  711.     regex_t *regexp;
  712.     char *a1;
  713.     char *a2;
  714.     char *a3;
  715.     char *cp;
  716.     const char *err;
  717.     int mode;
  718.  
  719.     sconf = (rewrite_server_conf *)
  720.             ap_get_module_config(cmd->server->module_config, &rewrite_module);
  721.  
  722.     /*  make a new entry in the internal rewrite rule list */
  723.     if (cmd->path == NULL) {   /* is server command */
  724.         new = ap_push_array(sconf->rewriterules);
  725.     }
  726.     else {                     /* is per-directory command */
  727.         new = ap_push_array(dconf->rewriterules);
  728.     }
  729.  
  730.     /*  parse the argument line ourself */
  731.     if (parseargline(str, &a1, &a2, &a3)) {
  732.         return ap_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str,
  733.                           "'\n", NULL);
  734.     }
  735.  
  736.     /* arg3: optional flags field */
  737.     new->forced_mimetype     = NULL;
  738.     new->forced_responsecode = HTTP_MOVED_TEMPORARILY;
  739.     new->flags  = RULEFLAG_NONE;
  740.     new->env[0] = NULL;
  741.     new->skip   = 0;
  742.     if (a3 != NULL) {
  743.         if ((err = cmd_rewriterule_parseflagfield(cmd->pool, new,
  744.                                                   a3)) != NULL) {
  745.             return err;
  746.         }
  747.     }
  748.  
  749.     /*  arg1: the pattern
  750.      *  try to compile the regexp to test if is ok
  751.      */
  752.     cp = a1;
  753.     if (cp[0] == '!') {
  754.         new->flags |= RULEFLAG_NOTMATCH;
  755.         cp++;
  756.     }
  757.     mode = REG_EXTENDED;
  758.     if (new->flags & RULEFLAG_NOCASE) {
  759.         mode |= REG_ICASE;
  760.     }
  761.     if ((regexp = ap_pregcomp(cmd->pool, cp, mode)) == NULL) {
  762.         return ap_pstrcat(cmd->pool,
  763.                           "RewriteRule: cannot compile regular expression '",
  764.                           a1, "'\n", NULL);
  765.     }
  766.     new->pattern = ap_pstrdup(cmd->pool, cp);
  767.     new->regexp  = regexp;
  768.  
  769.     /*  arg2: the output string
  770.      *  replace the $<N> by \<n> which is needed by the currently
  771.      *  used Regular Expression library
  772.      */
  773.     new->output = ap_pstrdup(cmd->pool, a2);
  774.  
  775.     /* now, if the server or per-dir config holds an
  776.      * array of RewriteCond entries, we take it for us
  777.      * and clear the array
  778.      */
  779.     if (cmd->path == NULL) {  /* is server command */
  780.         new->rewriteconds   = sconf->rewriteconds;
  781.         sconf->rewriteconds = ap_make_array(cmd->pool, 2,
  782.                                             sizeof(rewritecond_entry));
  783.     }
  784.     else {                    /* is per-directory command */
  785.         new->rewriteconds   = dconf->rewriteconds;
  786.         dconf->rewriteconds = ap_make_array(cmd->pool, 2,
  787.                                             sizeof(rewritecond_entry));
  788.     }
  789.  
  790.     return NULL;
  791. }
  792.  
  793. static const char *cmd_rewriterule_parseflagfield(pool *p,
  794.                                                   rewriterule_entry *cfg,
  795.                                                   char *str)
  796. {
  797.     char *cp;
  798.     char *cp1;
  799.     char *cp2;
  800.     char *cp3;
  801.     char *key;
  802.     char *val;
  803.     const char *err;
  804.  
  805.     if (str[0] != '[' || str[strlen(str)-1] != ']') {
  806.         return "RewriteRule: bad flag delimiters";
  807.     }
  808.  
  809.     cp = str+1;
  810.     str[strlen(str)-1] = ','; /* for simpler parsing */
  811.     for ( ; *cp != '\0'; ) {
  812.         /* skip whitespaces */
  813.         for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++)
  814.             ;
  815.         if (*cp == '\0') {
  816.             break;
  817.         }
  818.         cp1 = cp;
  819.         if ((cp2 = strchr(cp, ',')) != NULL) {
  820.             cp = cp2+1;
  821.             for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--)
  822.                 ;
  823.             *cp2 = '\0';
  824.             if ((cp3 = strchr(cp1, '=')) != NULL) {
  825.                 *cp3 = '\0';
  826.                 key = cp1;
  827.                 val = cp3+1;
  828.             }
  829.             else {
  830.                 key = cp1;
  831.                 val = "";
  832.             }
  833.             if ((err = cmd_rewriterule_setflag(p, cfg, key, val)) != NULL) {
  834.                 return err;
  835.             }
  836.         }
  837.         else {
  838.             break;
  839.         }
  840.     }
  841.  
  842.     return NULL;
  843. }
  844.  
  845. static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg,
  846.                                            char *key, char *val)
  847. {
  848.     int status = 0;
  849.     int i;
  850.  
  851.     if (   strcasecmp(key, "redirect") == 0
  852.         || strcasecmp(key, "R") == 0       ) {
  853.         cfg->flags |= RULEFLAG_FORCEREDIRECT;
  854.         if (strlen(val) > 0) {
  855.             if (strcasecmp(val, "permanent") == 0) {
  856.                 status = HTTP_MOVED_PERMANENTLY;
  857.             }
  858.             else if (strcasecmp(val, "temp") == 0) {
  859.                 status = HTTP_MOVED_TEMPORARILY;
  860.             }
  861.             else if (strcasecmp(val, "seeother") == 0) {
  862.                 status = HTTP_SEE_OTHER;
  863.             }
  864.             else if (ap_isdigit(*val)) {
  865.                 status = atoi(val);
  866.             }
  867.             if (!ap_is_HTTP_REDIRECT(status)) {
  868.                 return "RewriteRule: invalid HTTP response code "
  869.                        "for flag 'R'";
  870.             }
  871.             cfg->forced_responsecode = status;
  872.         }
  873.     }
  874.     else if (   strcasecmp(key, "last") == 0
  875.              || strcasecmp(key, "L") == 0   ) {
  876.         cfg->flags |= RULEFLAG_LASTRULE;
  877.     }
  878.     else if (   strcasecmp(key, "next") == 0
  879.              || strcasecmp(key, "N") == 0   ) {
  880.         cfg->flags |= RULEFLAG_NEWROUND;
  881.     }
  882.     else if (   strcasecmp(key, "chain") == 0
  883.              || strcasecmp(key, "C") == 0    ) {
  884.         cfg->flags |= RULEFLAG_CHAIN;
  885.     }
  886.     else if (   strcasecmp(key, "type") == 0
  887.              || strcasecmp(key, "T") == 0   ) {
  888.         cfg->forced_mimetype = ap_pstrdup(p, val);
  889.         ap_str_tolower(cfg->forced_mimetype);
  890.     }
  891.     else if (   strcasecmp(key, "env") == 0
  892.              || strcasecmp(key, "E") == 0   ) {
  893.         for (i = 0; (cfg->env[i] != NULL) && (i < MAX_ENV_FLAGS); i++)
  894.             ;
  895.         if (i < MAX_ENV_FLAGS) {
  896.             cfg->env[i] = ap_pstrdup(p, val);
  897.             cfg->env[i+1] = NULL;
  898.         }
  899.         else {
  900.             return "RewriteRule: too many environment flags 'E'";
  901.         }
  902.     }
  903.     else if (   strcasecmp(key, "nosubreq") == 0
  904.              || strcasecmp(key, "NS") == 0      ) {
  905.         cfg->flags |= RULEFLAG_IGNOREONSUBREQ;
  906.     }
  907.     else if (   strcasecmp(key, "proxy") == 0
  908.              || strcasecmp(key, "P") == 0      ) {
  909.         cfg->flags |= RULEFLAG_PROXY;
  910.     }
  911.     else if (   strcasecmp(key, "passthrough") == 0
  912.              || strcasecmp(key, "PT") == 0      ) {
  913.         cfg->flags |= RULEFLAG_PASSTHROUGH;
  914.     }
  915.     else if (   strcasecmp(key, "skip") == 0
  916.              || strcasecmp(key, "S") == 0   ) {
  917.         cfg->skip = atoi(val);
  918.     }
  919.     else if (   strcasecmp(key, "forbidden") == 0
  920.              || strcasecmp(key, "F") == 0   ) {
  921.         cfg->flags |= RULEFLAG_FORBIDDEN;
  922.     }
  923.     else if (   strcasecmp(key, "gone") == 0
  924.              || strcasecmp(key, "G") == 0   ) {
  925.         cfg->flags |= RULEFLAG_GONE;
  926.     }
  927.     else if (   strcasecmp(key, "qsappend") == 0
  928.              || strcasecmp(key, "QSA") == 0   ) {
  929.         cfg->flags |= RULEFLAG_QSAPPEND;
  930.     }
  931.     else if (   strcasecmp(key, "nocase") == 0
  932.              || strcasecmp(key, "NC") == 0    ) {
  933.         cfg->flags |= RULEFLAG_NOCASE;
  934.     }
  935.     else {
  936.         return ap_pstrcat(p, "RewriteRule: unknown flag '", key, "'\n", NULL);
  937.     }
  938.     return NULL;
  939. }
  940.  
  941.  
  942. /*
  943. **
  944. **  Global Module Initialization
  945. **  [called from read_config() after all
  946. **  config commands were already called]
  947. **
  948. */
  949.  
  950. static void init_module(server_rec *s, pool *p)
  951. {
  952.     /* check if proxy module is available */
  953.     proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL);
  954.  
  955.     /* precompile a static pattern
  956.        for the txt mapfile parsing */
  957.     lookup_map_txtfile_regexp = ap_pregcomp(p, MAPFILE_PATTERN, REG_EXTENDED);
  958.  
  959.     /* create the rewriting lockfile in the parent */
  960.     rewritelock_create(s, p);
  961.     ap_register_cleanup(p, (void *)s, rewritelock_remove, ap_null_cleanup);
  962.  
  963.     /* step through the servers and
  964.      * - open each rewriting logfile
  965.      * - open the RewriteMap prg:xxx programs
  966.      */
  967.     for (; s; s = s->next) {
  968.         open_rewritelog(s, p);
  969.         run_rewritemap_programs(s, p);
  970.     }
  971. }
  972.  
  973.  
  974. /*
  975. **
  976. **  Per-Child Module Initialization
  977. **  [called after a child process is spawned]
  978. **
  979. */
  980.  
  981. static void init_child(server_rec *s, pool *p)
  982. {
  983.      /* open the rewriting lockfile */
  984.      rewritelock_open(s, p);
  985.  
  986.      /* create the lookup cache */
  987.      cachep = init_cache(p);
  988. }
  989.  
  990.  
  991. /*
  992. ** +-------------------------------------------------------+
  993. ** |                                                       |
  994. ** |                     runtime hooks
  995. ** |                                                       |
  996. ** +-------------------------------------------------------+
  997. */
  998.  
  999. /*
  1000. **
  1001. **  URI-to-filename hook
  1002. **
  1003. **  [used for the rewriting engine triggered by
  1004. **  the per-server 'RewriteRule' directives]
  1005. **
  1006. */
  1007.  
  1008. static int hook_uri2file(request_rec *r)
  1009. {
  1010.     void *sconf;
  1011.     rewrite_server_conf *conf;
  1012.     const char *var;
  1013.     const char *thisserver;
  1014.     char *thisport;
  1015.     const char *thisurl;
  1016.     char buf[512];
  1017.     char docroot[512];
  1018.     char *cp, *cp2;
  1019.     const char *ccp;
  1020.     struct stat finfo;
  1021.     unsigned int port;
  1022.     int n;
  1023.     int l;
  1024.  
  1025.     /*
  1026.      *  retrieve the config structures
  1027.      */
  1028.     sconf = r->server->module_config;
  1029.     conf  = (rewrite_server_conf *)ap_get_module_config(sconf,
  1030.                                                         &rewrite_module);
  1031.  
  1032.     /*
  1033.      *  only do something under runtime if the engine is really enabled,
  1034.      *  else return immediately!
  1035.      */
  1036.     if (conf->state == ENGINE_DISABLED) {
  1037.         return DECLINED;
  1038.     }
  1039.  
  1040.     /*
  1041.      *  check for the ugly API case of a virtual host section where no
  1042.      *  mod_rewrite directives exists. In this situation we became no chance
  1043.      *  by the API to setup our default per-server config so we have to
  1044.      *  on-the-fly assume we have the default config. But because the default
  1045.      *  config has a disabled rewriting engine we are lucky because can
  1046.      *  just stop operating now.
  1047.      */
  1048.     if (conf->server != r->server) {
  1049.         return DECLINED;
  1050.     }
  1051.  
  1052.     /*
  1053.      *  add the SCRIPT_URL variable to the env. this is a bit complicated
  1054.      *  due to the fact that apache uses subrequests and internal redirects
  1055.      */
  1056.  
  1057.     if (r->main == NULL) {
  1058.          var = ap_pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL);
  1059.          var = ap_table_get(r->subprocess_env, var);
  1060.          if (var == NULL) {
  1061.              ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
  1062.          }
  1063.          else {
  1064.              ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
  1065.          }
  1066.     }
  1067.     else {
  1068.          var = ap_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
  1069.          ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
  1070.     }
  1071.  
  1072.     /*
  1073.      *  create the SCRIPT_URI variable for the env
  1074.      */
  1075.  
  1076.     /* add the canonical URI of this URL */
  1077.     thisserver = ap_get_server_name(r);
  1078.     port = ap_get_server_port(r);
  1079.     if (ap_is_default_port(port, r)) {
  1080.         thisport = "";
  1081.     }
  1082.     else {
  1083.         ap_snprintf(buf, sizeof(buf), ":%u", port);
  1084.         thisport = buf;
  1085.     }
  1086.     thisurl = ap_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL);
  1087.  
  1088.     /* set the variable */
  1089.     var = ap_pstrcat(r->pool, ap_http_method(r), "://", thisserver, thisport,
  1090.                      thisurl, NULL);
  1091.     ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
  1092.  
  1093.     /* if filename was not initially set,
  1094.      * we start with the requested URI
  1095.      */
  1096.     if (r->filename == NULL) {
  1097.         r->filename = ap_pstrdup(r->pool, r->uri);
  1098.         rewritelog(r, 2, "init rewrite engine with requested uri %s",
  1099.                    r->filename);
  1100.     }
  1101.  
  1102.     /*
  1103.      *  now apply the rules ...
  1104.      */
  1105.     if (apply_rewrite_list(r, conf->rewriterules, NULL)) {
  1106.  
  1107.         if (strlen(r->filename) > 6 &&
  1108.             strncmp(r->filename, "proxy:", 6) == 0) {
  1109.             /* it should be go on as an internal proxy request */
  1110.  
  1111.             /* check if the proxy module is enabled, so
  1112.              * we can actually use it!
  1113.              */
  1114.             if (!proxy_available) {
  1115.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1116.                              "attempt to make remote request from mod_rewrite "
  1117.                              "without proxy enabled: %s", r->filename);
  1118.                 return FORBIDDEN;
  1119.             }
  1120.  
  1121.             /* make sure the QUERY_STRING and
  1122.              * PATH_INFO parts get incorporated
  1123.              */
  1124.             if (r->path_info != NULL) {
  1125.                 r->filename = ap_pstrcat(r->pool, r->filename,
  1126.                                          r->path_info, NULL);
  1127.             }
  1128.             if (r->args != NULL &&
  1129.                 r->uri == r->unparsed_uri) {
  1130.                 /* see proxy_http:proxy_http_canon() */
  1131.                 r->filename = ap_pstrcat(r->pool, r->filename,
  1132.                                          "?", r->args, NULL);
  1133.             }
  1134.  
  1135.             /* now make sure the request gets handled by the proxy handler */
  1136.             r->proxyreq = 1;
  1137.             r->handler  = "proxy-server";
  1138.  
  1139.             rewritelog(r, 1, "go-ahead with proxy request %s [OK]",
  1140.                        r->filename);
  1141.             return OK;
  1142.         }
  1143.         else if (  (strlen(r->filename) > 7 &&
  1144.                     strncasecmp(r->filename, "http://", 7) == 0)
  1145.                 || (strlen(r->filename) > 8 &&
  1146.                     strncasecmp(r->filename, "https://", 8) == 0)
  1147.                 || (strlen(r->filename) > 9 &&
  1148.                     strncasecmp(r->filename, "gopher://", 9) == 0)
  1149.                 || (strlen(r->filename) > 6 &&
  1150.                     strncasecmp(r->filename, "ftp://", 6) == 0)    ) {
  1151.             /* it was finally rewritten to a remote URL */
  1152.  
  1153.             /* skip 'scheme:' */
  1154.             for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
  1155.                 ;
  1156.             /* skip '://' */
  1157.             cp += 3;
  1158.             /* skip host part */
  1159.             for ( ; *cp != '/' && *cp != '\0'; cp++)
  1160.                 ;
  1161.             if (*cp != '\0') {
  1162.                 rewritelog(r, 1, "escaping %s for redirect", r->filename);
  1163.                 cp2 = ap_escape_uri(r->pool, cp);
  1164.                 *cp = '\0';
  1165.                 r->filename = ap_pstrcat(r->pool, r->filename, cp2, NULL);
  1166.             }
  1167.  
  1168.             /* append the QUERY_STRING part */
  1169.             if (r->args != NULL) {
  1170.                r->filename = ap_pstrcat(r->pool, r->filename,
  1171.                                         "?", r->args, NULL);
  1172.             }
  1173.  
  1174.             /* determine HTTP redirect response code */
  1175.             if (ap_is_HTTP_REDIRECT(r->status)) {
  1176.                 n = r->status;
  1177.                 r->status = HTTP_OK; /* make Apache kernel happy */
  1178.             }
  1179.             else {
  1180.                 n = REDIRECT;
  1181.             }
  1182.  
  1183.             /* now do the redirection */
  1184.             ap_table_setn(r->headers_out, "Location", r->filename);
  1185.             rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n);
  1186.             return n;
  1187.         }
  1188.         else if (strlen(r->filename) > 10 &&
  1189.                  strncmp(r->filename, "forbidden:", 10) == 0) {
  1190.             /* This URLs is forced to be forbidden for the requester */
  1191.             return FORBIDDEN;
  1192.         }
  1193.         else if (strlen(r->filename) > 5 &&
  1194.                  strncmp(r->filename, "gone:", 5) == 0) {
  1195.             /* This URLs is forced to be gone */
  1196.             return HTTP_GONE;
  1197.         }
  1198.         else if (strlen(r->filename) > 12 &&
  1199.                  strncmp(r->filename, "passthrough:", 12) == 0) {
  1200.             /*
  1201.              * Hack because of underpowered API: passing the current
  1202.              * rewritten filename through to other URL-to-filename handlers
  1203.              * just as it were the requested URL. This is to enable
  1204.              * post-processing by mod_alias, etc.  which always act on
  1205.              * r->uri! The difference here is: We do not try to
  1206.              * add the document root
  1207.              */
  1208.             r->uri = ap_pstrdup(r->pool, r->filename+12);
  1209.             return DECLINED;
  1210.         }
  1211.         else {
  1212.             /* it was finally rewritten to a local path */
  1213.  
  1214.             /* expand "/~user" prefix */
  1215. #ifndef WIN32
  1216.             r->filename = expand_tildepaths(r, r->filename);
  1217. #endif
  1218.             rewritelog(r, 2, "local path result: %s", r->filename);
  1219.  
  1220.             /* the filename has to start with a slash! */
  1221.             if (r->filename[0] != '/') {
  1222.                 return BAD_REQUEST;
  1223.             }
  1224.  
  1225.             /* if there is no valid prefix, we have
  1226.              * to emulate the translator from the core and
  1227.              * prefix the filename with document_root
  1228.              *
  1229.              * NOTICE:
  1230.              * We cannot leave out the prefix_stat because
  1231.              * - when we always prefix with document_root
  1232.              *   then no absolute path can be created, e.g. via
  1233.              *   emulating a ScriptAlias directive, etc.
  1234.              * - when we always NOT prefix with document_root
  1235.              *   then the files under document_root have to
  1236.              *   be references directly and document_root
  1237.              *   gets never used and will be a dummy parameter -
  1238.              *   this is also bad
  1239.              *
  1240.              * BUT:
  1241.              * Under real Unix systems this is no problem,
  1242.              * because we only do stat() on the first directory
  1243.              * and this gets cached by the kernel for along time!
  1244.              */
  1245.             n = prefix_stat(r->filename, &finfo);
  1246.             if (n == 0) {
  1247.                 if ((ccp = ap_document_root(r)) != NULL) {
  1248.                     l = ap_cpystrn(docroot, ccp, sizeof(docroot)) - docroot;
  1249.  
  1250.                     /* always NOT have a trailing slash */
  1251.                     if (docroot[l-1] == '/') {
  1252.                         docroot[l-1] = '\0';
  1253.                     }
  1254.                     if (r->server->path
  1255.                         && !strncmp(r->filename, r->server->path,
  1256.                                     r->server->pathlen)) {
  1257.                         r->filename = ap_pstrcat(r->pool, docroot,
  1258.                                                  (r->filename +
  1259.                                                   r->server->pathlen), NULL);
  1260.                     }
  1261.                     else {
  1262.                         r->filename = ap_pstrcat(r->pool, docroot, 
  1263.                                                  r->filename, NULL);
  1264.                     }
  1265.                     rewritelog(r, 2, "prefixed with document_root to %s",
  1266.                                r->filename);
  1267.                 }
  1268.             }
  1269.  
  1270.             rewritelog(r, 1, "go-ahead with %s [OK]", r->filename);
  1271.             return OK;
  1272.         }
  1273.     }
  1274.     else {
  1275.         rewritelog(r, 1, "pass through %s", r->filename);
  1276.         return DECLINED;
  1277.     }
  1278. }
  1279.  
  1280.  
  1281. /*
  1282. **
  1283. **  MIME-type hook
  1284. **
  1285. **  [used to support the forced-MIME-type feature]
  1286. **
  1287. */
  1288.  
  1289. static int hook_mimetype(request_rec *r)
  1290. {
  1291.     const char *t;
  1292.  
  1293.     /* now check if we have to force a MIME-type */
  1294.     t = ap_table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR);
  1295.     if (t == NULL) {
  1296.         return DECLINED;
  1297.     }
  1298.     else {
  1299.         rewritelog(r, 1, "force filename %s to have MIME-type '%s'",
  1300.                    r->filename, t);
  1301.         r->content_type = t;
  1302.         return OK;
  1303.     }
  1304. }
  1305.  
  1306.  
  1307. /*
  1308. **
  1309. **  Fixup hook
  1310. **
  1311. **  [used for the rewriting engine triggered by
  1312. **  the per-directory 'RewriteRule' directives]
  1313. **
  1314. */
  1315.  
  1316. static int hook_fixup(request_rec *r)
  1317. {
  1318.     rewrite_perdir_conf *dconf;
  1319.     char *cp;
  1320.     char *cp2;
  1321.     const char *ccp;
  1322.     char *prefix;
  1323.     int l;
  1324.     int n;
  1325.     char *ofilename;
  1326.  
  1327.     dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config,
  1328.                                                         &rewrite_module);
  1329.  
  1330.     /* if there is no per-dir config we return immediately */
  1331.     if (dconf == NULL) {
  1332.         return DECLINED;
  1333.     }
  1334.  
  1335.     /* we shouldn't do anything in subrequests */
  1336.     if (r->main != NULL) {
  1337.         return DECLINED;
  1338.     }
  1339.  
  1340.     /* if there are no real (i.e. no RewriteRule directives!)
  1341.        per-dir config of us, we return also immediately */
  1342.     if (dconf->directory == NULL) {
  1343.         return DECLINED;
  1344.     }
  1345.  
  1346.     /*
  1347.      *  only do something under runtime if the engine is really enabled,
  1348.      *  for this directory, else return immediately!
  1349.      */
  1350.     if (!(ap_allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) {
  1351.         /* FollowSymLinks is mandatory! */
  1352.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1353.                      "Options FollowSymLinks or SymLinksIfOwnerMatch is off "
  1354.                      "which implies that RewriteRule directive is forbidden: "
  1355.                      "%s", r->filename);
  1356.         return FORBIDDEN;
  1357.     }
  1358.     else {
  1359.         /* FollowSymLinks is given, but the user can
  1360.          * still turn off the rewriting engine
  1361.          */
  1362.         if (dconf->state == ENGINE_DISABLED) {
  1363.             return DECLINED;
  1364.         }
  1365.     }
  1366.  
  1367.     /*
  1368.      *  remember the current filename before rewriting for later check
  1369.      *  to prevent deadlooping because of internal redirects
  1370.      *  on final URL/filename which can be equal to the inital one.
  1371.      */
  1372.     ofilename = r->filename;
  1373.  
  1374.     /*
  1375.      *  now apply the rules ...
  1376.      */
  1377.     if (apply_rewrite_list(r, dconf->rewriterules, dconf->directory)) {
  1378.  
  1379.         if (strlen(r->filename) > 6 &&
  1380.             strncmp(r->filename, "proxy:", 6) == 0) {
  1381.             /* it should go on as an internal proxy request */
  1382.  
  1383.             /* make sure the QUERY_STRING and
  1384.              * PATH_INFO parts get incorporated
  1385.              * (r->path_info was already appended by the
  1386.              * rewriting engine because of the per-dir context!)
  1387.              */
  1388.             if (r->args != NULL
  1389.                 && r->uri == r->unparsed_uri) {
  1390.                 /* see proxy_http:proxy_http_canon() */
  1391.                 r->filename = ap_pstrcat(r->pool, r->filename,
  1392.                                          "?", r->args, NULL);
  1393.             }
  1394.  
  1395.             /* now make sure the request gets handled by the proxy handler */
  1396.             r->proxyreq = 1;
  1397.             r->handler  = "proxy-server";
  1398.  
  1399.             rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request "
  1400.                        "%s [OK]", dconf->directory, r->filename);
  1401.             return OK;
  1402.         }
  1403.         else if (  (strlen(r->filename) > 7 &&
  1404.                     strncmp(r->filename, "http://", 7) == 0)
  1405.                 || (strlen(r->filename) > 8 &&
  1406.                     strncmp(r->filename, "https://", 8) == 0)
  1407.                 || (strlen(r->filename) > 9 &&
  1408.                     strncmp(r->filename, "gopher://", 9) == 0)
  1409.                 || (strlen(r->filename) > 6 &&
  1410.                     strncmp(r->filename, "ftp://", 6) == 0)    ) {
  1411.             /* it was finally rewritten to a remote URL */
  1412.  
  1413.             /* because we are in a per-dir context
  1414.              * first try to replace the directory with its base-URL
  1415.              * if there is a base-URL available
  1416.              */
  1417.             if (dconf->baseurl != NULL) {
  1418.                 /* skip 'scheme:' */
  1419.                 for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
  1420.                     ;
  1421.                 /* skip '://' */
  1422.                 cp += 3;
  1423.                 if ((cp = strchr(cp, '/')) != NULL) {
  1424.                     rewritelog(r, 2,
  1425.                                "[per-dir %s] trying to replace "
  1426.                                "prefix %s with %s",
  1427.                                dconf->directory, dconf->directory,
  1428.                                dconf->baseurl);
  1429.                     cp2 = subst_prefix_path(r, cp, dconf->directory,
  1430.                                             dconf->baseurl);
  1431.                     if (strcmp(cp2, cp) != 0) {
  1432.                         *cp = '\0';
  1433.                         r->filename = ap_pstrcat(r->pool, r->filename,
  1434.                                                  cp2, NULL);
  1435.                     }
  1436.                 }
  1437.             }
  1438.  
  1439.             /* now prepare the redirect... */
  1440.  
  1441.             /* skip 'scheme:' */
  1442.             for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
  1443.                 ;
  1444.             /* skip '://' */
  1445.             cp += 3;
  1446.             /* skip host part */
  1447.             for ( ; *cp != '/' && *cp != '\0'; cp++)
  1448.                 ;
  1449.             if (*cp != '\0') {
  1450.                 rewritelog(r, 1, "[per-dir %s] escaping %s for redirect",
  1451.                            dconf->directory, r->filename);
  1452.                 cp2 = ap_escape_uri(r->pool, cp);
  1453.                 *cp = '\0';
  1454.                 r->filename = ap_pstrcat(r->pool, r->filename, cp2, NULL);
  1455.             }
  1456.  
  1457.             /* append the QUERY_STRING part */
  1458.             if (r->args != NULL) {
  1459.                 r->filename = ap_pstrcat(r->pool, r->filename,
  1460.                                          "?", r->args, NULL);
  1461.             }
  1462.  
  1463.             /* determine HTTP redirect response code */
  1464.             if (ap_is_HTTP_REDIRECT(r->status)) {
  1465.                 n = r->status;
  1466.                 r->status = HTTP_OK; /* make Apache kernel happy */
  1467.             }
  1468.             else {
  1469.                 n = REDIRECT;
  1470.             }
  1471.  
  1472.             /* now do the redirection */
  1473.             ap_table_setn(r->headers_out, "Location", r->filename);
  1474.             rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]",
  1475.                        dconf->directory, r->filename, n);
  1476.             return n;
  1477.         }
  1478.         else if (strlen(r->filename) > 10 &&
  1479.                  strncmp(r->filename, "forbidden:", 10) == 0) {
  1480.             /* This URL is forced to be forbidden for the requester */
  1481.             return FORBIDDEN;
  1482.         }
  1483.         else if (strlen(r->filename) > 5 &&
  1484.                  strncmp(r->filename, "gone:", 5) == 0) {
  1485.             /* This URL is forced to be gone */
  1486.             return HTTP_GONE;
  1487.         }
  1488.         else {
  1489.             /* it was finally rewritten to a local path */
  1490.  
  1491.             /* if someone used the PASSTHROUGH flag in per-dir
  1492.              * context we just ignore it. It is only useful
  1493.              * in per-server context
  1494.              */
  1495.             if (strlen(r->filename) > 12 &&
  1496.                 strncmp(r->filename, "passthrough:", 12) == 0) {
  1497.                 r->filename = ap_pstrdup(r->pool, r->filename+12);
  1498.             }
  1499.  
  1500.             /* the filename has to start with a slash! */
  1501.             if (r->filename[0] != '/') {
  1502.                 return BAD_REQUEST;
  1503.             }
  1504.  
  1505.             /* Check for deadlooping:
  1506.              * At this point we KNOW that at least one rewriting
  1507.              * rule was applied, but when the resulting URL is
  1508.              * the same as the initial URL, we are not allowed to
  1509.              * use the following internal redirection stuff because
  1510.              * this would lead to a deadloop.
  1511.              */
  1512.             if (strcmp(r->filename, ofilename) == 0) {
  1513.                 rewritelog(r, 1, "[per-dir %s] initial URL equal rewritten "
  1514.                            "URL: %s [IGNORING REWRITE]",
  1515.                            dconf->directory, r->filename);
  1516.                 return OK;
  1517.             }
  1518.  
  1519.             /* if there is a valid base-URL then substitute
  1520.              * the per-dir prefix with this base-URL if the
  1521.              * current filename still is inside this per-dir
  1522.              * context. If not then treat the result as a
  1523.              * plain URL
  1524.              */
  1525.             if (dconf->baseurl != NULL) {
  1526.                 rewritelog(r, 2,
  1527.                            "[per-dir %s] trying to replace prefix %s with %s",
  1528.                            dconf->directory, dconf->directory, dconf->baseurl);
  1529.                 r->filename = subst_prefix_path(r, r->filename,
  1530.                                                 dconf->directory,
  1531.                                                 dconf->baseurl);
  1532.             }
  1533.             else {
  1534.                 /* if no explicit base-URL exists we assume
  1535.                  * that the directory prefix is also a valid URL
  1536.                  * for this webserver and only try to remove the
  1537.                  * document_root if it is prefix
  1538.                  */
  1539.                 if ((ccp = ap_document_root(r)) != NULL) {
  1540.                     prefix = ap_pstrdup(r->pool, ccp);
  1541.                     /* always NOT have a trailing slash */
  1542.                     l = strlen(prefix);
  1543.                     if (prefix[l-1] == '/') {
  1544.                         prefix[l-1] = '\0';
  1545.                         l--;
  1546.                     }
  1547.                     if (strncmp(r->filename, prefix, l) == 0) {
  1548.                         rewritelog(r, 2,
  1549.                                    "[per-dir %s] strip document_root "
  1550.                                    "prefix: %s -> %s",
  1551.                                    dconf->directory, r->filename,
  1552.                                    r->filename+l);
  1553.                         r->filename = ap_pstrdup(r->pool, r->filename+l);
  1554.                     }
  1555.                 }
  1556.             }
  1557.  
  1558.             /* now initiate the internal redirect */
  1559.             rewritelog(r, 1, "[per-dir %s] internal redirect with %s "
  1560.                        "[INTERNAL REDIRECT]", dconf->directory, r->filename);
  1561.             r->filename = ap_pstrcat(r->pool, "redirect:", r->filename, NULL);
  1562.             r->handler = "redirect-handler";
  1563.             return OK;
  1564.         }
  1565.     }
  1566.     else {
  1567.         rewritelog(r, 1, "[per-dir %s] pass through %s", 
  1568.                    dconf->directory, r->filename);
  1569.         return DECLINED;
  1570.     }
  1571. }
  1572.  
  1573.  
  1574. /*
  1575. **
  1576. **  Content-Handlers
  1577. **
  1578. **  [used for redirect support]
  1579. **
  1580. */
  1581.  
  1582. static int handler_redirect(request_rec *r)
  1583. {
  1584.     /* just make sure that we are really meant! */
  1585.     if (strncmp(r->filename, "redirect:", 9) != 0) {
  1586.         return DECLINED;
  1587.     }
  1588.  
  1589.     /* now do the internal redirect */
  1590.     ap_internal_redirect(ap_pstrcat(r->pool, r->filename+9,
  1591.                                     r->args ? "?" : NULL, r->args, NULL), r);
  1592.  
  1593.     /* and return gracefully */
  1594.     return OK;
  1595. }
  1596.  
  1597.  
  1598. /*
  1599. ** +-------------------------------------------------------+
  1600. ** |                                                       |
  1601. ** |                  the rewriting engine
  1602. ** |                                                       |
  1603. ** +-------------------------------------------------------+
  1604. */
  1605.  
  1606. /*
  1607.  *  Apply a complete rule set,
  1608.  *  i.e. a list of rewrite rules
  1609.  */
  1610. static int apply_rewrite_list(request_rec *r, array_header *rewriterules,
  1611.                               char *perdir)
  1612. {
  1613.     rewriterule_entry *entries;
  1614.     rewriterule_entry *p;
  1615.     int i;
  1616.     int changed;
  1617.     int rc;
  1618.     int s;
  1619.  
  1620.     /*
  1621.      *  Iterate over all existing rules
  1622.      */
  1623.     entries = (rewriterule_entry *)rewriterules->elts;
  1624.     changed = 0;
  1625.     loop:
  1626.     for (i = 0; i < rewriterules->nelts; i++) {
  1627.         p = &entries[i];
  1628.  
  1629.         /*
  1630.          *  Ignore this rule on subrequests if we are explicitly
  1631.          *  asked to do so or this is a proxy-throughput or a
  1632.          *  forced redirect rule.
  1633.          */
  1634.         if (r->main != NULL &&
  1635.             (p->flags & RULEFLAG_IGNOREONSUBREQ ||
  1636.              p->flags & RULEFLAG_PROXY          ||
  1637.              p->flags & RULEFLAG_FORCEREDIRECT    )) {
  1638.             continue;
  1639.         }
  1640.  
  1641.         /*
  1642.          *  Apply the current rule.
  1643.          */
  1644.         rc = apply_rewrite_rule(r, p, perdir);
  1645.         if (rc) {
  1646.             /*
  1647.              *  Indicate a change if this was not a match-only rule.
  1648.              */
  1649.             if (rc != 2) {
  1650.                 changed = 1;
  1651.             }
  1652.  
  1653.             /*
  1654.              *  Pass-Through Feature (`RewriteRule .. .. [PT]'):
  1655.              *  Because the Apache 1.x API is very limited we
  1656.              *  need this hack to pass the rewritten URL to other
  1657.              *  modules like mod_alias, mod_userdir, etc.
  1658.              */
  1659.             if (p->flags & RULEFLAG_PASSTHROUGH) {
  1660.                 rewritelog(r, 2, "forcing '%s' to get passed through "
  1661.                            "to next API URI-to-filename handler", r->filename);
  1662.                 r->filename = ap_pstrcat(r->pool, "passthrough:",
  1663.                                          r->filename, NULL);
  1664.                 changed = 1;
  1665.                 break;
  1666.             }
  1667.  
  1668.             /*
  1669.              *  Rule has the "forbidden" flag set which means that
  1670.              *  we stop processing and indicate this to the caller.
  1671.              */
  1672.             if (p->flags & RULEFLAG_FORBIDDEN) {
  1673.                 rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename);
  1674.                 r->filename = ap_pstrcat(r->pool, "forbidden:",
  1675.                                          r->filename, NULL);
  1676.                 changed = 1;
  1677.                 break;
  1678.             }
  1679.  
  1680.             /*
  1681.              *  Rule has the "gone" flag set which means that
  1682.              *  we stop processing and indicate this to the caller.
  1683.              */
  1684.             if (p->flags & RULEFLAG_GONE) {
  1685.                 rewritelog(r, 2, "forcing '%s' to be gone", r->filename);
  1686.                 r->filename = ap_pstrcat(r->pool, "gone:", r->filename, NULL);
  1687.                 changed = 1;
  1688.                 break;
  1689.             }
  1690.  
  1691.             /*
  1692.              *  Stop processing also on proxy pass-through and
  1693.              *  last-rule and new-round flags.
  1694.              */
  1695.             if (p->flags & RULEFLAG_PROXY) {
  1696.                 break;
  1697.             }
  1698.             if (p->flags & RULEFLAG_LASTRULE) {
  1699.                 break;
  1700.             }
  1701.  
  1702.             /*
  1703.              *  On "new-round" flag we just start from the top of
  1704.              *  the rewriting ruleset again.
  1705.              */
  1706.             if (p->flags & RULEFLAG_NEWROUND) {
  1707.                 goto loop;
  1708.             }
  1709.  
  1710.             /*
  1711.              *  If we are forced to skip N next rules, do it now.
  1712.              */
  1713.             if (p->skip > 0) {
  1714.                 s = p->skip;
  1715.                 while (   i < rewriterules->nelts
  1716.                        && s > 0) {
  1717.                     i++;
  1718.                     p = &entries[i];
  1719.                     s--;
  1720.                 }
  1721.             }
  1722.         }
  1723.         else {
  1724.             /*
  1725.              *  If current rule is chained with next rule(s),
  1726.              *  skip all this next rule(s)
  1727.              */
  1728.             while (   i < rewriterules->nelts
  1729.                    && p->flags & RULEFLAG_CHAIN) {
  1730.                 i++;
  1731.                 p = &entries[i];
  1732.             }
  1733.         }
  1734.     }
  1735.     return changed;
  1736. }
  1737.  
  1738. /*
  1739.  *  Apply a single(!) rewrite rule
  1740.  */
  1741. static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p,
  1742.                               char *perdir)
  1743. {
  1744.     char *uri;
  1745.     char *output;
  1746.     const char *vary;
  1747.     char newuri[MAX_STRING_LEN];
  1748.     char env[MAX_STRING_LEN];
  1749.     regex_t *regexp;
  1750.     regmatch_t regmatch[MAX_NMATCH];
  1751.     backrefinfo *briRR = NULL;
  1752.     backrefinfo *briRC = NULL;
  1753.     int prefixstrip;
  1754.     int failed;
  1755.     array_header *rewriteconds;
  1756.     rewritecond_entry *conds;
  1757.     rewritecond_entry *c;
  1758.     int i;
  1759.     int rc;
  1760.  
  1761.     /*
  1762.      *  Initialisation
  1763.      */
  1764.     uri     = r->filename;
  1765.     regexp  = p->regexp;
  1766.     output  = p->output;
  1767.  
  1768.     /*
  1769.      *  Add (perhaps splitted away) PATH_INFO postfix to URL to
  1770.      *  make sure we really match against the complete URL.
  1771.      */
  1772.     if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') {
  1773.         rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s",
  1774.                    perdir, uri, uri, r->path_info);
  1775.         uri = ap_pstrcat(r->pool, uri, r->path_info, NULL);
  1776.     }
  1777.  
  1778.     /*
  1779.      *  On per-directory context (.htaccess) strip the location
  1780.      *  prefix from the URL to make sure patterns apply only to
  1781.      *  the local part.  Additionally indicate this special
  1782.      *  threatment in the logfile.
  1783.      */
  1784.     prefixstrip = 0;
  1785.     if (perdir != NULL) {
  1786.         if (   strlen(uri) >= strlen(perdir)
  1787.             && strncmp(uri, perdir, strlen(perdir)) == 0) {
  1788.             rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s",
  1789.                        perdir, uri, uri+strlen(perdir));
  1790.             uri = uri+strlen(perdir);
  1791.             prefixstrip = 1;
  1792.         }
  1793.     }
  1794.  
  1795.     /*
  1796.      *  Try to match the URI against the RewriteRule pattern
  1797.      *  and exit immeddiately if it didn't apply.
  1798.      */
  1799.     if (perdir == NULL) {
  1800.         rewritelog(r, 3, "applying pattern '%s' to uri '%s'",
  1801.                    p->pattern, uri);
  1802.     }
  1803.     else {
  1804.         rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'",
  1805.                    perdir, p->pattern, uri);
  1806.     }
  1807.     rc = (regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0);
  1808.     if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
  1809.            (!rc &&  (p->flags & RULEFLAG_NOTMATCH))   ) ) {
  1810.         return 0;
  1811.     }
  1812.  
  1813.     /*
  1814.      *  Else create the RewriteRule `regsubinfo' structure which
  1815.      *  holds the substitution information.
  1816.      */
  1817.     briRR = (backrefinfo *)ap_palloc(r->pool, sizeof(backrefinfo));
  1818.     if (!rc && (p->flags & RULEFLAG_NOTMATCH)) {
  1819.         /*  empty info on negative patterns  */
  1820.         briRR->source = "";
  1821.         briRR->nsub   = 0;
  1822.     }
  1823.     else {
  1824.         briRR->source = ap_pstrdup(r->pool, uri);
  1825.         briRR->nsub   = regexp->re_nsub;
  1826.         memcpy((void *)(briRR->regmatch), (void *)(regmatch),
  1827.                sizeof(regmatch));
  1828.     }
  1829.  
  1830.     /*
  1831.      *  Initiallally create the RewriteCond backrefinfo with
  1832.      *  empty backrefinfo, i.e. not subst parts
  1833.      *  (this one is adjusted inside apply_rewrite_cond() later!!)
  1834.      */
  1835.     briRC = (backrefinfo *)ap_pcalloc(r->pool, sizeof(backrefinfo));
  1836.     briRC->source = "";
  1837.     briRC->nsub   = 0;
  1838.  
  1839.     /*
  1840.      *  Ok, we already know the pattern has matched, but we now
  1841.      *  additionally have to check for all existing preconditions
  1842.      *  (RewriteCond) which have to be also true. We do this at
  1843.      *  this very late stage to avoid unnessesary checks which
  1844.      *  would slow down the rewriting engine!!
  1845.      */
  1846.     rewriteconds = p->rewriteconds;
  1847.     conds = (rewritecond_entry *)rewriteconds->elts;
  1848.     failed = 0;
  1849.     for (i = 0; i < rewriteconds->nelts; i++) {
  1850.         c = &conds[i];
  1851.         rc = apply_rewrite_cond(r, c, perdir, briRR, briRC);
  1852.         if (c->flags & CONDFLAG_ORNEXT) {
  1853.             /*
  1854.              *  The "OR" case
  1855.              */
  1856.             if (rc == 0) {
  1857.                 /*  One condition is false, but another can be
  1858.                  *  still true, so we have to continue...
  1859.                  */
  1860.             ap_table_unset(r->notes, VARY_KEY_THIS);
  1861.                 continue;
  1862.             }
  1863.             else {
  1864.                 /*  One true condition is enough in "or" case, so
  1865.                  *  skip the other conditions which are "ornext"
  1866.                  *  chained
  1867.                  */
  1868.                 while (   i < rewriteconds->nelts
  1869.                        && c->flags & CONDFLAG_ORNEXT) {
  1870.                     i++;
  1871.                     c = &conds[i];
  1872.                 }
  1873.                 continue;
  1874.             }
  1875.         }
  1876.         else {
  1877.             /*
  1878.              *  The "AND" case, i.e. no "or" flag,
  1879.              *  so a single failure means total failure.
  1880.              */
  1881.             if (rc == 0) {
  1882.                 failed = 1;
  1883.                 break;
  1884.             }
  1885.         }
  1886.     vary = ap_table_get(r->notes, VARY_KEY_THIS);
  1887.     if (vary != NULL) {
  1888.         ap_table_merge(r->notes, VARY_KEY, vary);
  1889.         ap_table_unset(r->notes, VARY_KEY_THIS);
  1890.     }
  1891.     }
  1892.     /*  if any condition fails the complete rule fails  */
  1893.     if (failed) {
  1894.         ap_table_unset(r->notes, VARY_KEY);
  1895.         ap_table_unset(r->notes, VARY_KEY_THIS);
  1896.         return 0;
  1897.     }
  1898.  
  1899.     /*
  1900.      * Regardless of what we do next, we've found a match.  Check to see
  1901.      * if any of the request header fields were involved, and add them
  1902.      * to the Vary field of the response.
  1903.      */
  1904.     if ((vary = ap_table_get(r->notes, VARY_KEY)) != NULL) {
  1905.         ap_table_merge(r->headers_out, "Vary", vary);
  1906.     ap_table_unset(r->notes, VARY_KEY);
  1907.     }
  1908.  
  1909.     /*
  1910.      *  If this is a pure matching rule (`RewriteRule <pat> -')
  1911.      *  we stop processing and return immediately. The only thing
  1912.      *  we have not to forget are the environment variables
  1913.      *  (`RewriteRule <pat> - [E=...]')
  1914.      */
  1915.     if (strcmp(output, "-") == 0) {
  1916.         for (i = 0; p->env[i] != NULL; i++) {
  1917.             /*  1. take the string  */
  1918.             ap_cpystrn(env, p->env[i], sizeof(env));
  1919.             /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
  1920.             expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
  1921.             /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
  1922.             expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
  1923.             /*  4. expand %{...} (i.e. variables) */
  1924.             expand_variables_inbuffer(r, env, sizeof(env));
  1925.             /*  5. expand ${...} (RewriteMap lookups)  */
  1926.             expand_map_lookups(r, env, sizeof(env));
  1927.             /*  and add the variable to Apache's structures  */
  1928.             add_env_variable(r, env);
  1929.         }
  1930.         if (p->forced_mimetype != NULL) {
  1931.             if (perdir == NULL) {
  1932.                 /* In the per-server context we can force the MIME-type
  1933.                  * the correct way by notifying our MIME-type hook handler
  1934.                  * to do the job when the MIME-type API stage is reached.
  1935.                  */
  1936.                 rewritelog(r, 2, "remember %s to have MIME-type '%s'",
  1937.                            r->filename, p->forced_mimetype);
  1938.                 ap_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
  1939.                               p->forced_mimetype);
  1940.             }
  1941.             else {
  1942.                 /* In per-directory context we operate in the Fixup API hook
  1943.                  * which is after the MIME-type hook, so our MIME-type handler
  1944.                  * has no chance to set r->content_type. And because we are
  1945.                  * in the situation where no substitution takes place no
  1946.                  * sub-request will happen (which could solve the
  1947.                  * restriction). As a workaround we do it ourself now
  1948.                  * immediately although this is not strictly API-conforming.
  1949.                  * But it's the only chance we have...
  1950.                  */
  1951.                 rewritelog(r, 1, "[per-dir %s] force %s to have MIME-type "
  1952.                            "'%s'", perdir, r->filename, p->forced_mimetype);
  1953.                 r->content_type = p->forced_mimetype;
  1954.             }
  1955.         }
  1956.         return 2;
  1957.     }
  1958.  
  1959.     /*
  1960.      *  Ok, now we finally know all patterns have matched and
  1961.      *  that there is something to replace, so we create the
  1962.      *  substitution URL string in `newuri'.
  1963.      */
  1964.     /*  1. take the output string  */
  1965.     ap_cpystrn(newuri, output, sizeof(newuri));
  1966.     /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
  1967.     expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$');
  1968.     /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
  1969.     expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%');
  1970.     /*  4. expand %{...} (i.e. variables) */
  1971.     expand_variables_inbuffer(r, newuri, sizeof(newuri));
  1972.     /*  5. expand ${...} (RewriteMap lookups)  */
  1973.     expand_map_lookups(r, newuri, sizeof(newuri));
  1974.     /*  and log the result... */
  1975.     if (perdir == NULL) {
  1976.         rewritelog(r, 2, "rewrite %s -> %s", uri, newuri);
  1977.     }
  1978.     else {
  1979.         rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri);
  1980.     }
  1981.  
  1982.     /*
  1983.      *  Additionally do expansion for the environment variable
  1984.      *  strings (`RewriteRule .. .. [E=<string>]').
  1985.      */
  1986.     for (i = 0; p->env[i] != NULL; i++) {
  1987.         /*  1. take the string  */
  1988.         ap_cpystrn(env, p->env[i], sizeof(env));
  1989.         /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
  1990.         expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
  1991.         /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
  1992.         expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
  1993.         /*  4. expand %{...} (i.e. variables) */
  1994.         expand_variables_inbuffer(r, env, sizeof(env));
  1995.         /*  5. expand ${...} (RewriteMap lookups)  */
  1996.         expand_map_lookups(r, env, sizeof(env));
  1997.         /*  and add the variable to Apache's structures  */
  1998.         add_env_variable(r, env);
  1999.     }
  2000.  
  2001.     /*
  2002.      *  Now replace API's knowledge of the current URI:
  2003.      *  Replace r->filename with the new URI string and split out
  2004.      *  an on-the-fly generated QUERY_STRING part into r->args
  2005.      */
  2006.     r->filename = ap_pstrdup(r->pool, newuri);
  2007.     splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
  2008.  
  2009.     /*
  2010.      *   Again add the previously stripped per-directory location
  2011.      *   prefix if the new URI is not a new one for this
  2012.      *   location, i.e. if it's not starting with either a slash
  2013.      *   or a fully qualified URL scheme.
  2014.      */
  2015.     i = strlen(r->filename);
  2016.     if (   prefixstrip
  2017.         && !(   r->filename[0] == '/'
  2018.              || (   (i > 7 && strncasecmp(r->filename, "http://", 7) == 0)
  2019.                  || (i > 8 && strncasecmp(r->filename, "https://", 8) == 0)
  2020.                  || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0)
  2021.                  || (i > 6 && strncasecmp(r->filename, "ftp://", 6) == 0)))) {
  2022.         rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s",
  2023.                    perdir, r->filename, perdir, r->filename);
  2024.         r->filename = ap_pstrcat(r->pool, perdir, r->filename, NULL);
  2025.     }
  2026.  
  2027.     /*
  2028.      *  If this rule is forced for proxy throughput
  2029.      *  (`RewriteRule ... ... [P]') then emulate mod_proxy's
  2030.      *  URL-to-filename handler to be sure mod_proxy is triggered
  2031.      *  for this URL later in the Apache API. But make sure it is
  2032.      *  a fully-qualified URL. (If not it is qualified with
  2033.      *  ourself).
  2034.      */
  2035.     if (p->flags & RULEFLAG_PROXY) {
  2036.         fully_qualify_uri(r);
  2037.         if (perdir == NULL) {
  2038.             rewritelog(r, 2, "forcing proxy-throughput with %s", r->filename);
  2039.         }
  2040.         else {
  2041.             rewritelog(r, 2, "[per-dir %s] forcing proxy-throughput with %s",
  2042.                        perdir, r->filename);
  2043.         }
  2044.         r->filename = ap_pstrcat(r->pool, "proxy:", r->filename, NULL);
  2045.         return 1;
  2046.     }
  2047.  
  2048.     /*
  2049.      *  If this rule is explicitly forced for HTTP redirection
  2050.      *  (`RewriteRule .. .. [R]') then force an external HTTP
  2051.      *  redirect. But make sure it is a fully-qualified URL. (If
  2052.      *  not it is qualified with ourself).
  2053.      */
  2054.     if (p->flags & RULEFLAG_FORCEREDIRECT) {
  2055.         fully_qualify_uri(r);
  2056.         if (perdir == NULL) {
  2057.             rewritelog(r, 2,
  2058.                        "explicitly forcing redirect with %s", r->filename);
  2059.         }
  2060.         else {
  2061.             rewritelog(r, 2,
  2062.                        "[per-dir %s] explicitly forcing redirect with %s",
  2063.                        perdir, r->filename);
  2064.         }
  2065.         r->status = p->forced_responsecode;
  2066.         return 1;
  2067.     }
  2068.  
  2069.     /*
  2070.      *  Special Rewriting Feature: Self-Reduction
  2071.      *  We reduce the URL by stripping a possible
  2072.      *  http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which
  2073.      *  corresponds to ourself. This is to simplify rewrite maps
  2074.      *  and to avoid recursion, etc. When this prefix is not a
  2075.      *  coincidence then the user has to use [R] explicitly (see
  2076.      *  above).
  2077.      */
  2078.     reduce_uri(r);
  2079.  
  2080.     /*
  2081.      *  If this rule is still implicitly forced for HTTP
  2082.      *  redirection (`RewriteRule .. <scheme>://...') then
  2083.      *  directly force an external HTTP redirect.
  2084.      */
  2085.     i = strlen(r->filename);
  2086.     if (   (i > 7 && strncasecmp(r->filename, "http://", 7)   == 0)
  2087.         || (i > 8 && strncasecmp(r->filename, "https://", 8)  == 0)
  2088.         || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0)
  2089.         || (i > 6 && strncasecmp(r->filename, "ftp://", 6)    == 0)) {
  2090.         if (perdir == NULL) {
  2091.             rewritelog(r, 2,
  2092.                        "implicitly forcing redirect (rc=%d) with %s",
  2093.                        p->forced_responsecode, r->filename);
  2094.         }
  2095.         else {
  2096.             rewritelog(r, 2, "[per-dir %s] implicitly forcing redirect "
  2097.                        "(rc=%d) with %s", perdir, p->forced_responsecode,
  2098.                        r->filename);
  2099.         }
  2100.         r->status = p->forced_responsecode;
  2101.         return 1;
  2102.     }
  2103.  
  2104.     /*
  2105.      *  Now we are sure it is not a fully qualified URL.  But
  2106.      *  there is still one special case left: A local rewrite in
  2107.      *  per-directory context, i.e. a substitution URL which does
  2108.      *  not start with a slash. Here we add again the initially
  2109.      *  stripped per-directory prefix.
  2110.      */
  2111.     if (prefixstrip && r->filename[0] != '/') {
  2112.         rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s",
  2113.                    perdir, r->filename, perdir, r->filename);
  2114.         r->filename = ap_pstrcat(r->pool, perdir, r->filename, NULL);
  2115.     }
  2116.  
  2117.     /*
  2118.      *  Finally we had to remember if a MIME-type should be
  2119.      *  forced for this URL (`RewriteRule .. .. [T=<type>]')
  2120.      *  Later in the API processing phase this is forced by our
  2121.      *  MIME API-hook function. This time its no problem even for
  2122.      *  the per-directory context (where the MIME-type hook was
  2123.      *  already processed) because a sub-request happens ;-)
  2124.      */
  2125.     if (p->forced_mimetype != NULL) {
  2126.         ap_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
  2127.                       p->forced_mimetype);
  2128.         if (perdir == NULL) {
  2129.             rewritelog(r, 2, "remember %s to have MIME-type '%s'",
  2130.                        r->filename, p->forced_mimetype);
  2131.         }
  2132.         else {
  2133.             rewritelog(r, 2,
  2134.                        "[per-dir %s] remember %s to have MIME-type '%s'",
  2135.                        perdir, r->filename, p->forced_mimetype);
  2136.         }
  2137.     }
  2138.  
  2139.     /*
  2140.      *  Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_)
  2141.      *  But now we're done for this particular rule.
  2142.      */
  2143.     return 1;
  2144. }
  2145.  
  2146. static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p,
  2147.                               char *perdir, backrefinfo *briRR,
  2148.                               backrefinfo *briRC)
  2149. {
  2150.     char input[MAX_STRING_LEN];
  2151.     struct stat sb;
  2152.     request_rec *rsub;
  2153.     regmatch_t regmatch[MAX_NMATCH];
  2154.     int rc;
  2155.  
  2156.     /*
  2157.      *   Construct the string we match against
  2158.      */
  2159.  
  2160.     /*  1. take the string  */
  2161.     ap_cpystrn(input, p->input, sizeof(input));
  2162.     /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
  2163.     expand_backref_inbuffer(r->pool, input, sizeof(input), briRR, '$');
  2164.     /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
  2165.     expand_backref_inbuffer(r->pool, input, sizeof(input), briRC, '%');
  2166.     /*  4. expand %{...} (i.e. variables) */
  2167.     expand_variables_inbuffer(r, input, sizeof(input));
  2168.     /*  5. expand ${...} (RewriteMap lookups)  */
  2169.     expand_map_lookups(r, input, sizeof(input));
  2170.  
  2171.     /*
  2172.      *   Apply the patterns
  2173.      */
  2174.  
  2175.     rc = 0;
  2176.     if (strcmp(p->pattern, "-f") == 0) {
  2177.         if (stat(input, &sb) == 0) {
  2178.             if (S_ISREG(sb.st_mode)) {
  2179.                 rc = 1;
  2180.             }
  2181.         }
  2182.     }
  2183.     else if (strcmp(p->pattern, "-s ") == 0) {
  2184.         if (stat(input, &sb) == 0) {
  2185.             if (S_ISREG(sb.st_mode) && sb.st_size > 0) {
  2186.                 rc = 1;
  2187.             }
  2188.         }
  2189.     }
  2190.     else if (strcmp(p->pattern, "-l") == 0) {
  2191. #if !defined(OS2) && !defined(WIN32)
  2192.         if (lstat(input, &sb) == 0) {
  2193.             if (S_ISLNK(sb.st_mode)) {
  2194.                 rc = 1;
  2195.             }
  2196.         }
  2197. #endif
  2198.     }
  2199.     else if (strcmp(p->pattern, "-d") == 0) {
  2200.         if (stat(input, &sb) == 0) {
  2201.             if (S_ISDIR(sb.st_mode)) {
  2202.                 rc = 1;
  2203.             }
  2204.         }
  2205.     }
  2206.     else if (strcmp(p->pattern, "-U") == 0) {
  2207.         /* avoid infinite subrequest recursion */
  2208.         if (strlen(input) > 0               /* nonempty path, and            */
  2209.             && (   r->main == NULL          /* - either not in a subrequest  */
  2210.                 || (   r->main->uri != NULL /* - or in a subrequest...       */
  2211.                     && r->uri != NULL       /*   ...and URIs aren't NULL...  */
  2212.                                             /*   ...and sub/main URIs differ */
  2213.                     && strcmp(r->main->uri, r->uri) != 0) ) ) {
  2214.  
  2215.             /* run a URI-based subrequest */
  2216.             rsub = ap_sub_req_lookup_uri(input, r);
  2217.  
  2218.             /* URI exists for any result up to 3xx, redirects allowed */
  2219.             if (rsub->status < 400)
  2220.                 rc = 1;
  2221.  
  2222.             /* log it */
  2223.             rewritelog(r, 5, "RewriteCond URI (-U) check: "
  2224.                        "path=%s -> status=%d", input, rsub->status);
  2225.  
  2226.             /* cleanup by destroying the subrequest */
  2227.             ap_destroy_sub_req(rsub);
  2228.         }
  2229.     }
  2230.     else if (strcmp(p->pattern, "-F") == 0) {
  2231.         /* avoid infinite subrequest recursion */
  2232.         if (strlen(input) > 0               /* nonempty path, and            */
  2233.             && (   r->main == NULL          /* - either not in a subrequest  */
  2234.                 || (   r->main->uri != NULL /* - or in a subrequest...       */
  2235.                     && r->uri != NULL       /*   ...and URIs aren't NULL...  */
  2236.                                             /*   ...and sub/main URIs differ */
  2237.                     && strcmp(r->main->uri, r->uri) != 0) ) ) {
  2238.  
  2239.             /* process a file-based subrequest:
  2240.              * this differs from -U in that no path translation is done.
  2241.              */
  2242.             rsub = ap_sub_req_lookup_file(input, r);
  2243.  
  2244.             /* file exists for any result up to 2xx, no redirects */
  2245.             if (rsub->status < 300 &&
  2246.                 /* double-check that file exists since default result is 200 */
  2247.                 stat(rsub->filename, &sb) == 0) {
  2248.                 rc = 1;
  2249.             }
  2250.  
  2251.             /* log it */
  2252.             rewritelog(r, 5, "RewriteCond file (-F) check: path=%s "
  2253.                        "-> file=%s status=%d", input, rsub->filename, 
  2254.                        rsub->status);
  2255.  
  2256.             /* cleanup by destroying the subrequest */
  2257.             ap_destroy_sub_req(rsub);
  2258.         }
  2259.     }
  2260.     else if (strlen(p->pattern) > 1 && *(p->pattern) == '>') {
  2261.         rc = (compare_lexicography(input, p->pattern+1) == 1 ? 1 : 0);
  2262.     }
  2263.     else if (strlen(p->pattern) > 1 && *(p->pattern) == '<') {
  2264.         rc = (compare_lexicography(input, p->pattern+1) == -1 ? 1 : 0);
  2265.     }
  2266.     else if (strlen(p->pattern) > 1 && *(p->pattern) == '=') {
  2267.         if (strcmp(p->pattern+1, "\"\"") == 0) {
  2268.             rc = (*input == '\0');
  2269.         }
  2270.         else {
  2271.             rc = (strcmp(input, p->pattern+1) == 0 ? 1 : 0);
  2272.         }
  2273.     }
  2274.     else {
  2275.         /* it is really a regexp pattern, so apply it */
  2276.         rc = (regexec(p->regexp, input,
  2277.                       p->regexp->re_nsub+1, regmatch,0) == 0);
  2278.  
  2279.         /* if it isn't a negated pattern and really matched
  2280.            we update the passed-through regex subst info structure */
  2281.         if (rc && !(p->flags & CONDFLAG_NOTMATCH)) {
  2282.             briRC->source = ap_pstrdup(r->pool, input);
  2283.             briRC->nsub   = p->regexp->re_nsub;
  2284.             memcpy((void *)(briRC->regmatch), (void *)(regmatch),
  2285.                    sizeof(regmatch));
  2286.         }
  2287.     }
  2288.  
  2289.     /* if this is a non-matching regexp, just negate the result */
  2290.     if (p->flags & CONDFLAG_NOTMATCH) {
  2291.         rc = !rc;
  2292.     }
  2293.  
  2294.     rewritelog(r, 4, "RewriteCond: input='%s' pattern='%s%s' => %s",
  2295.                input, (p->flags & CONDFLAG_NOTMATCH ? "!" : ""),
  2296.                p->pattern, rc ? "matched" : "not-matched");
  2297.  
  2298.     /* end just return the result */
  2299.     return rc;
  2300. }
  2301.  
  2302.  
  2303. /*
  2304. ** +-------------------------------------------------------+
  2305. ** |                                                       |
  2306. ** |              URL transformation functions
  2307. ** |                                                       |
  2308. ** +-------------------------------------------------------+
  2309. */
  2310.  
  2311. /*
  2312. **
  2313. **  split out a QUERY_STRING part from
  2314. **  the current URI string
  2315. **
  2316. */
  2317.  
  2318. static void splitout_queryargs(request_rec *r, int qsappend)
  2319. {
  2320.     char *q;
  2321.     char *olduri;
  2322.  
  2323.     q = strchr(r->filename, '?');
  2324.     if (q != NULL) {
  2325.         olduri = ap_pstrdup(r->pool, r->filename);
  2326.         *q++ = '\0';
  2327.         if (qsappend) {
  2328.             r->args = ap_pstrcat(r->pool, q, "&", r->args, NULL);
  2329.         }
  2330.         else {
  2331.             r->args = ap_pstrdup(r->pool, q);
  2332.         }
  2333.         if (strlen(r->args) == 0) {
  2334.             r->args = NULL;
  2335.             rewritelog(r, 3, "split uri=%s -> uri=%s, args=<none>", olduri,
  2336.                        r->filename);
  2337.         }
  2338.         else {
  2339.             if (r->args[strlen(r->args)-1] == '&') {
  2340.                 r->args[strlen(r->args)-1] = '\0';
  2341.             }
  2342.             rewritelog(r, 3, "split uri=%s -> uri=%s, args=%s", olduri,
  2343.                        r->filename, r->args);
  2344.         }
  2345.     }
  2346.     return;
  2347. }
  2348.  
  2349.  
  2350. /*
  2351. **
  2352. **  strip 'http[s]://ourhost/' from URI
  2353. **
  2354. */
  2355.  
  2356. static void reduce_uri(request_rec *r)
  2357. {
  2358.     char *cp;
  2359.     unsigned short port;
  2360.     char *portp;
  2361.     char *hostp;
  2362.     char *url;
  2363.     char c;
  2364.     char host[LONG_STRING_LEN];
  2365.     char buf[MAX_STRING_LEN];
  2366.     char *olduri;
  2367.     int l;
  2368.  
  2369.     cp = ap_http_method(r);
  2370.     l  = strlen(cp);
  2371.     if (   strlen(r->filename) > l+3 
  2372.         && strncasecmp(r->filename, cp, l) == 0
  2373.         && r->filename[l]   == ':'
  2374.         && r->filename[l+1] == '/'
  2375.         && r->filename[l+2] == '/'             ) {
  2376.         /* there was really a rewrite to a remote path */
  2377.  
  2378.         olduri = ap_pstrdup(r->pool, r->filename); /* save for logging */
  2379.  
  2380.         /* cut the hostname and port out of the URI */
  2381.         ap_cpystrn(buf, r->filename+(l+3), sizeof(buf));
  2382.         hostp = buf;
  2383.         for (cp = hostp; *cp != '\0' && *cp != '/' && *cp != ':'; cp++)
  2384.             ;
  2385.         if (*cp == ':') {
  2386.             /* set host */
  2387.             *cp++ = '\0';
  2388.             ap_cpystrn(host, hostp, sizeof(host));
  2389.             /* set port */
  2390.             portp = cp;
  2391.             for (; *cp != '\0' && *cp != '/'; cp++)
  2392.                 ;
  2393.             c = *cp;
  2394.             *cp = '\0';
  2395.             port = atoi(portp);
  2396.             *cp = c;
  2397.             /* set remaining url */
  2398.             url = cp;
  2399.         }
  2400.         else if (*cp == '/') {
  2401.             /* set host */
  2402.             *cp = '\0';
  2403.             ap_cpystrn(host, hostp, sizeof(host));
  2404.             *cp = '/';
  2405.             /* set port */
  2406.             port = ap_default_port(r);
  2407.             /* set remaining url */
  2408.             url = cp;
  2409.         }
  2410.         else {
  2411.             /* set host */
  2412.             ap_cpystrn(host, hostp, sizeof(host));
  2413.             /* set port */
  2414.             port = ap_default_port(r);
  2415.             /* set remaining url */
  2416.             url = "/";
  2417.         }
  2418.  
  2419.         /* now check whether we could reduce it to a local path... */
  2420.         if (ap_matches_request_vhost(r, host, port)) {
  2421.             /* this is our host, so only the URL remains */
  2422.             r->filename = ap_pstrdup(r->pool, url);
  2423.             rewritelog(r, 3, "reduce %s -> %s", olduri, r->filename);
  2424.         }
  2425.     }
  2426.     return;
  2427. }
  2428.  
  2429.  
  2430. /*
  2431. **
  2432. **  add 'http[s]://ourhost[:ourport]/' to URI
  2433. **  if URI is still not fully qualified
  2434. **
  2435. */
  2436.  
  2437. static void fully_qualify_uri(request_rec *r)
  2438. {
  2439.     int i;
  2440.     char buf[32];
  2441.     const char *thisserver;
  2442.     char *thisport;
  2443.     int port;
  2444.  
  2445.     i = strlen(r->filename);
  2446.     if (!(   (i > 7 && strncasecmp(r->filename, "http://", 7)   == 0)
  2447.           || (i > 8 && strncasecmp(r->filename, "https://", 8)  == 0)
  2448.           || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0)
  2449.           || (i > 6 && strncasecmp(r->filename, "ftp://", 6)    == 0))) {
  2450.  
  2451.         thisserver = ap_get_server_name(r);
  2452.         port = ap_get_server_port(r);
  2453.         if (ap_is_default_port(port,r)) {
  2454.             thisport = "";
  2455.         }
  2456.         else {
  2457.             ap_snprintf(buf, sizeof(buf), ":%u", port);
  2458.             thisport = buf;
  2459.         }
  2460.  
  2461.         if (r->filename[0] == '/') {
  2462.             r->filename = ap_psprintf(r->pool, "%s://%s%s%s",
  2463.                                       ap_http_method(r), thisserver,
  2464.                                       thisport, r->filename);
  2465.         }
  2466.         else {
  2467.             r->filename = ap_psprintf(r->pool, "%s://%s%s/%s",
  2468.                                       ap_http_method(r), thisserver,
  2469.                                       thisport, r->filename);
  2470.         }
  2471.     }
  2472.     return;
  2473. }
  2474.  
  2475.  
  2476. /*
  2477. **
  2478. **  Expand the %0-%9 or $0-$9 regex backreferences
  2479. **
  2480. */
  2481.  
  2482. static void expand_backref_inbuffer(pool *p, char *buf, int nbuf,
  2483.                                     backrefinfo *bri, char c)
  2484. {
  2485.     int i;
  2486.  
  2487.     if (bri->nsub < 1) {
  2488.         return;
  2489.     }
  2490.  
  2491.     if (c != '$') {
  2492.         /* safe existing $N backrefs and replace <c>N with $N backrefs */
  2493.         for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
  2494.             if (buf[i] == '$' && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
  2495.                 buf[i++] = '\001';
  2496.             }
  2497.             else if (buf[i] == c && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
  2498.                 buf[i++] = '$';
  2499.             }
  2500.         }
  2501.     }
  2502.  
  2503.     /* now apply the pregsub() function */
  2504.     ap_cpystrn(buf, ap_pregsub(p, buf, bri->source,
  2505.                          bri->nsub+1, bri->regmatch), nbuf);
  2506.  
  2507.     if (c != '$') {
  2508.         /* restore the original $N backrefs */
  2509.         for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
  2510.             if (buf[i] == '\001' && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
  2511.                 buf[i++] = '$';
  2512.             }
  2513.         }
  2514.     }
  2515. }
  2516.  
  2517.  
  2518. /*
  2519. **
  2520. **  Expand tilde-paths (/~user) through
  2521. **  Unix /etc/passwd database information
  2522. **
  2523. */
  2524. #ifndef WIN32
  2525. static char *expand_tildepaths(request_rec *r, char *uri)
  2526. {
  2527.     char user[LONG_STRING_LEN];
  2528.     struct passwd *pw;
  2529.     char *newuri;
  2530.     int i, j;
  2531.  
  2532.     newuri = uri;
  2533.     if (uri != NULL && strlen(uri) > 2 && uri[0] == '/' && uri[1] == '~') {
  2534.         /* cut out the username */
  2535.         for (j = 0, i = 2; j < sizeof(user)-1
  2536.                && uri[i] != '\0'
  2537.                && uri[i] != '/'  ; ) {
  2538.             user[j++] = uri[i++];
  2539.         }
  2540.         user[j] = '\0';
  2541.  
  2542.         /* lookup username in systems passwd file */
  2543.         if ((pw = getpwnam(user)) != NULL) {
  2544.             /* ok, user was found, so expand the ~user string */
  2545.             if (uri[i] != '\0') {
  2546.                 /* ~user/anything...  has to be expanded */
  2547.                 if (pw->pw_dir[strlen(pw->pw_dir)-1] == '/') {
  2548.                     pw->pw_dir[strlen(pw->pw_dir)-1] = '\0';
  2549.                 }
  2550.                 newuri = ap_pstrcat(r->pool, pw->pw_dir, uri+i, NULL);
  2551.             }
  2552.             else {
  2553.                 /* only ~user has to be expanded */
  2554.                 newuri = ap_pstrdup(r->pool, pw->pw_dir);
  2555.             }
  2556.         }
  2557.     }
  2558.     return newuri;
  2559. }
  2560. #endif
  2561.  
  2562. /*
  2563. **
  2564. **  mapfile expansion support
  2565. **  i.e. expansion of MAP lookup directives
  2566. **  ${<mapname>:<key>} in RewriteRule rhs
  2567. **
  2568. */
  2569.  
  2570. #define limit_length(n) (n > LONG_STRING_LEN-1 ? LONG_STRING_LEN-1 : n)
  2571.  
  2572. static void expand_map_lookups(request_rec *r, char *uri, int uri_len)
  2573. {
  2574.     char newuri[MAX_STRING_LEN];
  2575.     char *cpI;
  2576.     char *cpIE;
  2577.     char *cpO;
  2578.     char *cpT;
  2579.     char *cpT2;
  2580.     char mapname[LONG_STRING_LEN];
  2581.     char mapkey[LONG_STRING_LEN];
  2582.     char defaultvalue[LONG_STRING_LEN];
  2583.     int n;
  2584.  
  2585.     cpI = uri;
  2586.     cpIE = cpI+strlen(cpI);
  2587.     cpO = newuri;
  2588.     while (cpI < cpIE) {
  2589.         if (cpI+6 < cpIE && strncmp(cpI, "${", 2) == 0) {
  2590.             /* missing delimiter -> take it as plain text */
  2591.             if (   strchr(cpI+2, ':') == NULL
  2592.                 || strchr(cpI+2, '}') == NULL) {
  2593.                 memcpy(cpO, cpI, 2);
  2594.                 cpO += 2;
  2595.                 cpI += 2;
  2596.                 continue;
  2597.             }
  2598.             cpI += 2;
  2599.  
  2600.             cpT = strchr(cpI, ':');
  2601.             n = cpT-cpI;
  2602.             memcpy(mapname, cpI, limit_length(n));
  2603.             mapname[limit_length(n)] = '\0';
  2604.             cpI += n+1;
  2605.  
  2606.             cpT2 = strchr(cpI, '|');
  2607.             cpT = strchr(cpI, '}');
  2608.             if (cpT2 != NULL && cpT2 < cpT) {
  2609.                 n = cpT2-cpI;
  2610.                 memcpy(mapkey, cpI, limit_length(n));
  2611.                 mapkey[limit_length(n)] = '\0';
  2612.                 cpI += n+1;
  2613.  
  2614.                 n = cpT-cpI;
  2615.                 memcpy(defaultvalue, cpI, limit_length(n));
  2616.                 defaultvalue[limit_length(n)] = '\0';
  2617.                 cpI += n+1;
  2618.             }
  2619.             else {
  2620.                 n = cpT-cpI;
  2621.                 memcpy(mapkey, cpI, limit_length(n));
  2622.                 mapkey[limit_length(n)] = '\0';
  2623.                 cpI += n+1;
  2624.  
  2625.                 defaultvalue[0] = '\0';
  2626.             }
  2627.  
  2628.             cpT = lookup_map(r, mapname, mapkey);
  2629.             if (cpT != NULL) {
  2630.                 n = strlen(cpT);
  2631.                 if (cpO + n >= newuri + sizeof(newuri)) {
  2632.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
  2633.                                  r, "insufficient space in "
  2634.                                  "expand_map_lookups, aborting");
  2635.                     return;
  2636.                 }
  2637.                 memcpy(cpO, cpT, n);
  2638.                 cpO += n;
  2639.             }
  2640.             else {
  2641.                 n = strlen(defaultvalue);
  2642.                 if (cpO + n >= newuri + sizeof(newuri)) {
  2643.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 
  2644.                                  r, "insufficient space in "
  2645.                                  "expand_map_lookups, aborting");
  2646.                     return;
  2647.                 }
  2648.                 memcpy(cpO, defaultvalue, n);
  2649.                 cpO += n;
  2650.             }
  2651.         }
  2652.         else {
  2653.             cpT = strstr(cpI, "${");
  2654.             if (cpT == NULL)
  2655.                 cpT = cpI+strlen(cpI);
  2656.             n = cpT-cpI;
  2657.             if (cpO + n >= newuri + sizeof(newuri)) {
  2658.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 
  2659.                              r, "insufficient space in "
  2660.                              "expand_map_lookups, aborting");
  2661.                 return;
  2662.             }
  2663.             memcpy(cpO, cpI, n);
  2664.             cpO += n;
  2665.             cpI += n;
  2666.         }
  2667.     }
  2668.     *cpO = '\0';
  2669.     ap_cpystrn(uri, newuri, uri_len);
  2670.     return;
  2671. }
  2672.  
  2673. #undef limit_length
  2674.  
  2675.  
  2676.  
  2677. /*
  2678. ** +-------------------------------------------------------+
  2679. ** |                                                       |
  2680. ** |              DBM hashfile support
  2681. ** |                                                       |
  2682. ** +-------------------------------------------------------+
  2683. */
  2684.  
  2685.  
  2686. static char *lookup_map(request_rec *r, char *name, char *key)
  2687. {
  2688.     void *sconf;
  2689.     rewrite_server_conf *conf;
  2690.     array_header *rewritemaps;
  2691.     rewritemap_entry *entries;
  2692.     rewritemap_entry *s;
  2693.     char *value;
  2694.     struct stat st;
  2695.     int i;
  2696.  
  2697.     /* get map configuration */
  2698.     sconf = r->server->module_config;
  2699.     conf  = (rewrite_server_conf *)ap_get_module_config(sconf, 
  2700.                                                         &rewrite_module);
  2701.     rewritemaps = conf->rewritemaps;
  2702.  
  2703.     entries = (rewritemap_entry *)rewritemaps->elts;
  2704.     for (i = 0; i < rewritemaps->nelts; i++) {
  2705.         s = &entries[i];
  2706.         if (strcmp(s->name, name) == 0) {
  2707.             if (s->type == MAPTYPE_TXT) {
  2708.                 if (stat(s->checkfile, &st) == -1) {
  2709.                     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  2710.                                  "mod_rewrite: can't access text RewriteMap "
  2711.                                  "file %s", s->checkfile);
  2712.                     rewritelog(r, 1, "can't open RewriteMap file, "
  2713.                                "see error log");
  2714.                     return NULL;
  2715.                 }
  2716.                 value = get_cache_string(cachep, s->name, CACHEMODE_TS,
  2717.                                          st.st_mtime, key);
  2718.                 if (value == NULL) {
  2719.                     rewritelog(r, 6, "cache lookup FAILED, forcing new "
  2720.                                "map lookup");
  2721.                     if ((value =
  2722.                          lookup_map_txtfile(r, s->datafile, key)) != NULL) {
  2723.                         rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] "
  2724.                                    "-> val=%s", s->name, key, value);
  2725.                         set_cache_string(cachep, s->name, CACHEMODE_TS,
  2726.                                          st.st_mtime, key, value);
  2727.                         return value;
  2728.                     }
  2729.                     else {
  2730.                         rewritelog(r, 5, "map lookup FAILED: map=%s[txt] "
  2731.                                    "key=%s", s->name, key);
  2732.                         return NULL;
  2733.                     }
  2734.                 }
  2735.                 else {
  2736.                     rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s "
  2737.                                "-> val=%s", s->name, key, value);
  2738.                     return value;
  2739.                 }
  2740.             }
  2741.             else if (s->type == MAPTYPE_DBM) {
  2742. #ifndef NO_DBM_REWRITEMAP
  2743.                 if (stat(s->checkfile, &st) == -1) {
  2744.                     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  2745.                                  "mod_rewrite: can't access DBM RewriteMap "
  2746.                                  "file %s", s->checkfile);
  2747.                     rewritelog(r, 1, "can't open DBM RewriteMap file, "
  2748.                                "see error log");
  2749.                     return NULL;
  2750.                 }
  2751.                 value = get_cache_string(cachep, s->name, CACHEMODE_TS,
  2752.                                          st.st_mtime, key);
  2753.                 if (value == NULL) {
  2754.                     rewritelog(r, 6,
  2755.                                "cache lookup FAILED, forcing new map lookup");
  2756.                     if ((value =
  2757.                          lookup_map_dbmfile(r, s->datafile, key)) != NULL) {
  2758.                         rewritelog(r, 5, "map lookup OK: map=%s[dbm] key=%s "
  2759.                                    "-> val=%s", s->name, key, value);
  2760.                         set_cache_string(cachep, s->name, CACHEMODE_TS,
  2761.                                          st.st_mtime, key, value);
  2762.                         return value;
  2763.                     }
  2764.                     else {
  2765.                         rewritelog(r, 5, "map lookup FAILED: map=%s[dbm] "
  2766.                                    "key=%s", s->name, key);
  2767.                         return NULL;
  2768.                     }
  2769.                 }
  2770.                 else {
  2771.                     rewritelog(r, 5, "cache lookup OK: map=%s[dbm] key=%s "
  2772.                                "-> val=%s", s->name, key, value);
  2773.                     return value;
  2774.                 }
  2775. #else
  2776.                 return NULL;
  2777. #endif
  2778.             }
  2779.             else if (s->type == MAPTYPE_PRG) {
  2780.                 if ((value =
  2781.                      lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) {
  2782.                     rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s",
  2783.                                s->name, key, value);
  2784.                     return value;
  2785.                 }
  2786.                 else {
  2787.                     rewritelog(r, 5, "map lookup FAILED: map=%s key=%s",
  2788.                                s->name, key);
  2789.                 }
  2790.             }
  2791.             else if (s->type == MAPTYPE_INT) {
  2792.                 if ((value = lookup_map_internal(r, s->func, key)) != NULL) {
  2793.                     rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s",
  2794.                                s->name, key, value);
  2795.                     return value;
  2796.                 }
  2797.                 else {
  2798.                     rewritelog(r, 5, "map lookup FAILED: map=%s key=%s",
  2799.                                s->name, key);
  2800.                 }
  2801.             }
  2802.             else if (s->type == MAPTYPE_RND) {
  2803.                 if (stat(s->checkfile, &st) == -1) {
  2804.                     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  2805.                                  "mod_rewrite: can't access text RewriteMap "
  2806.                                  "file %s", s->checkfile);
  2807.                     rewritelog(r, 1, "can't open RewriteMap file, "
  2808.                                "see error log");
  2809.                     return NULL;
  2810.                 }
  2811.                 value = get_cache_string(cachep, s->name, CACHEMODE_TS,
  2812.                                          st.st_mtime, key);
  2813.                 if (value == NULL) {
  2814.                     rewritelog(r, 6, "cache lookup FAILED, forcing new "
  2815.                                "map lookup");
  2816.                     if ((value =
  2817.                          lookup_map_txtfile(r, s->datafile, key)) != NULL) {
  2818.                         rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] "
  2819.                                    "-> val=%s", s->name, key, value);
  2820.                         set_cache_string(cachep, s->name, CACHEMODE_TS,
  2821.                                          st.st_mtime, key, value);
  2822.                     }
  2823.                     else {
  2824.                         rewritelog(r, 5, "map lookup FAILED: map=%s[txt] "
  2825.                                    "key=%s", s->name, key);
  2826.                         return NULL;
  2827.                     }
  2828.                 }
  2829.                 else {
  2830.                     rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s "
  2831.                                "-> val=%s", s->name, key, value);
  2832.                 }
  2833.                 value = select_random_value_part(r, value);
  2834.                 rewritelog(r, 5, "randomly choosen the subvalue `%s'", value);
  2835.                 return value;
  2836.             }
  2837.         }
  2838.     }
  2839.     return NULL;
  2840. }
  2841.  
  2842.  
  2843. static char *lookup_map_txtfile(request_rec *r, char *file, char *key)
  2844. {
  2845.     FILE *fp = NULL;
  2846.     char line[1024];
  2847.     char output[1024];
  2848.     char result[1024];
  2849.     char *value = NULL;
  2850.     char *cpT;
  2851.     char *curkey;
  2852.     char *curval;
  2853.  
  2854.     if ((fp = ap_pfopen(r->pool, file, "r")) == NULL) {
  2855.         return NULL;
  2856.     }
  2857.  
  2858.     ap_cpystrn(output, MAPFILE_OUTPUT, sizeof(output));
  2859.     while (fgets(line, sizeof(line), fp) != NULL) {
  2860.         if (line[strlen(line)-1] == '\n') {
  2861.             line[strlen(line)-1] = '\0';
  2862.         }
  2863.         if (regexec(lookup_map_txtfile_regexp, line,
  2864.                     lookup_map_txtfile_regexp->re_nsub+1,
  2865.                     lookup_map_txtfile_regmatch, 0) == 0) {
  2866.             ap_cpystrn(result, ap_pregsub(r->pool, output, line,
  2867.                     lookup_map_txtfile_regexp->re_nsub+1,
  2868.                     lookup_map_txtfile_regmatch),
  2869.                     sizeof(result)); /* substitute in output */
  2870.             cpT = strchr(result, ',');
  2871.             *cpT = '\0';
  2872.             curkey = result;
  2873.             curval = cpT+1;
  2874.  
  2875.             if (strcmp(curkey, key) == 0) {
  2876.                 value = ap_pstrdup(r->pool, curval);
  2877.                 break;
  2878.             }
  2879.         }
  2880.     }
  2881.     ap_pfclose(r->pool, fp);
  2882.     return value;
  2883. }
  2884.  
  2885. #ifndef NO_DBM_REWRITEMAP
  2886. static char *lookup_map_dbmfile(request_rec *r, char *file, char *key)
  2887. {
  2888.     DBM *dbmfp = NULL;
  2889.     datum dbmkey;
  2890.     datum dbmval;
  2891.     char *value = NULL;
  2892.     char buf[MAX_STRING_LEN];
  2893.  
  2894.     dbmkey.dptr  = key;
  2895.     dbmkey.dsize = strlen(key);
  2896.     if ((dbmfp = dbm_open(file, O_RDONLY, 0666)) != NULL) {
  2897.         dbmval = dbm_fetch(dbmfp, dbmkey);
  2898.         if (dbmval.dptr != NULL) {
  2899.             memcpy(buf, dbmval.dptr, 
  2900.                    dbmval.dsize < sizeof(buf)-1 ? 
  2901.                    dbmval.dsize : sizeof(buf)-1  );
  2902.             buf[dbmval.dsize] = '\0';
  2903.             value = ap_pstrdup(r->pool, buf);
  2904.         }
  2905.         dbm_close(dbmfp);
  2906.     }
  2907.     return value;
  2908. }
  2909. #endif
  2910.  
  2911. static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key)
  2912. {
  2913.     char buf[LONG_STRING_LEN];
  2914.     char c;
  2915.     int i;
  2916.  
  2917.     /* when `RewriteEngine off' was used in the per-server
  2918.      * context then the rewritemap-programs were not spawned.
  2919.      * In this case using such a map (usually in per-dir context)
  2920.      * is useless because it is not available.
  2921.      */
  2922.     if (fpin == -1 || fpout == -1) {
  2923.         return NULL;
  2924.     }
  2925.  
  2926.     /* take the lock */
  2927.     rewritelock_alloc(r);
  2928.  
  2929.     /* write out the request key */
  2930.     write(fpin, key, strlen(key));
  2931.     write(fpin, "\n", 1);
  2932.  
  2933.     /* read in the response value */
  2934.     i = 0;
  2935.     while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) {
  2936.         if (c == '\n') {
  2937.             break;
  2938.         }
  2939.         buf[i++] = c;
  2940.     }
  2941.     buf[i] = '\0';
  2942.  
  2943.     /* give the lock back */
  2944.     rewritelock_free(r);
  2945.  
  2946.     if (strcasecmp(buf, "NULL") == 0) {
  2947.         return NULL;
  2948.     }
  2949.     else {
  2950.         return ap_pstrdup(r->pool, buf);
  2951.     }
  2952. }
  2953.  
  2954. static char *lookup_map_internal(request_rec *r,
  2955.                                  char *(*func)(request_rec *, char *),
  2956.                                  char *key)
  2957. {
  2958.     /* currently we just let the function convert
  2959.        the key to a corresponding value */
  2960.     return func(r, key);
  2961. }
  2962.  
  2963. static char *rewrite_mapfunc_toupper(request_rec *r, char *key)
  2964. {
  2965.     char *value, *cp;
  2966.  
  2967.     for (cp = value = ap_pstrdup(r->pool, key); cp != NULL && *cp != '\0';
  2968.          cp++) {
  2969.         *cp = ap_toupper(*cp);
  2970.     }
  2971.     return value;
  2972. }
  2973.  
  2974. static char *rewrite_mapfunc_tolower(request_rec *r, char *key)
  2975. {
  2976.     char *value, *cp;
  2977.  
  2978.     for (cp = value = ap_pstrdup(r->pool, key); cp != NULL && *cp != '\0';
  2979.          cp++) {
  2980.         *cp = ap_tolower(*cp);
  2981.     }
  2982.     return value;
  2983. }
  2984.  
  2985. static char *rewrite_mapfunc_escape(request_rec *r, char *key)
  2986. {
  2987.     char *value;
  2988.  
  2989.     value = ap_escape_uri(r->pool, key);
  2990.     return value;
  2991. }
  2992.  
  2993. static char *rewrite_mapfunc_unescape(request_rec *r, char *key)
  2994. {
  2995.     char *value;
  2996.  
  2997.     value = ap_pstrdup(r->pool, key);
  2998.     ap_unescape_url(value);
  2999.     return value;
  3000. }
  3001.  
  3002. static int rewrite_rand_init_done = 0;
  3003.  
  3004. static void rewrite_rand_init(void)
  3005. {
  3006.     if (!rewrite_rand_init_done) {
  3007.         srand((unsigned)(getpid()));
  3008.         rewrite_rand_init_done = 1;
  3009.     }
  3010.     return;
  3011. }
  3012.  
  3013. static int rewrite_rand(int l, int h)
  3014. {
  3015.     int i;
  3016.     char buf[50];
  3017.  
  3018.     rewrite_rand_init();
  3019.     ap_snprintf(buf, sizeof(buf), "%.0f", 
  3020.                 (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
  3021.     i = atoi(buf)+1;
  3022.     if (i < l) i = l;
  3023.     if (i > h) i = h;
  3024.     return i;
  3025. }
  3026.  
  3027. static char *select_random_value_part(request_rec *r, char *value)
  3028. {
  3029.     char *buf;
  3030.     int n, i, k;
  3031.  
  3032.     /*  count number of distinct values  */
  3033.     for (n = 1, i = 0; value[i] != '\0'; i++) {
  3034.         if (value[i] == '|') {
  3035.             n++;
  3036.         }
  3037.     }
  3038.  
  3039.     /*  when only one value we have no option to choose  */
  3040.     if (n == 1) {
  3041.         return value;
  3042.     }
  3043.  
  3044.     /*  else randomly select one  */
  3045.     k = rewrite_rand(1, n);
  3046.  
  3047.     /*  and grep it out  */
  3048.     for (n = 1, i = 0; value[i] != '\0'; i++) {
  3049.         if (n == k) {
  3050.             break;
  3051.         }
  3052.         if (value[i] == '|') {
  3053.             n++;
  3054.         }
  3055.     }
  3056.     buf = ap_pstrdup(r->pool, &value[i]);
  3057.     for (i = 0; buf[i] != '\0' && buf[i] != '|'; i++)
  3058.         ;
  3059.     buf[i] = '\0';
  3060.     return buf;
  3061. }
  3062.  
  3063.  
  3064. /*
  3065. ** +-------------------------------------------------------+
  3066. ** |                                                       |
  3067. ** |              rewriting logfile support
  3068. ** |                                                       |
  3069. ** +-------------------------------------------------------+
  3070. */
  3071.  
  3072.  
  3073. static void open_rewritelog(server_rec *s, pool *p)
  3074. {
  3075.     rewrite_server_conf *conf;
  3076.     char *fname;
  3077.     piped_log *pl;
  3078.     int    rewritelog_flags = ( O_WRONLY|O_APPEND|O_CREAT );
  3079. #ifdef WIN32
  3080.     mode_t rewritelog_mode  = ( _S_IREAD|_S_IWRITE );
  3081. #else
  3082.     mode_t rewritelog_mode  = ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
  3083. #endif
  3084.  
  3085.     conf = ap_get_module_config(s->module_config, &rewrite_module);
  3086.  
  3087.     if (conf->rewritelogfile == NULL) {
  3088.         return;
  3089.     }
  3090.     if (*(conf->rewritelogfile) == '\0') {
  3091.         return;
  3092.     }
  3093.     if (conf->rewritelogfp > 0) {
  3094.         return; /* virtual log shared w/ main server */
  3095.     }
  3096.  
  3097.     fname = ap_server_root_relative(p, conf->rewritelogfile);
  3098.  
  3099.     if (*conf->rewritelogfile == '|') {
  3100.         if ((pl = ap_open_piped_log(p, conf->rewritelogfile+1)) == NULL) {
  3101.             ap_log_error(APLOG_MARK, APLOG_ERR, s, 
  3102.                          "mod_rewrite: could not open reliable pipe "
  3103.                          "to RewriteLog filter %s", conf->rewritelogfile+1);
  3104.             exit(1);
  3105.         }
  3106.         conf->rewritelogfp = ap_piped_log_write_fd(pl);
  3107.     }
  3108.     else if (*conf->rewritelogfile != '\0') {
  3109.         if ((conf->rewritelogfp = ap_popenf(p, fname, rewritelog_flags,
  3110.                                             rewritelog_mode)) < 0) {
  3111.             ap_log_error(APLOG_MARK, APLOG_ERR, s, 
  3112.  
  3113.                          "mod_rewrite: could not open RewriteLog "
  3114.                          "file %s", fname);
  3115.             exit(1);
  3116.         }
  3117.     }
  3118.     return;
  3119. }
  3120.  
  3121. static void rewritelog(request_rec *r, int level, const char *text, ...)
  3122. {
  3123.     rewrite_server_conf *conf;
  3124.     conn_rec *conn;
  3125.     char *str1;
  3126.     char str2[512];
  3127.     char str3[1024];
  3128.     char type[20];
  3129.     char redir[20];
  3130.     va_list ap;
  3131.     int i;
  3132.     request_rec *req;
  3133.     char *ruser;
  3134.     const char *rhost;
  3135.  
  3136.     va_start(ap, text);
  3137.     conf = ap_get_module_config(r->server->module_config, &rewrite_module);
  3138.     conn = r->connection;
  3139.  
  3140.     if (conf->rewritelogfp < 0) {
  3141.         return;
  3142.     }
  3143.     if (conf->rewritelogfile == NULL) {
  3144.         return;
  3145.     }
  3146.     if (*(conf->rewritelogfile) == '\0') {
  3147.         return;
  3148.     }
  3149.  
  3150.     if (level > conf->rewriteloglevel) {
  3151.         return;
  3152.     }
  3153.  
  3154.     if (conn->user == NULL) {
  3155.         ruser = "-";
  3156.     }
  3157.     else if (strlen(conn->user) != 0) {
  3158.         ruser = conn->user;
  3159.     }
  3160.     else {
  3161.         ruser = "\"\"";
  3162.     }
  3163.  
  3164.     rhost = ap_get_remote_host(conn, r->server->module_config, 
  3165.                                REMOTE_NOLOOKUP);
  3166.     if (rhost == NULL) {
  3167.         rhost = "UNKNOWN-HOST";
  3168.     }
  3169.  
  3170.     str1 = ap_pstrcat(r->pool, rhost, " ",
  3171.                       (conn->remote_logname != NULL ?
  3172.                       conn->remote_logname : "-"), " ",
  3173.                       ruser, NULL);
  3174.     ap_vsnprintf(str2, sizeof(str2), text, ap);
  3175.  
  3176.     if (r->main == NULL) {
  3177.         strcpy(type, "initial");
  3178.     }
  3179.     else {
  3180.         strcpy(type, "subreq");
  3181.     }
  3182.  
  3183.     for (i = 0, req = r; req->prev != NULL; req = req->prev) {
  3184.         i++;
  3185.     }
  3186.     if (i == 0) {
  3187.         redir[0] = '\0';
  3188.     }
  3189.     else {
  3190.         ap_snprintf(redir, sizeof(redir), "/redir#%d", i);
  3191.     }
  3192.  
  3193.     ap_snprintf(str3, sizeof(str3),
  3194.                 "%s %s [%s/sid#%lx][rid#%lx/%s%s] (%d) %s\n", str1,
  3195.                 current_logtime(r), ap_get_server_name(r),
  3196.                 (unsigned long)(r->server), (unsigned long)r,
  3197.                 type, redir, level, str2);
  3198.  
  3199.     fd_lock(r, conf->rewritelogfp);
  3200.     write(conf->rewritelogfp, str3, strlen(str3));
  3201.     fd_unlock(r, conf->rewritelogfp);
  3202.  
  3203.     va_end(ap);
  3204.     return;
  3205. }
  3206.  
  3207. static char *current_logtime(request_rec *r)
  3208. {
  3209.     int timz;
  3210.     struct tm *t;
  3211.     char tstr[80];
  3212.     char sign;
  3213.  
  3214.     t = ap_get_gmtoff(&timz);
  3215.     sign = (timz < 0 ? '-' : '+');
  3216.     if (timz < 0) {
  3217.         timz = -timz;
  3218.     }
  3219.  
  3220.     strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t);
  3221.     ap_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]",
  3222.                 sign, timz/60, timz%60);
  3223.     return ap_pstrdup(r->pool, tstr);
  3224. }
  3225.  
  3226.  
  3227.  
  3228.  
  3229. /*
  3230. ** +-------------------------------------------------------+
  3231. ** |                                                       |
  3232. ** |              rewriting lockfile support
  3233. ** |                                                       |
  3234. ** +-------------------------------------------------------+
  3235. */
  3236.  
  3237. #ifdef WIN32
  3238. #define REWRITELOCK_MODE ( _S_IREAD|_S_IWRITE )
  3239. #else
  3240. #define REWRITELOCK_MODE ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH )
  3241. #endif
  3242.  
  3243. static void rewritelock_create(server_rec *s, pool *p)
  3244. {
  3245.     rewrite_server_conf *conf;
  3246.  
  3247.     conf = ap_get_module_config(s->module_config, &rewrite_module);
  3248.  
  3249.     /* only operate if a lockfile is used */
  3250.     if (conf->rewritelockfile == NULL
  3251.         || *(conf->rewritelockfile) == '\0') {
  3252.         return;
  3253.     }
  3254.  
  3255.     /* fixup the path, especially for rewritelock_remove() */
  3256.     conf->rewritelockfile = ap_server_root_relative(p, conf->rewritelockfile);
  3257.  
  3258.     /* create the lockfile */
  3259.     unlink(conf->rewritelockfile);
  3260.     if ((conf->rewritelockfp = ap_popenf(p, conf->rewritelockfile,
  3261.                                          O_WRONLY|O_CREAT,
  3262.                                          REWRITELOCK_MODE)) < 0) {
  3263.         ap_log_error(APLOG_MARK, APLOG_ERR, s,
  3264.                      "mod_rewrite: Parent could not create RewriteLock "
  3265.                      "file %s", conf->rewritelockfile);
  3266.         exit(1);
  3267.     }
  3268. #if !defined(OS2) && !defined(WIN32)
  3269.     /* make sure the childs have access to this file */
  3270.     if (geteuid() == 0 /* is superuser */)
  3271.         chown(conf->rewritelockfile, ap_user_id, -1 /* no gid change */);
  3272. #endif
  3273.  
  3274.     return;
  3275. }
  3276.  
  3277. static void rewritelock_open(server_rec *s, pool *p)
  3278. {
  3279.     rewrite_server_conf *conf;
  3280.  
  3281.     conf = ap_get_module_config(s->module_config, &rewrite_module);
  3282.  
  3283.     /* only operate if a lockfile is used */
  3284.     if (conf->rewritelockfile == NULL
  3285.         || *(conf->rewritelockfile) == '\0') {
  3286.         return;
  3287.     }
  3288.  
  3289.     /* open the lockfile (once per child) to get a unique fd */
  3290.     if ((conf->rewritelockfp = ap_popenf(p, conf->rewritelockfile,
  3291.                                          O_WRONLY,
  3292.                                          REWRITELOCK_MODE)) < 0) {
  3293.         ap_log_error(APLOG_MARK, APLOG_ERR, s,
  3294.                      "mod_rewrite: Child could not open RewriteLock "
  3295.                      "file %s", conf->rewritelockfile);
  3296.         exit(1);
  3297.     }
  3298.     return;
  3299. }
  3300.  
  3301. static void rewritelock_remove(void *data)
  3302. {
  3303.     server_rec *s;
  3304.     rewrite_server_conf *conf;
  3305.  
  3306.     /* the data is really the server_rec */
  3307.     s = (server_rec *)data;
  3308.     conf = ap_get_module_config(s->module_config, &rewrite_module);
  3309.  
  3310.     /* only operate if a lockfile is used */
  3311.     if (conf->rewritelockfile == NULL
  3312.         || *(conf->rewritelockfile) == '\0') {
  3313.         return;
  3314.     }
  3315.  
  3316.     /* remove the lockfile */
  3317.     unlink(conf->rewritelockfile);
  3318. }
  3319.  
  3320. static void rewritelock_alloc(request_rec *r)
  3321. {
  3322.     rewrite_server_conf *conf;
  3323.  
  3324.     conf = ap_get_module_config(r->server->module_config, &rewrite_module);
  3325.  
  3326.     if (conf->rewritelockfp != -1) {
  3327.         fd_lock(r, conf->rewritelockfp);
  3328.     }
  3329.     return;
  3330. }
  3331.  
  3332. static void rewritelock_free(request_rec *r)
  3333. {
  3334.     rewrite_server_conf *conf;
  3335.  
  3336.     conf = ap_get_module_config(r->server->module_config, &rewrite_module);
  3337.  
  3338.     if (conf->rewritelockfp != -1) {
  3339.         fd_unlock(r, conf->rewritelockfp);
  3340.     }
  3341.     return;
  3342. }
  3343.  
  3344.  
  3345. /*
  3346. ** +-------------------------------------------------------+
  3347. ** |                                                       |
  3348. ** |                  program map support
  3349. ** |                                                       |
  3350. ** +-------------------------------------------------------+
  3351. */
  3352.  
  3353. static void run_rewritemap_programs(server_rec *s, pool *p)
  3354. {
  3355.     rewrite_server_conf *conf;
  3356.     FILE *fpin;
  3357.     FILE *fpout;
  3358.     FILE *fperr;
  3359.     array_header *rewritemaps;
  3360.     rewritemap_entry *entries;
  3361.     rewritemap_entry *map;
  3362.     int i;
  3363.     int rc;
  3364.  
  3365.     conf = ap_get_module_config(s->module_config, &rewrite_module);
  3366.  
  3367.     /*  If the engine isn't turned on,
  3368.      *  don't even try to do anything.
  3369.      */
  3370.     if (conf->state == ENGINE_DISABLED) {
  3371.         return;
  3372.     }
  3373.  
  3374.     rewritemaps = conf->rewritemaps;
  3375.     entries = (rewritemap_entry *)rewritemaps->elts;
  3376.     for (i = 0; i < rewritemaps->nelts; i++) {
  3377.         map = &entries[i];
  3378.         if (map->type != MAPTYPE_PRG) {
  3379.             continue;
  3380.         }
  3381.         if (map->datafile == NULL
  3382.             || *(map->datafile) == '\0'
  3383.             || map->fpin  != -1
  3384.             || map->fpout != -1        ) {
  3385.             continue;
  3386.         }
  3387.         fpin  = NULL;
  3388.         fpout = NULL;
  3389.         rc = ap_spawn_child(p, rewritemap_program_child,
  3390.                             (void *)map->datafile, kill_after_timeout,
  3391.                             &fpin, &fpout, &fperr);
  3392.         if (rc == 0 || fpin == NULL || fpout == NULL) {
  3393.             ap_log_error(APLOG_MARK, APLOG_ERR, s,
  3394.                          "mod_rewrite: could not fork child for "
  3395.                          "RewriteMap process");
  3396.             exit(1);
  3397.         }
  3398.         map->fpin  = fileno(fpin);
  3399.         map->fpout = fileno(fpout);
  3400.         map->fperr = fileno(fperr);
  3401.     }
  3402.     return;
  3403. }
  3404.  
  3405. /* child process code */
  3406. static int rewritemap_program_child(void *cmd, child_info *pinfo)
  3407. {
  3408.     int child_pid = 1;
  3409.  
  3410.     /*
  3411.      * Prepare for exec
  3412.      */
  3413.     ap_cleanup_for_exec();
  3414. #ifdef SIGHUP
  3415.     signal(SIGHUP, SIG_IGN);
  3416. #endif
  3417.  
  3418.     /*
  3419.      * Exec() the child program
  3420.      */
  3421. #if defined(WIN32)
  3422.     /* MS Windows */
  3423.     {
  3424.         char pCommand[MAX_STRING_LEN];
  3425.         STARTUPINFO si;
  3426.         PROCESS_INFORMATION pi;
  3427.  
  3428.         ap_snprintf(pCommand, sizeof(pCommand), "%s /C %s", SHELL_PATH, cmd);
  3429.  
  3430.         memset(&si, 0, sizeof(si));
  3431.         memset(&pi, 0, sizeof(pi));
  3432.  
  3433.         si.cb          = sizeof(si);
  3434.         si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  3435.         si.wShowWindow = SW_HIDE;
  3436.         si.hStdInput   = pinfo->hPipeInputRead;
  3437.         si.hStdOutput  = pinfo->hPipeOutputWrite;
  3438.         si.hStdError   = pinfo->hPipeErrorWrite;
  3439.  
  3440.         if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0,
  3441.                           environ, NULL, &si, &pi)) {
  3442.             CloseHandle(pi.hProcess);
  3443.             CloseHandle(pi.hThread);
  3444.             child_pid = pi.dwProcessId;
  3445.         }
  3446.     }
  3447. #elif defined(OS2)
  3448.     /* IBM OS/2 */
  3449.     execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
  3450. #else
  3451.     /* Standard Unix */
  3452.     execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
  3453. #endif
  3454.     return(child_pid);
  3455. }
  3456.  
  3457.  
  3458.  
  3459.  
  3460. /*
  3461. ** +-------------------------------------------------------+
  3462. ** |                                                       |
  3463. ** |             environment variable support
  3464. ** |                                                       |
  3465. ** +-------------------------------------------------------+
  3466. */
  3467.  
  3468.  
  3469. static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len)
  3470. {
  3471.     char *newbuf;
  3472.     newbuf = expand_variables(r, buf);
  3473.     if (strcmp(newbuf, buf) != 0) {
  3474.         ap_cpystrn(buf, newbuf, buf_len);
  3475.     }
  3476.     return;
  3477. }
  3478.  
  3479. static char *expand_variables(request_rec *r, char *str)
  3480. {
  3481.     char output[MAX_STRING_LEN];
  3482.     char input[MAX_STRING_LEN];
  3483.     char *cp;
  3484.     char *cp2;
  3485.     char *cp3;
  3486.     int expanded;
  3487.     char *outp;
  3488.     char *endp;
  3489.  
  3490.     ap_cpystrn(input, str, sizeof(input));
  3491.     output[0] = '\0';
  3492.     outp = output;
  3493.     endp = output + sizeof(output);
  3494.     expanded = 0;
  3495.     for (cp = input; cp < input+MAX_STRING_LEN; ) {
  3496.         if ((cp2 = strstr(cp, "%{")) != NULL) {
  3497.             if ((cp3 = strstr(cp2, "}")) != NULL) {
  3498.                 *cp2 = '\0';
  3499.                 outp = ap_cpystrn(outp, cp, endp - outp);
  3500.  
  3501.                 cp2 += 2;
  3502.                 *cp3 = '\0';
  3503.                 outp = ap_cpystrn(outp, lookup_variable(r, cp2), endp - outp);
  3504.  
  3505.                 cp = cp3+1;
  3506.                 expanded = 1;
  3507.                 continue;
  3508.             }
  3509.         }
  3510.         outp = ap_cpystrn(outp, cp, endp - outp);
  3511.         break;
  3512.     }
  3513.     return expanded ? ap_pstrdup(r->pool, output) : str;
  3514. }
  3515.  
  3516. static char *lookup_variable(request_rec *r, char *var)
  3517. {
  3518.     const char *result;
  3519.     char resultbuf[LONG_STRING_LEN];
  3520.     time_t tc;
  3521.     struct tm *tm;
  3522.     request_rec *rsub;
  3523. #ifndef WIN32
  3524.     struct passwd *pw;
  3525.     struct group *gr;
  3526.     struct stat finfo;
  3527. #endif
  3528.  
  3529.     result = NULL;
  3530.  
  3531.     /* HTTP headers */
  3532.     if (strcasecmp(var, "HTTP_USER_AGENT") == 0) {
  3533.         result = lookup_header(r, "User-Agent");
  3534.     }
  3535.     else if (strcasecmp(var, "HTTP_REFERER") == 0) {
  3536.         result = lookup_header(r, "Referer");
  3537.     }
  3538.     else if (strcasecmp(var, "HTTP_COOKIE") == 0) {
  3539.         result = lookup_header(r, "Cookie");
  3540.     }
  3541.     else if (strcasecmp(var, "HTTP_FORWARDED") == 0) {
  3542.         result = lookup_header(r, "Forwarded");
  3543.     }
  3544.     else if (strcasecmp(var, "HTTP_HOST") == 0) {
  3545.         result = lookup_header(r, "Host");
  3546.     }
  3547.     else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) {
  3548.         result = lookup_header(r, "Proxy-Connection");
  3549.     }
  3550.     else if (strcasecmp(var, "HTTP_ACCEPT") == 0) {
  3551.         result = lookup_header(r, "Accept");
  3552.     }
  3553.     /* all other headers from which we are still not know about */
  3554.     else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) {
  3555.         result = lookup_header(r, var+5);
  3556.     }
  3557.  
  3558.     /* connection stuff */
  3559.     else if (strcasecmp(var, "REMOTE_ADDR") == 0) {
  3560.         result = r->connection->remote_ip;
  3561.     }
  3562.     else if (strcasecmp(var, "REMOTE_HOST") == 0) {
  3563.         result = (char *)ap_get_remote_host(r->connection,
  3564.                                          r->per_dir_config, REMOTE_NAME);
  3565.     }
  3566.     else if (strcasecmp(var, "REMOTE_USER") == 0) {
  3567.         result = r->connection->user;
  3568.     }
  3569.     else if (strcasecmp(var, "REMOTE_IDENT") == 0) {
  3570.         result = (char *)ap_get_remote_logname(r);
  3571.     }
  3572.  
  3573.     /* request stuff */
  3574.     else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */
  3575.         result = r->the_request;
  3576.     }
  3577.     else if (strcasecmp(var, "REQUEST_METHOD") == 0) {
  3578.         result = r->method;
  3579.     }
  3580.     else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */
  3581.         result = r->uri;
  3582.     }
  3583.     else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 ||
  3584.              strcasecmp(var, "REQUEST_FILENAME") == 0  ) {
  3585.         result = r->filename;
  3586.     }
  3587.     else if (strcasecmp(var, "PATH_INFO") == 0) {
  3588.         result = r->path_info;
  3589.     }
  3590.     else if (strcasecmp(var, "QUERY_STRING") == 0) {
  3591.         result = r->args;
  3592.     }
  3593.     else if (strcasecmp(var, "AUTH_TYPE") == 0) {
  3594.         result = r->connection->ap_auth_type;
  3595.     }
  3596.     else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */
  3597.         result = (r->main != NULL ? "true" : "false");
  3598.     }
  3599.  
  3600.     /* internal server stuff */
  3601.     else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) {
  3602.         result = ap_document_root(r);
  3603.     }
  3604.     else if (strcasecmp(var, "SERVER_ADMIN") == 0) {
  3605.         result = r->server->server_admin;
  3606.     }
  3607.     else if (strcasecmp(var, "SERVER_NAME") == 0) {
  3608.         result = ap_get_server_name(r);
  3609.     }
  3610.     else if (strcasecmp(var, "SERVER_PORT") == 0) {
  3611.         ap_snprintf(resultbuf, sizeof(resultbuf), "%u", ap_get_server_port(r));
  3612.         result = resultbuf;
  3613.     }
  3614.     else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) {
  3615.         result = r->protocol;
  3616.     }
  3617.     else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) {
  3618.         result = ap_get_server_version();
  3619.     }
  3620.     else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */
  3621.         ap_snprintf(resultbuf, sizeof(resultbuf), "%d:%d",
  3622.             MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
  3623.         result = resultbuf;
  3624.     }
  3625.  
  3626.     /* underlaying Unix system stuff */
  3627.     else if (strcasecmp(var, "TIME_YEAR") == 0) {
  3628.         tc = time(NULL);
  3629.         tm = localtime(&tc);
  3630.         ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d",
  3631.                     (tm->tm_year / 100) + 19, tm->tm_year % 100);
  3632.         result = resultbuf;
  3633.     }
  3634. #define MKTIMESTR(format, tmfield) \
  3635.     tc = time(NULL); \
  3636.     tm = localtime(&tc); \
  3637.     ap_snprintf(resultbuf, sizeof(resultbuf), format, tm->tmfield); \
  3638.     result = resultbuf;
  3639.     else if (strcasecmp(var, "TIME_MON") == 0) {
  3640.         MKTIMESTR("%02d", tm_mon+1)
  3641.     }
  3642.     else if (strcasecmp(var, "TIME_DAY") == 0) {
  3643.         MKTIMESTR("%02d", tm_mday)
  3644.     }
  3645.     else if (strcasecmp(var, "TIME_HOUR") == 0) {
  3646.         MKTIMESTR("%02d", tm_hour)
  3647.     }
  3648.     else if (strcasecmp(var, "TIME_MIN") == 0) {
  3649.         MKTIMESTR("%02d", tm_min)
  3650.     }
  3651.     else if (strcasecmp(var, "TIME_SEC") == 0) {
  3652.         MKTIMESTR("%02d", tm_sec)
  3653.     }
  3654.     else if (strcasecmp(var, "TIME_WDAY") == 0) {
  3655.         MKTIMESTR("%d", tm_wday)
  3656.     }
  3657.     else if (strcasecmp(var, "TIME") == 0) {
  3658.         tc = time(NULL);
  3659.         tm = localtime(&tc);
  3660.         ap_snprintf(resultbuf, sizeof(resultbuf),
  3661.                     "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
  3662.                     (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
  3663.                     tm->tm_hour, tm->tm_min, tm->tm_sec);
  3664.         result = resultbuf;
  3665.         rewritelog(r, 1, "RESULT='%s'", result);
  3666.     }
  3667.  
  3668.     /* all other env-variables from the parent Apache process */
  3669.     else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) {
  3670.         /* first try the internal Apache notes structure */
  3671.         result = ap_table_get(r->notes, var+4);
  3672.         /* second try the internal Apache env structure  */
  3673.         if (result == NULL) {
  3674.             result = ap_table_get(r->subprocess_env, var+4);
  3675.         }
  3676.         /* third try the external OS env */
  3677.         if (result == NULL) {
  3678.             result = getenv(var+4);
  3679.         }
  3680.     }
  3681.  
  3682. #define LOOKAHEAD(subrecfunc) \
  3683.         if ( \
  3684.           /* filename is safe to use */ \
  3685.           r->filename != NULL \
  3686.               /* - and we're either not in a subrequest */ \
  3687.               && ( r->main == NULL \
  3688.                   /* - or in a subrequest where paths are non-NULL... */ \
  3689.                     || ( r->main->uri != NULL && r->uri != NULL \
  3690.                         /*   ...and sub and main paths differ */ \
  3691.                         && strcmp(r->main->uri, r->uri) != 0))) { \
  3692.             /* process a file-based subrequest */ \
  3693.             rsub = subrecfunc(r->filename, r); \
  3694.             /* now recursively lookup the variable in the sub_req */ \
  3695.             result = lookup_variable(rsub, var+5); \
  3696.             /* copy it up to our scope before we destroy sub_req's pool */ \
  3697.             result = ap_pstrdup(r->pool, result); \
  3698.             /* cleanup by destroying the subrequest */ \
  3699.             ap_destroy_sub_req(rsub); \
  3700.             /* log it */ \
  3701.             rewritelog(r, 5, "lookahead: path=%s var=%s -> val=%s", \
  3702.                        r->filename, var+5, result); \
  3703.             /* return ourself to prevent re-pstrdup */ \
  3704.             return (char *)result; \
  3705.         }
  3706.  
  3707.     /* look-ahead for parameter through URI-based sub-request */
  3708.     else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) {
  3709.         LOOKAHEAD(ap_sub_req_lookup_uri)
  3710.     }
  3711.     /* look-ahead for parameter through file-based sub-request */
  3712.     else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) {
  3713.         LOOKAHEAD(ap_sub_req_lookup_file)
  3714.     }
  3715.  
  3716. #ifndef WIN32
  3717.     /* Win32 has a rather different view of file ownerships.
  3718.        For now, just forget it */
  3719.  
  3720.     /* file stuff */
  3721.     else if (strcasecmp(var, "SCRIPT_USER") == 0) {
  3722.         result = "<unknown>";
  3723.         if (r->finfo.st_mode != 0) {
  3724.             if ((pw = getpwuid(r->finfo.st_uid)) != NULL) {
  3725.                 result = pw->pw_name;
  3726.             }
  3727.         }
  3728.         else {
  3729.             if (stat(r->filename, &finfo) == 0) {
  3730.                 if ((pw = getpwuid(finfo.st_uid)) != NULL) {
  3731.                     result = pw->pw_name;
  3732.                 }
  3733.             }
  3734.         }
  3735.     }
  3736.     else if (strcasecmp(var, "SCRIPT_GROUP") == 0) {
  3737.         result = "<unknown>";
  3738.         if (r->finfo.st_mode != 0) {
  3739.             if ((gr = getgrgid(r->finfo.st_gid)) != NULL) {
  3740.                 result = gr->gr_name;
  3741.             }
  3742.         }
  3743.         else {
  3744.             if (stat(r->filename, &finfo) == 0) {
  3745.                 if ((gr = getgrgid(finfo.st_gid)) != NULL) {
  3746.                     result = gr->gr_name;
  3747.                 }
  3748.             }
  3749.         }
  3750.     }
  3751. #endif /* ndef WIN32 */
  3752.  
  3753.     if (result == NULL) {
  3754.         return ap_pstrdup(r->pool, "");
  3755.     }
  3756.     else {
  3757.         return ap_pstrdup(r->pool, result);
  3758.     }
  3759. }
  3760.  
  3761. static char *lookup_header(request_rec *r, const char *name)
  3762. {
  3763.     array_header *hdrs_arr;
  3764.     table_entry *hdrs;
  3765.     int i;
  3766.  
  3767.     hdrs_arr = ap_table_elts(r->headers_in);
  3768.     hdrs = (table_entry *)hdrs_arr->elts;
  3769.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  3770.         if (hdrs[i].key == NULL) {
  3771.             continue;
  3772.         }
  3773.         if (strcasecmp(hdrs[i].key, name) == 0) {
  3774.         ap_table_merge(r->notes, VARY_KEY_THIS, name);
  3775.             return hdrs[i].val;
  3776.         }
  3777.     }
  3778.     return NULL;
  3779. }
  3780.  
  3781.  
  3782.  
  3783.  
  3784. /*
  3785. ** +-------------------------------------------------------+
  3786. ** |                                                       |
  3787. ** |                    caching support
  3788. ** |                                                       |
  3789. ** +-------------------------------------------------------+
  3790. */
  3791.  
  3792.  
  3793. static cache *init_cache(pool *p)
  3794. {
  3795.     cache *c;
  3796.  
  3797.     c = (cache *)ap_palloc(p, sizeof(cache));
  3798.     c->pool = ap_make_sub_pool(p);
  3799.     c->lists = ap_make_array(c->pool, 2, sizeof(cachelist));
  3800.     return c;
  3801. }
  3802.  
  3803. static void set_cache_string(cache *c, char *res, int mode, time_t t,
  3804.                              char *key, char *value)
  3805. {
  3806.     cacheentry ce;
  3807.  
  3808.     ce.time  = t;
  3809.     ce.key   = key;
  3810.     ce.value = value;
  3811.     store_cache_string(c, res, &ce);
  3812.     return;
  3813. }
  3814.  
  3815. static char *get_cache_string(cache *c, char *res, int mode,
  3816.                               time_t t, char *key)
  3817. {
  3818.     cacheentry *ce;
  3819.  
  3820.     ce = retrieve_cache_string(c, res, key);
  3821.     if (ce == NULL) {
  3822.         return NULL;
  3823.     }
  3824.     if (mode & CACHEMODE_TS) {
  3825.         if (t != ce->time) {
  3826.             return NULL;
  3827.         }
  3828.     }
  3829.     else if (mode & CACHEMODE_TTL) {
  3830.         if (t > ce->time) {
  3831.             return NULL;
  3832.         }
  3833.     }
  3834.     return ap_pstrdup(c->pool, ce->value);
  3835. }
  3836.  
  3837. static void store_cache_string(cache *c, char *res, cacheentry *ce)
  3838. {
  3839.     int i;
  3840.     int j;
  3841.     cachelist *l;
  3842.     cacheentry *e;
  3843.     int found_list;
  3844.  
  3845.     found_list = 0;
  3846.     /* first try to edit an existing entry */
  3847.     for (i = 0; i < c->lists->nelts; i++) {
  3848.         l = &(((cachelist *)c->lists->elts)[i]);
  3849.         if (strcmp(l->resource, res) == 0) {
  3850.             found_list = 1;
  3851.             for (j = 0; j < l->entries->nelts; j++) {
  3852.                 e = &(((cacheentry *)l->entries->elts)[j]);
  3853.                 if (strcmp(e->key, ce->key) == 0) {
  3854.                     e->time  = ce->time;
  3855.                     e->value = ap_pstrdup(c->pool, ce->value);
  3856.                     return;
  3857.                 }
  3858.             }
  3859.         }
  3860.     }
  3861.  
  3862.     /* create a needed new list */
  3863.     if (!found_list) {
  3864.         l = ap_push_array(c->lists);
  3865.         l->resource = ap_pstrdup(c->pool, res);
  3866.         l->entries  = ap_make_array(c->pool, 2, sizeof(cacheentry));
  3867.     }
  3868.  
  3869.     /* create the new entry */
  3870.     for (i = 0; i < c->lists->nelts; i++) {
  3871.         l = &(((cachelist *)c->lists->elts)[i]);
  3872.         if (strcmp(l->resource, res) == 0) {
  3873.             e = ap_push_array(l->entries);
  3874.             e->time  = ce->time;
  3875.             e->key   = ap_pstrdup(c->pool, ce->key);
  3876.             e->value = ap_pstrdup(c->pool, ce->value);
  3877.             return;
  3878.         }
  3879.     }
  3880.  
  3881.     /* not reached, but when it is no problem... */
  3882.     return;
  3883. }
  3884.  
  3885. static cacheentry *retrieve_cache_string(cache *c, char *res, char *key)
  3886. {
  3887.     int i;
  3888.     int j;
  3889.     cachelist *l;
  3890.     cacheentry *e;
  3891.  
  3892.     for (i = 0; i < c->lists->nelts; i++) {
  3893.         l = &(((cachelist *)c->lists->elts)[i]);
  3894.         if (strcmp(l->resource, res) == 0) {
  3895.             for (j = 0; j < l->entries->nelts; j++) {
  3896.                 e = &(((cacheentry *)l->entries->elts)[j]);
  3897.                 if (strcmp(e->key, key) == 0) {
  3898.                     return e;
  3899.                 }
  3900.             }
  3901.         }
  3902.     }
  3903.     return NULL;
  3904. }
  3905.  
  3906.  
  3907.  
  3908.  
  3909. /*
  3910. ** +-------------------------------------------------------+
  3911. ** |                                                       |
  3912. ** |                    misc functions
  3913. ** |                                                       |
  3914. ** +-------------------------------------------------------+
  3915. */
  3916.  
  3917. static char *subst_prefix_path(request_rec *r, char *input, char *match,
  3918.                                char *subst)
  3919. {
  3920.     char matchbuf[LONG_STRING_LEN];
  3921.     char substbuf[LONG_STRING_LEN];
  3922.     char *output;
  3923.     int l;
  3924.  
  3925.     output = input;
  3926.  
  3927.     /* first create a match string which always has a trailing slash */
  3928.     l = ap_cpystrn(matchbuf, match, sizeof(matchbuf)) - matchbuf;
  3929.     if (matchbuf[l-1] != '/') {
  3930.        matchbuf[l] = '/';
  3931.        matchbuf[l+1] = '\0';
  3932.        l++;
  3933.     }
  3934.     /* now compare the prefix */
  3935.     if (strncmp(input, matchbuf, l) == 0) {
  3936.         rewritelog(r, 5, "strip matching prefix: %s -> %s", output, output+l);
  3937.         output = ap_pstrdup(r->pool, output+l);
  3938.  
  3939.         /* and now add the base-URL as replacement prefix */
  3940.         l = ap_cpystrn(substbuf, subst, sizeof(substbuf)) - substbuf;
  3941.         if (substbuf[l-1] != '/') {
  3942.            substbuf[l] = '/';
  3943.            substbuf[l+1] = '\0';
  3944.            l++;
  3945.         }
  3946.         if (output[0] == '/') {
  3947.             rewritelog(r, 4, "add subst prefix: %s -> %s%s",
  3948.                        output, substbuf, output+1);
  3949.             output = ap_pstrcat(r->pool, substbuf, output+1, NULL);
  3950.         }
  3951.         else {
  3952.             rewritelog(r, 4, "add subst prefix: %s -> %s%s",
  3953.                        output, substbuf, output);
  3954.             output = ap_pstrcat(r->pool, substbuf, output, NULL);
  3955.         }
  3956.     }
  3957.     return output;
  3958. }
  3959.  
  3960.  
  3961. /*
  3962. **
  3963. **  own command line parser which don't have the '\\' problem
  3964. **
  3965. */
  3966.  
  3967. static int parseargline(char *str, char **a1, char **a2, char **a3)
  3968. {
  3969.     char *cp;
  3970.     int isquoted;
  3971.  
  3972. #define SKIP_WHITESPACE(cp) \
  3973.     for ( ; *cp == ' ' || *cp == '\t'; ) { \
  3974.         cp++; \
  3975.     };
  3976.  
  3977. #define CHECK_QUOTATION(cp,isquoted) \
  3978.     isquoted = 0; \
  3979.     if (*cp == '"') { \
  3980.         isquoted = 1; \
  3981.         cp++; \
  3982.     }
  3983.  
  3984. #define DETERMINE_NEXTSTRING(cp,isquoted) \
  3985.     for ( ; *cp != '\0'; cp++) { \
  3986.         if (   (isquoted    && (*cp     == ' ' || *cp     == '\t')) \
  3987.             || (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t'))) { \
  3988.             cp++; \
  3989.             continue; \
  3990.         } \
  3991.         if (   (!isquoted && (*cp == ' ' || *cp == '\t')) \
  3992.             || (isquoted  && *cp == '"')                  ) { \
  3993.             break; \
  3994.         } \
  3995.     }
  3996.  
  3997.     cp = str;
  3998.     SKIP_WHITESPACE(cp);
  3999.  
  4000.     /*  determine first argument */
  4001.     CHECK_QUOTATION(cp, isquoted);
  4002.     *a1 = cp;
  4003.     DETERMINE_NEXTSTRING(cp, isquoted);
  4004.     if (*cp == '\0') {
  4005.         return 1;
  4006.     }
  4007.     *cp++ = '\0';
  4008.  
  4009.     SKIP_WHITESPACE(cp);
  4010.  
  4011.     /*  determine second argument */
  4012.     CHECK_QUOTATION(cp, isquoted);
  4013.     *a2 = cp;
  4014.     DETERMINE_NEXTSTRING(cp, isquoted);
  4015.     if (*cp == '\0') {
  4016.         *cp++ = '\0';
  4017.         *a3 = NULL;
  4018.         return 0;
  4019.     }
  4020.     *cp++ = '\0';
  4021.  
  4022.     SKIP_WHITESPACE(cp);
  4023.  
  4024.     /* again check if there are only two arguments */
  4025.     if (*cp == '\0') {
  4026.         *cp++ = '\0';
  4027.         *a3 = NULL;
  4028.         return 0;
  4029.     }
  4030.  
  4031.     /*  determine second argument */
  4032.     CHECK_QUOTATION(cp, isquoted);
  4033.     *a3 = cp;
  4034.     DETERMINE_NEXTSTRING(cp, isquoted);
  4035.     *cp++ = '\0';
  4036.  
  4037.     return 0;
  4038. }
  4039.  
  4040.  
  4041. static void add_env_variable(request_rec *r, char *s)
  4042. {
  4043.     char var[MAX_STRING_LEN];
  4044.     char val[MAX_STRING_LEN];
  4045.     char *cp;
  4046.     int n;
  4047.  
  4048.     if ((cp = strchr(s, ':')) != NULL) {
  4049.         n = ((cp-s) > MAX_STRING_LEN-1 ? MAX_STRING_LEN-1 : (cp-s));
  4050.         memcpy(var, s, n);
  4051.         var[n] = '\0';
  4052.         ap_cpystrn(val, cp+1, sizeof(val));
  4053.         ap_table_set(r->subprocess_env, var, val);
  4054.         rewritelog(r, 5, "setting env variable '%s' to '%s'", var, val);
  4055.     }
  4056. }
  4057.  
  4058.  
  4059.  
  4060. /*
  4061. **
  4062. **  stat() for only the prefix of a path
  4063. **
  4064. */
  4065.  
  4066. static int prefix_stat(const char *path, struct stat *sb)
  4067. {
  4068.     char curpath[LONG_STRING_LEN];
  4069.     char *cp;
  4070.  
  4071.     ap_cpystrn(curpath, path, sizeof(curpath));
  4072.     if (curpath[0] != '/') {
  4073.         return 0;
  4074.     }
  4075.     if ((cp = strchr(curpath+1, '/')) != NULL) {
  4076.         *cp = '\0';
  4077.     }
  4078.     if (stat(curpath, sb) == 0) {
  4079.         return 1;
  4080.     }
  4081.     else {
  4082.         return 0;
  4083.     }
  4084. }
  4085.  
  4086.  
  4087. /*
  4088. **
  4089. **  File locking
  4090. **
  4091. */
  4092.  
  4093. #ifdef USE_FCNTL
  4094. static struct flock   lock_it;
  4095. static struct flock unlock_it;
  4096. #endif
  4097.  
  4098. static void fd_lock(request_rec *r, int fd)
  4099. {
  4100.     int rc;
  4101.  
  4102. #ifdef USE_FCNTL
  4103.     lock_it.l_whence = SEEK_SET; /* from current point */
  4104.     lock_it.l_start  = 0;        /* -"- */
  4105.     lock_it.l_len    = 0;        /* until end of file */
  4106.     lock_it.l_type   = F_WRLCK;  /* set exclusive/write lock */
  4107.     lock_it.l_pid    = 0;        /* pid not actually interesting */
  4108.  
  4109.     while (   ((rc = fcntl(fd, F_SETLKW, &lock_it)) < 0)
  4110.               && (errno == EINTR)                               ) {
  4111.         continue;
  4112.     }
  4113. #endif
  4114. #ifdef USE_FLOCK
  4115.     while (   ((rc = flock(fd, LOCK_EX)) < 0)
  4116.               && (errno == EINTR)               ) {
  4117.         continue;
  4118.     }
  4119. #endif
  4120. #ifdef USE_LOCKING
  4121.     /* Lock the first byte, always, assume we want to append
  4122.        and seek to the end afterwards */
  4123.     lseek(fd, 0, SEEK_SET);
  4124.     rc = _locking(fd, _LK_LOCK, 1);
  4125.     lseek(fd, 0, SEEK_END);
  4126. #endif
  4127.  
  4128.     if (rc < 0) {
  4129.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  4130.                      "mod_rewrite: failed to lock file descriptor");
  4131.         exit(1);
  4132.     }
  4133.     return;
  4134. }
  4135.  
  4136. static void fd_unlock(request_rec *r, int fd)
  4137. {
  4138.     int rc;
  4139.  
  4140. #ifdef USE_FCNTL
  4141.     unlock_it.l_whence = SEEK_SET; /* from current point */
  4142.     unlock_it.l_start  = 0;        /* -"- */
  4143.     unlock_it.l_len    = 0;        /* until end of file */
  4144.     unlock_it.l_type   = F_UNLCK;  /* unlock */
  4145.     unlock_it.l_pid    = 0;        /* pid not actually interesting */
  4146.  
  4147.     rc = fcntl(fd, F_SETLKW, &unlock_it);
  4148. #endif
  4149. #ifdef USE_FLOCK
  4150.     rc = flock(fd, LOCK_UN);
  4151. #endif
  4152. #ifdef USE_LOCKING
  4153.     lseek(fd, 0, SEEK_SET);
  4154.     rc = _locking(fd, _LK_UNLCK, 1);
  4155.     lseek(fd, 0, SEEK_END);
  4156. #endif
  4157.  
  4158.     if (rc < 0) {
  4159.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  4160.                      "mod_rewrite: failed to unlock file descriptor");
  4161.         exit(1);
  4162.     }
  4163. }
  4164.  
  4165. /*
  4166. **
  4167. **  Lexicographic Compare
  4168. **
  4169. */
  4170.  
  4171. static int compare_lexicography(char *cpNum1, char *cpNum2)
  4172. {
  4173.     int i;
  4174.     int n1, n2;
  4175.  
  4176.     n1 = strlen(cpNum1);
  4177.     n2 = strlen(cpNum2);
  4178.     if (n1 > n2) {
  4179.         return 1;
  4180.     }
  4181.     if (n1 < n2) {
  4182.         return -1;
  4183.     }
  4184.     for (i = 0; i < n1; i++) {
  4185.         if (cpNum1[i] > cpNum2[i]) {
  4186.             return 1;
  4187.         }
  4188.         if (cpNum1[i] < cpNum2[i]) {
  4189.             return -1;
  4190.         }
  4191.     }
  4192.     return 0;
  4193. }
  4194.  
  4195.  
  4196. /*EOF*/
  4197.