static int hook_uri2file()

in modules/mappers/mod_rewrite.c [4872:5234]


static int hook_uri2file(request_rec *r)
{
    rewrite_perdir_conf *dconf;
    rewrite_server_conf *conf;
    const char *saved_rulestatus;
    const char *var;
    const char *thisserver;
    char *thisport;
    const char *thisurl;
    unsigned int port;
    int rulestatus;
    void *skipdata;
    char *ofilename;
    const char *oargs;
    rewriterule_entry *lastsub = NULL;

    /*
     *  retrieve the config structures
     */
    conf = ap_get_module_config(r->server->module_config, &rewrite_module);

    dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config,
                                                        &rewrite_module);

    /*
     *  only do something under runtime if the engine is really enabled,
     *  else return immediately!
     */
    if (!dconf || dconf->state == ENGINE_DISABLED) {
        return DECLINED;
    }

    /*
     *  check for the ugly API case of a virtual host section where no
     *  mod_rewrite directives exists. In this situation we became no chance
     *  by the API to setup our default per-server config so we have to
     *  on-the-fly assume we have the default config. But because the default
     *  config has a disabled rewriting engine we are lucky because can
     *  just stop operating now.
     */
    if (conf->server != r->server) {
        return DECLINED;
    }

    /* END flag was used as a RewriteRule flag on this request */
    apr_pool_userdata_get(&skipdata, really_last_key, r->pool);
    if (skipdata != NULL) {
        rewritelog(r, 8, NULL, "Declining, no further rewriting due to END flag");
        return DECLINED;
    }

    /* Unless the anyuri option is set, ensure that the input to the
     * first rule really is a URL-path, avoiding security issues with
     * poorly configured rules.  See CVE-2011-3368, CVE-2011-4317. */
    if ((dconf->options & OPTION_ANYURI) == 0
        && ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0')
            || !r->uri || r->uri[0] != '/')) {
        rewritelog(r, 8, NULL, "Declining, request-URI '%s' is not a URL-path. "
                    "Consult the manual entry for the RewriteOptions directive "
                    "for options and caveats about matching other strings.",
                   r->uri);
        return DECLINED;
    }

    /*
     *  remember the original query string for later check, since we don't
     *  want to apply URL-escaping when no substitution has changed it.
     *  also, we'll restore original r->filename if we decline this
     *  request.
     */
    ofilename = r->filename;
    oargs = r->args;

    /*
     *  add the SCRIPT_URL variable to the env. this is a bit complicated
     *  due to the fact that apache uses subrequests and internal redirects
     */

    if (r->main == NULL) {
         var = apr_table_get(r->subprocess_env, REDIRECT_ENVVAR_SCRIPT_URL);
         if (var == NULL) {
             apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
         }
         else {
             apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
         }
    }
    else {
         var = apr_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
         apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
    }

    /*
     *  create the SCRIPT_URI variable for the env
     */

    /* add the canonical URI of this URL */
    thisserver = ap_get_server_name_for_url(r);
    port = ap_get_server_port(r);
    if (ap_is_default_port(port, r)) {
        thisport = "";
    }
    else {
        thisport = apr_psprintf(r->pool, ":%u", port);
    }
    thisurl = apr_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL);

    /* set the variable */
    var = apr_pstrcat(r->pool, ap_http_scheme(r), "://", thisserver, thisport,
                      thisurl, NULL);
    apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);

    if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) {
        /* If r->filename was not initially set or if it's a pre_trans reverse
         * "proxy:" scheme, we start with the requested URI.
         */
        if (r->filename == NULL || (r->proxyreq == PROXYREQ_REVERSE &&
                                    strncmp(r->filename, "proxy:", 6) == 0)) {
            r->filename = apr_pstrdup(r->pool, r->uri);
            rewritelog(r, 2, NULL, "init rewrite engine with requested uri "
                       "%s. Original filename = %s", r->filename,
                       (ofilename) ? ofilename : "n/a");
        }
        else {
            rewritelog(r, 2, NULL, "init rewrite engine with passed filename "
                        "%s. Original uri = %s", r->filename, r->uri);
        }

        /*
         *  now apply the rules ...
         */
        rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL, &lastsub);
        apr_table_setn(r->notes, "mod_rewrite_rewritten",
                       apr_psprintf(r->pool,"%d",rulestatus));
    }
    else {
        rewritelog(r, 2, NULL, "uri already rewritten. Status %s, Uri %s, "
                   "r->filename %s", saved_rulestatus, r->uri, r->filename);

        rulestatus = atoi(saved_rulestatus);
    }

    if (rulestatus) {
        apr_size_t flen =  r->filename ? strlen(r->filename) : 0;
        unsigned skip_absolute = flen ? is_absolute_uri(r->filename, NULL) : 0;
        int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0);
        int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE);

        if (r->args
                && !will_escape
                && *(ap_scan_vchar_obstext(r->args))) {
            /*
             * We have a raw control character or a ' ' in r->args.
             * Correct encoding was missed and we're not going to escape
             * it before returning.
             */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410)
                          "Rewritten query string contains control "
                          "characters or spaces");
            return HTTP_FORBIDDEN;
        }

        if (ACTION_STATUS == rulestatus) {
            int n = r->status;

            r->status = HTTP_OK;
            return n;
        }
        else if (ACTION_STATUS_SET == rulestatus) {
            return r->status;
        }

        /* If a pre_trans reverse "proxy:" filename gets rewritten to
         * a non-proxy one this is not a proxy request anymore.
         */
        if (r->proxyreq == PROXYREQ_REVERSE && !to_proxyreq) {
            if (r->handler && strcmp(r->handler, "proxy-server") == 0) {
                r->handler = NULL;
            }
            r->proxyreq = PROXYREQ_NONE;
        }

        if (to_proxyreq) {
            /* it should be go on as an internal proxy request */

            /* check if the proxy module is enabled, so
             * we can actually use it!
             */
            if (!proxy_available) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00669)
                              "attempt to make remote request from mod_rewrite "
                              "without proxy enabled: %s", r->filename);
                return HTTP_FORBIDDEN;
            }

            if (rulestatus == ACTION_NOESCAPE) {
                apr_table_setn(r->notes, "proxy-nocanon", "1");
            }

            /* make sure the QUERY_STRING and
             * PATH_INFO parts get incorporated
             */
            if (r->path_info != NULL) {
                r->filename = apr_pstrcat(r->pool, r->filename,
                                          r->path_info, NULL);
            }
            if ((r->args != NULL)
                && ((r->proxyreq == PROXYREQ_PROXY)
                    || apr_table_get(r->notes, "proxy-nocanon"))) {
                /* see proxy_http:proxy_http_canon() */
                r->filename = apr_pstrcat(r->pool, r->filename,
                                          "?", r->args, NULL);
            }

            /* now make sure the request gets handled by the proxy handler */
            if (PROXYREQ_NONE == r->proxyreq) {
                r->proxyreq = PROXYREQ_REVERSE;
            }
            r->handler  = "proxy-server";

            rewritelog(r, 1, NULL, "go-ahead with proxy request %s [OK]",
                       r->filename);
            return OK;
        }
        else if (skip_absolute > 0) {
            int n;

            /* it was finally rewritten to a remote URL */

            if (rulestatus != ACTION_NOESCAPE) {
                rewritelog(r, 1, NULL, "escaping %s for redirect",
                           r->filename);
                r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute);
            }

            /* append the QUERY_STRING part */
            if (r->args) {
                char *escaped_args = NULL;
                int noescape = (rulestatus == ACTION_NOESCAPE ||
                                (oargs && !strcmp(r->args, oargs)));

                r->filename = apr_pstrcat(r->pool, r->filename, "?",
                                          noescape
                                            ? r->args
                                            : (escaped_args =
                                               ap_escape_uri(r->pool, r->args)),
                                          NULL);

                rewritelog(r, 1, NULL, "%s %s to query string for redirect %s",
                           noescape ? "copying" : "escaping",
                           r->args ,
                           noescape ? "" : escaped_args);
            }

            /* determine HTTP redirect response code */
            if (ap_is_HTTP_REDIRECT(r->status)) {
                n = r->status;
                r->status = HTTP_OK; /* make Apache kernel happy */
            }
            else {
                n = HTTP_MOVED_TEMPORARILY;
            }

            /* now do the redirection */
            apr_table_setn(r->headers_out, "Location", r->filename);
            rewritelog(r, 1, NULL, "redirect to %s [REDIRECT/%d]", r->filename,
                       n);

            return n;
        }
        else if (flen > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
            /*
             * Hack because of underpowered API: passing the current
             * rewritten filename through to other URL-to-filename handlers
             * just as it were the requested URL. This is to enable
             * post-processing by mod_alias, etc.  which always act on
             * r->uri! The difference here is: We do not try to
             * add the document root
             */
            r->uri = apr_pstrdup(r->pool, r->filename+12);
            return DECLINED;
        }
        else {
            /* it was finally rewritten to a local path */
            const char *uri_reduced = NULL;

            /* expand "/~user" prefix */
#if APR_HAS_USER
            r->filename = expand_tildepaths(r, r->filename);
#endif
            rewritelog(r, 2, NULL, "local path result: %s", r->filename);

            /* the filename must be either an absolute local path or an
             * absolute local URL.
             */
            if (   *r->filename != '/'
                && !ap_os_is_path_absolute(r->pool, r->filename)) {
                return HTTP_BAD_REQUEST;
            }

            /* We have r->filename as a path in a server-context rewrite without
             * the PT flag. The historical behavior is to treat it as a verbatim
             * filesystem path iff the first component of the path exists and is
             * readable by httpd. Otherwise, it is interpreted as DocumentRoot
             * relative.
             *
             * NOTICE:
             * We cannot leave out the prefix_stat because
             * - If we always prefix with document_root
             *   then no absolute path can could ever be used in
             *   a substitution. e.g. emulating an Alias.
             * - If we never prefix with document_root
             *   then the files under document_root have to
             *   be references directly and document_root
             *   gets never used and will be a dummy parameter -
             *   this is also bad.
             *   - Later addition: This part is questionable.
             *     If we had never prefixed, users would just 
             *     need %{DOCUMENT_ROOT} in substitutions or the 
             *     [PT] flag.
             *
             * BUT:
             * Under real Unix systems this is no perf problem,
             * because we only do stat() on the first directory
             * and this gets cached by the kernel for along time!
             */

            if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
                uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced");
            }

            if (!prefix_stat(r, r->filename, r->pool,
                             conf->options & OPTION_UNSAFE_PREFIX_STAT ? NULL : lastsub)
                || uri_reduced != NULL) {
                int res;
                char *tmp = r->uri;

                r->uri = r->filename;
                res = ap_core_translate(r);
                r->uri = tmp;

                if (res != OK) {
                    rewritelog(r, 1, NULL, "prefixing with document_root of %s"
                               " FAILED", r->filename);

                    return res;
                }

                rewritelog(r, 2, NULL, "prefixed with document_root to %s",
                           r->filename);
            }

            rewritelog(r, 1, NULL, "go-ahead with %s [OK]", r->filename);
            return OK;
        }
    }
    else {
        rewritelog(r, 1, NULL, "pass through %s, filename %s",
                   r->filename, (ofilename) ? ofilename : "n/a");
        r->filename = ofilename;
        return DECLINED;
    }
}