Blame modules/http/http_protocol.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * http_protocol.c --- routines which directly communicate with the client.
Packit 90a5c9
 *
Packit 90a5c9
 * Code originally by Rob McCool; much redone by Robert S. Thau
Packit 90a5c9
 * and the Apache Software Foundation.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_buckets.h"
Packit 90a5c9
#include "apr_lib.h"
Packit 90a5c9
#include "apr_signal.h"
Packit 90a5c9
Packit 90a5c9
#define APR_WANT_STDIO          /* for sscanf */
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#define APR_WANT_MEMFUNC
Packit 90a5c9
#include "apr_want.h"
Packit 90a5c9
Packit 90a5c9
#include "util_filter.h"
Packit 90a5c9
#include "ap_config.h"
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_core.h"
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_main.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "http_vhost.h"
Packit 90a5c9
#include "http_log.h"           /* For errors detected in basic auth common
Packit 90a5c9
                                 * support code... */
Packit 90a5c9
#include "apr_date.h"           /* For apr_date_parse_http and APR_DATE_BAD */
Packit 90a5c9
#include "util_charset.h"
Packit 90a5c9
#include "util_ebcdic.h"
Packit 90a5c9
#include "util_time.h"
Packit 90a5c9
#include "ap_mpm.h"
Packit 90a5c9
Packit 90a5c9
#include "mod_core.h"
Packit 90a5c9
Packit 90a5c9
#if APR_HAVE_STDARG_H
Packit 90a5c9
#include <stdarg.h>
Packit 90a5c9
#endif
Packit 90a5c9
#if APR_HAVE_UNISTD_H
Packit 90a5c9
#include <unistd.h>
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
APLOG_USE_MODULE(http);
Packit 90a5c9
Packit 90a5c9
/* New Apache routine to map status codes into array indicies
Packit 90a5c9
 *  e.g.  100 -> 0,  101 -> 1,  200 -> 2 ...
Packit 90a5c9
 * The number of status lines must equal the value of
Packit 90a5c9
 * RESPONSE_CODES (httpd.h) and must be listed in order.
Packit 90a5c9
 * No gaps are allowed between X00 and the largest Xnn
Packit 90a5c9
 * for any X (see ap_index_of_response).
Packit 90a5c9
 * When adding a new code here, add a define to httpd.h
Packit 90a5c9
 * as well.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
static const char * const status_lines[RESPONSE_CODES] =
Packit 90a5c9
{
Packit 90a5c9
    "100 Continue",
Packit 90a5c9
    "101 Switching Protocols",
Packit 90a5c9
    "102 Processing",
Packit 90a5c9
#define LEVEL_200  3
Packit 90a5c9
    "200 OK",
Packit 90a5c9
    "201 Created",
Packit 90a5c9
    "202 Accepted",
Packit 90a5c9
    "203 Non-Authoritative Information",
Packit 90a5c9
    "204 No Content",
Packit 90a5c9
    "205 Reset Content",
Packit 90a5c9
    "206 Partial Content",
Packit 90a5c9
    "207 Multi-Status",
Packit 90a5c9
    "208 Already Reported",
Packit 90a5c9
    NULL, /* 209 */
Packit 90a5c9
    NULL, /* 210 */
Packit 90a5c9
    NULL, /* 211 */
Packit 90a5c9
    NULL, /* 212 */
Packit 90a5c9
    NULL, /* 213 */
Packit 90a5c9
    NULL, /* 214 */
Packit 90a5c9
    NULL, /* 215 */
Packit 90a5c9
    NULL, /* 216 */
Packit 90a5c9
    NULL, /* 217 */
Packit 90a5c9
    NULL, /* 218 */
Packit 90a5c9
    NULL, /* 219 */
Packit 90a5c9
    NULL, /* 220 */
Packit 90a5c9
    NULL, /* 221 */
Packit 90a5c9
    NULL, /* 222 */
Packit 90a5c9
    NULL, /* 223 */
Packit 90a5c9
    NULL, /* 224 */
Packit 90a5c9
    NULL, /* 225 */
Packit 90a5c9
    "226 IM Used",
Packit 90a5c9
#define LEVEL_300 30
Packit 90a5c9
    "300 Multiple Choices",
Packit 90a5c9
    "301 Moved Permanently",
Packit 90a5c9
    "302 Found",
Packit 90a5c9
    "303 See Other",
Packit 90a5c9
    "304 Not Modified",
Packit 90a5c9
    "305 Use Proxy",
Packit 90a5c9
    NULL, /* 306 */
Packit 90a5c9
    "307 Temporary Redirect",
Packit 90a5c9
    "308 Permanent Redirect",
Packit 90a5c9
#define LEVEL_400 39
Packit 90a5c9
    "400 Bad Request",
Packit 90a5c9
    "401 Unauthorized",
Packit 90a5c9
    "402 Payment Required",
Packit 90a5c9
    "403 Forbidden",
Packit 90a5c9
    "404 Not Found",
Packit 90a5c9
    "405 Method Not Allowed",
Packit 90a5c9
    "406 Not Acceptable",
Packit 90a5c9
    "407 Proxy Authentication Required",
Packit 90a5c9
    "408 Request Timeout",
Packit 90a5c9
    "409 Conflict",
Packit 90a5c9
    "410 Gone",
Packit 90a5c9
    "411 Length Required",
Packit 90a5c9
    "412 Precondition Failed",
Packit 90a5c9
    "413 Request Entity Too Large",
Packit 90a5c9
    "414 Request-URI Too Long",
Packit 90a5c9
    "415 Unsupported Media Type",
Packit 90a5c9
    "416 Requested Range Not Satisfiable",
Packit 90a5c9
    "417 Expectation Failed",
Packit 90a5c9
    NULL, /* 418 */
Packit 90a5c9
    NULL, /* 419 */
Packit 90a5c9
    NULL, /* 420 */
Packit 90a5c9
    "421 Misdirected Request",
Packit 90a5c9
    "422 Unprocessable Entity",
Packit 90a5c9
    "423 Locked",
Packit 90a5c9
    "424 Failed Dependency",
Packit 90a5c9
    NULL, /* 425 */
Packit 90a5c9
    "426 Upgrade Required",
Packit 90a5c9
    NULL, /* 427 */
Packit 90a5c9
    "428 Precondition Required",
Packit 90a5c9
    "429 Too Many Requests",
Packit 90a5c9
    NULL, /* 430 */
Packit 90a5c9
    "431 Request Header Fields Too Large",
Packit 90a5c9
    NULL, /* 432 */
Packit 90a5c9
    NULL, /* 433 */
Packit 90a5c9
    NULL, /* 434 */
Packit 90a5c9
    NULL, /* 435 */
Packit 90a5c9
    NULL, /* 436 */
Packit 90a5c9
    NULL, /* 437 */
Packit 90a5c9
    NULL, /* 438 */
Packit 90a5c9
    NULL, /* 439 */
Packit 90a5c9
    NULL, /* 440 */
Packit 90a5c9
    NULL, /* 441 */
Packit 90a5c9
    NULL, /* 442 */
Packit 90a5c9
    NULL, /* 443 */
Packit 90a5c9
    NULL, /* 444 */
Packit 90a5c9
    NULL, /* 445 */
Packit 90a5c9
    NULL, /* 446 */
Packit 90a5c9
    NULL, /* 447 */
Packit 90a5c9
    NULL, /* 448 */
Packit 90a5c9
    NULL, /* 449 */
Packit 90a5c9
    NULL, /* 450 */
Packit 90a5c9
    "451 Unavailable For Legal Reasons",
Packit 90a5c9
#define LEVEL_500 91
Packit 90a5c9
    "500 Internal Server Error",
Packit 90a5c9
    "501 Not Implemented",
Packit 90a5c9
    "502 Bad Gateway",
Packit 90a5c9
    "503 Service Unavailable",
Packit 90a5c9
    "504 Gateway Timeout",
Packit 90a5c9
    "505 HTTP Version Not Supported",
Packit 90a5c9
    "506 Variant Also Negotiates",
Packit 90a5c9
    "507 Insufficient Storage",
Packit 90a5c9
    "508 Loop Detected",
Packit 90a5c9
    NULL, /* 509 */
Packit 90a5c9
    "510 Not Extended",
Packit 90a5c9
    "511 Network Authentication Required"
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
APR_HOOK_STRUCT(
Packit 90a5c9
    APR_HOOK_LINK(insert_error_filter)
Packit 90a5c9
)
Packit 90a5c9
Packit 90a5c9
AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
Packit 90a5c9
Packit 90a5c9
/* The index of the first bit field that is used to index into a limit
Packit 90a5c9
 * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
Packit 90a5c9
 */
Packit 90a5c9
#define METHOD_NUMBER_FIRST (M_INVALID + 1)
Packit 90a5c9
Packit 90a5c9
/* The max method number. Method numbers are used to shift bitmasks,
Packit 90a5c9
 * so this cannot exceed 63, and all bits high is equal to -1, which is a
Packit 90a5c9
 * special flag, so the last bit used has index 62.
Packit 90a5c9
 */
Packit 90a5c9
#define METHOD_NUMBER_LAST  62
Packit 90a5c9
Packit 90a5c9
static int is_mpm_running(void)
Packit 90a5c9
{
Packit 90a5c9
    int mpm_state = 0;
Packit 90a5c9
Packit 90a5c9
    if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
Packit 90a5c9
      return 0;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (mpm_state == AP_MPMQ_STOPPING) {
Packit 90a5c9
      return 0;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return 1;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(int) ap_set_keepalive(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    int ka_sent = 0;
Packit 90a5c9
    int left = r->server->keep_alive_max - r->connection->keepalives;
Packit 90a5c9
    int wimpy = ap_find_token(r->pool,
Packit 90a5c9
                              apr_table_get(r->headers_out, "Connection"),
Packit 90a5c9
                              "close");
Packit 90a5c9
    const char *conn = apr_table_get(r->headers_in, "Connection");
Packit 90a5c9
Packit 90a5c9
    /* The following convoluted conditional determines whether or not
Packit 90a5c9
     * the current connection should remain persistent after this response
Packit 90a5c9
     * (a.k.a. HTTP Keep-Alive) and whether or not the output message
Packit 90a5c9
     * body should use the HTTP/1.1 chunked transfer-coding.  In English,
Packit 90a5c9
     *
Packit 90a5c9
     *   IF  we have not marked this connection as errored;
Packit 90a5c9
     *   and the client isn't expecting 100-continue (PR47087 - more
Packit 90a5c9
     *       input here could be the client continuing when we're
Packit 90a5c9
     *       closing the request).
Packit 90a5c9
     *   and the response body has a defined length due to the status code
Packit 90a5c9
     *       being 304 or 204, the request method being HEAD, already
Packit 90a5c9
     *       having defined Content-Length or Transfer-Encoding: chunked, or
Packit 90a5c9
     *       the request version being HTTP/1.1 and thus capable of being set
Packit 90a5c9
     *       as chunked [we know the (r->chunked = 1) side-effect is ugly];
Packit 90a5c9
     *   and the server configuration enables keep-alive;
Packit 90a5c9
     *   and the server configuration has a reasonable inter-request timeout;
Packit 90a5c9
     *   and there is no maximum # requests or the max hasn't been reached;
Packit 90a5c9
     *   and the response status does not require a close;
Packit 90a5c9
     *   and the response generator has not already indicated close;
Packit 90a5c9
     *   and the client did not request non-persistence (Connection: close);
Packit 90a5c9
     *   and    we haven't been configured to ignore the buggy twit
Packit 90a5c9
     *       or they're a buggy twit coming through a HTTP/1.1 proxy
Packit 90a5c9
     *   and    the client is requesting an HTTP/1.0-style keep-alive
Packit 90a5c9
     *       or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
Packit 90a5c9
     *   and this MPM process is not already exiting
Packit 90a5c9
     *   THEN we can be persistent, which requires more headers be output.
Packit 90a5c9
     *
Packit 90a5c9
     * Note that the condition evaluation order is extremely important.
Packit 90a5c9
     */
Packit 90a5c9
    if ((r->connection->keepalive != AP_CONN_CLOSE)
Packit 90a5c9
        && !r->expecting_100
Packit 90a5c9
        && (r->header_only
Packit 90a5c9
            || AP_STATUS_IS_HEADER_ONLY(r->status)
Packit 90a5c9
            || apr_table_get(r->headers_out, "Content-Length")
Packit 90a5c9
            || ap_find_last_token(r->pool,
Packit 90a5c9
                                  apr_table_get(r->headers_out,
Packit 90a5c9
                                                "Transfer-Encoding"),
Packit 90a5c9
                                  "chunked")
Packit 90a5c9
            || ((r->proto_num >= HTTP_VERSION(1,1))
Packit 90a5c9
                && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
Packit 90a5c9
        && r->server->keep_alive
Packit 90a5c9
        && (r->server->keep_alive_timeout > 0)
Packit 90a5c9
        && ((r->server->keep_alive_max == 0)
Packit 90a5c9
            || (left > 0))
Packit 90a5c9
        && !ap_status_drops_connection(r->status)
Packit 90a5c9
        && !wimpy
Packit 90a5c9
        && !ap_find_token(r->pool, conn, "close")
Packit 90a5c9
        && (!apr_table_get(r->subprocess_env, "nokeepalive")
Packit 90a5c9
            || apr_table_get(r->headers_in, "Via"))
Packit 90a5c9
        && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
Packit 90a5c9
            || (r->proto_num >= HTTP_VERSION(1,1)))
Packit 90a5c9
        && is_mpm_running()) {
Packit 90a5c9
Packit 90a5c9
        r->connection->keepalive = AP_CONN_KEEPALIVE;
Packit 90a5c9
        r->connection->keepalives++;
Packit 90a5c9
Packit 90a5c9
        /* If they sent a Keep-Alive token, send one back */
Packit 90a5c9
        if (ka_sent) {
Packit 90a5c9
            if (r->server->keep_alive_max) {
Packit 90a5c9
                apr_table_setn(r->headers_out, "Keep-Alive",
Packit 90a5c9
                       apr_psprintf(r->pool, "timeout=%d, max=%d",
Packit 90a5c9
                            (int)apr_time_sec(r->server->keep_alive_timeout),
Packit 90a5c9
                            left));
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                apr_table_setn(r->headers_out, "Keep-Alive",
Packit 90a5c9
                      apr_psprintf(r->pool, "timeout=%d",
Packit 90a5c9
                            (int)apr_time_sec(r->server->keep_alive_timeout)));
Packit 90a5c9
            }
Packit 90a5c9
            apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Otherwise, we need to indicate that we will be closing this
Packit 90a5c9
     * connection immediately after the current response.
Packit 90a5c9
     *
Packit 90a5c9
     * We only really need to send "close" to HTTP/1.1 clients, but we
Packit 90a5c9
     * always send it anyway, because a broken proxy may identify itself
Packit 90a5c9
     * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
Packit 90a5c9
     * to a HTTP/1.1 client. Better safe than sorry.
Packit 90a5c9
     */
Packit 90a5c9
    if (!wimpy) {
Packit 90a5c9
        apr_table_mergen(r->headers_out, "Connection", "close");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * If we had previously been a keepalive connection and this
Packit 90a5c9
     * is the last one, then bump up the number of keepalives
Packit 90a5c9
     * we've had
Packit 90a5c9
     */
Packit 90a5c9
    if ((r->connection->keepalive != AP_CONN_CLOSE)
Packit 90a5c9
        && r->server->keep_alive_max
Packit 90a5c9
        && !left) {
Packit 90a5c9
        r->connection->keepalives++;
Packit 90a5c9
    }
Packit 90a5c9
    r->connection->keepalive = AP_CONN_CLOSE;
Packit 90a5c9
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(ap_condition_e) ap_condition_if_match(request_rec *r,
Packit 90a5c9
        apr_table_t *headers)
Packit 90a5c9
{
Packit 90a5c9
    const char *if_match, *etag;
Packit 90a5c9
Packit 90a5c9
    /* A server MUST use the strong comparison function (see section 13.3.3)
Packit 90a5c9
     * to compare the entity tags in If-Match.
Packit 90a5c9
     */
Packit 90a5c9
    if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
Packit 90a5c9
        if (if_match[0] == '*'
Packit 90a5c9
                || ((etag = apr_table_get(headers, "ETag")) != NULL
Packit 90a5c9
                        && ap_find_etag_strong(r->pool, if_match, etag))) {
Packit 90a5c9
            return AP_CONDITION_STRONG;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            return AP_CONDITION_NOMATCH;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return AP_CONDITION_NONE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(ap_condition_e) ap_condition_if_unmodified_since(request_rec *r,
Packit 90a5c9
        apr_table_t *headers)
Packit 90a5c9
{
Packit 90a5c9
    const char *if_unmodified;
Packit 90a5c9
Packit 90a5c9
    if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
Packit 90a5c9
    if (if_unmodified) {
Packit 90a5c9
        apr_int64_t mtime, reqtime;
Packit 90a5c9
Packit 90a5c9
        apr_time_t ius = apr_time_sec(apr_date_parse_http(if_unmodified));
Packit 90a5c9
Packit 90a5c9
        /* All of our comparisons must be in seconds, because that's the
Packit 90a5c9
         * highest time resolution the HTTP specification allows.
Packit 90a5c9
         */
Packit 90a5c9
        mtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                        apr_table_get(headers, "Last-Modified")));
Packit 90a5c9
        if (mtime == APR_DATE_BAD) {
Packit 90a5c9
            mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        reqtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                        apr_table_get(headers, "Date")));
Packit 90a5c9
        if (!reqtime) {
Packit 90a5c9
            reqtime = apr_time_sec(r->request_time);
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if ((ius != APR_DATE_BAD) && (mtime > ius)) {
Packit 90a5c9
            if (reqtime < mtime + 60) {
Packit 90a5c9
                if (apr_table_get(r->headers_in, "Range")) {
Packit 90a5c9
                    /* weak matches not allowed with Range requests */
Packit 90a5c9
                    return AP_CONDITION_NOMATCH;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    return AP_CONDITION_WEAK;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                return AP_CONDITION_STRONG;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            return AP_CONDITION_NOMATCH;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return AP_CONDITION_NONE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(ap_condition_e) ap_condition_if_none_match(request_rec *r,
Packit 90a5c9
        apr_table_t *headers)
Packit 90a5c9
{
Packit 90a5c9
    const char *if_nonematch, *etag;
Packit 90a5c9
Packit 90a5c9
    if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
Packit 90a5c9
    if (if_nonematch != NULL) {
Packit 90a5c9
Packit 90a5c9
        if (if_nonematch[0] == '*') {
Packit 90a5c9
            return AP_CONDITION_STRONG;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* See section 13.3.3 for rules on how to determine if two entities tags
Packit 90a5c9
         * match. The weak comparison function can only be used with GET or HEAD
Packit 90a5c9
         * requests.
Packit 90a5c9
         */
Packit 90a5c9
        if (r->method_number == M_GET) {
Packit 90a5c9
            if ((etag = apr_table_get(headers, "ETag")) != NULL) {
Packit 90a5c9
                if (apr_table_get(r->headers_in, "Range")) {
Packit 90a5c9
                    if (ap_find_etag_strong(r->pool, if_nonematch, etag)) {
Packit 90a5c9
                        return AP_CONDITION_STRONG;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    if (ap_find_etag_weak(r->pool, if_nonematch, etag)) {
Packit 90a5c9
                        return AP_CONDITION_WEAK;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        else if ((etag = apr_table_get(headers, "ETag")) != NULL
Packit 90a5c9
                && ap_find_etag_strong(r->pool, if_nonematch, etag)) {
Packit 90a5c9
            return AP_CONDITION_STRONG;
Packit 90a5c9
        }
Packit 90a5c9
        return AP_CONDITION_NOMATCH;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return AP_CONDITION_NONE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(ap_condition_e) ap_condition_if_modified_since(request_rec *r,
Packit 90a5c9
        apr_table_t *headers)
Packit 90a5c9
{
Packit 90a5c9
    const char *if_modified_since;
Packit 90a5c9
Packit 90a5c9
    if ((if_modified_since = apr_table_get(r->headers_in, "If-Modified-Since"))
Packit 90a5c9
            != NULL) {
Packit 90a5c9
        apr_int64_t mtime;
Packit 90a5c9
        apr_int64_t ims, reqtime;
Packit 90a5c9
Packit 90a5c9
        /* All of our comparisons must be in seconds, because that's the
Packit 90a5c9
         * highest time resolution the HTTP specification allows.
Packit 90a5c9
         */
Packit 90a5c9
Packit 90a5c9
        mtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                        apr_table_get(headers, "Last-Modified")));
Packit 90a5c9
        if (mtime == APR_DATE_BAD) {
Packit 90a5c9
            mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        reqtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                        apr_table_get(headers, "Date")));
Packit 90a5c9
        if (!reqtime) {
Packit 90a5c9
            reqtime = apr_time_sec(r->request_time);
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        ims = apr_time_sec(apr_date_parse_http(if_modified_since));
Packit 90a5c9
Packit 90a5c9
        if (ims >= mtime && ims <= reqtime) {
Packit 90a5c9
            if (reqtime < mtime + 60) {
Packit 90a5c9
                if (apr_table_get(r->headers_in, "Range")) {
Packit 90a5c9
                    /* weak matches not allowed with Range requests */
Packit 90a5c9
                    return AP_CONDITION_NOMATCH;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    return AP_CONDITION_WEAK;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                return AP_CONDITION_STRONG;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            return AP_CONDITION_NOMATCH;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return AP_CONDITION_NONE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(ap_condition_e) ap_condition_if_range(request_rec *r,
Packit 90a5c9
        apr_table_t *headers)
Packit 90a5c9
{
Packit 90a5c9
    const char *if_range, *etag;
Packit 90a5c9
Packit 90a5c9
    if ((if_range = apr_table_get(r->headers_in, "If-Range"))
Packit 90a5c9
            && apr_table_get(r->headers_in, "Range")) {
Packit 90a5c9
        if (if_range[0] == '"') {
Packit 90a5c9
Packit 90a5c9
            if ((etag = apr_table_get(headers, "ETag"))
Packit 90a5c9
                    && !strcmp(if_range, etag)) {
Packit 90a5c9
                return AP_CONDITION_STRONG;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                return AP_CONDITION_NOMATCH;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            apr_int64_t mtime;
Packit 90a5c9
            apr_int64_t rtime, reqtime;
Packit 90a5c9
Packit 90a5c9
            /* All of our comparisons must be in seconds, because that's the
Packit 90a5c9
             * highest time resolution the HTTP specification allows.
Packit 90a5c9
             */
Packit 90a5c9
Packit 90a5c9
            mtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                            apr_table_get(headers, "Last-Modified")));
Packit 90a5c9
            if (mtime == APR_DATE_BAD) {
Packit 90a5c9
                mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            reqtime = apr_time_sec(apr_date_parse_http(
Packit 90a5c9
                            apr_table_get(headers, "Date")));
Packit 90a5c9
            if (!reqtime) {
Packit 90a5c9
                reqtime = apr_time_sec(r->request_time);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            rtime = apr_time_sec(apr_date_parse_http(if_range));
Packit 90a5c9
Packit 90a5c9
            if (rtime == mtime) {
Packit 90a5c9
                if (reqtime < mtime + 60) {
Packit 90a5c9
                    /* weak matches not allowed with Range requests */
Packit 90a5c9
                    return AP_CONDITION_NOMATCH;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    return AP_CONDITION_STRONG;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                return AP_CONDITION_NOMATCH;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return AP_CONDITION_NONE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(int) ap_meets_conditions(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    int not_modified = -1; /* unset by default */
Packit 90a5c9
    ap_condition_e cond;
Packit 90a5c9
Packit 90a5c9
    /* Check for conditional requests --- note that we only want to do
Packit 90a5c9
     * this if we are successful so far and we are not processing a
Packit 90a5c9
     * subrequest or an ErrorDocument.
Packit 90a5c9
     *
Packit 90a5c9
     * The order of the checks is important, since ETag checks are supposed
Packit 90a5c9
     * to be more accurate than checks relative to the modification time.
Packit 90a5c9
     * However, not all documents are guaranteed to *have* ETags, and some
Packit 90a5c9
     * might have Last-Modified values w/o ETags, so this gets a little
Packit 90a5c9
     * complicated.
Packit 90a5c9
     */
Packit 90a5c9
Packit 90a5c9
    if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* If an If-Match request-header field was given
Packit 90a5c9
     * AND the field value is not "*" (meaning match anything)
Packit 90a5c9
     * AND if our strong ETag does not match any entity tag in that field,
Packit 90a5c9
     *     respond with a status of 412 (Precondition Failed).
Packit 90a5c9
     */
Packit 90a5c9
    cond = ap_condition_if_match(r, r->headers_out);
Packit 90a5c9
    if (AP_CONDITION_NOMATCH == cond) {
Packit 90a5c9
        return HTTP_PRECONDITION_FAILED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Else if a valid If-Unmodified-Since request-header field was given
Packit 90a5c9
     * AND the requested resource has been modified since the time
Packit 90a5c9
     * specified in this field, then the server MUST
Packit 90a5c9
     *     respond with a status of 412 (Precondition Failed).
Packit 90a5c9
     */
Packit 90a5c9
    cond = ap_condition_if_unmodified_since(r, r->headers_out);
Packit 90a5c9
    if (AP_CONDITION_NOMATCH == cond) {
Packit 90a5c9
        not_modified = 0;
Packit 90a5c9
    }
Packit 90a5c9
    else if (cond >= AP_CONDITION_WEAK) {
Packit 90a5c9
        return HTTP_PRECONDITION_FAILED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* If an If-None-Match request-header field was given
Packit 90a5c9
     * AND the field value is "*" (meaning match anything)
Packit 90a5c9
     *     OR our ETag matches any of the entity tags in that field, fail.
Packit 90a5c9
     *
Packit 90a5c9
     * If the request method was GET or HEAD, failure means the server
Packit 90a5c9
     *    SHOULD respond with a 304 (Not Modified) response.
Packit 90a5c9
     * For all other request methods, failure means the server MUST
Packit 90a5c9
     *    respond with a status of 412 (Precondition Failed).
Packit 90a5c9
     *
Packit 90a5c9
     * GET or HEAD allow weak etag comparison, all other methods require
Packit 90a5c9
     * strong comparison.  We can only use weak if it's not a range request.
Packit 90a5c9
     */
Packit 90a5c9
    cond = ap_condition_if_none_match(r, r->headers_out);
Packit 90a5c9
    if (AP_CONDITION_NOMATCH == cond) {
Packit 90a5c9
        not_modified = 0;
Packit 90a5c9
    }
Packit 90a5c9
    else if (cond >= AP_CONDITION_WEAK) {
Packit 90a5c9
        if (r->method_number == M_GET) {
Packit 90a5c9
            if (not_modified) {
Packit 90a5c9
                not_modified = 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            return HTTP_PRECONDITION_FAILED;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* If a valid If-Modified-Since request-header field was given
Packit 90a5c9
     * AND it is a GET or HEAD request
Packit 90a5c9
     * AND the requested resource has not been modified since the time
Packit 90a5c9
     * specified in this field, then the server MUST
Packit 90a5c9
     *    respond with a status of 304 (Not Modified).
Packit 90a5c9
     * A date later than the server's current request time is invalid.
Packit 90a5c9
     */
Packit 90a5c9
    cond = ap_condition_if_modified_since(r, r->headers_out);
Packit 90a5c9
    if (AP_CONDITION_NOMATCH == cond) {
Packit 90a5c9
        not_modified = 0;
Packit 90a5c9
    }
Packit 90a5c9
    else if (cond >= AP_CONDITION_WEAK) {
Packit 90a5c9
        if (r->method_number == M_GET) {
Packit 90a5c9
            if (not_modified) {
Packit 90a5c9
                not_modified = 1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* If an If-Range and an Range header is present, we must return
Packit 90a5c9
     * 200 OK. The byterange filter will convert it to a range response.
Packit 90a5c9
     */
Packit 90a5c9
    cond = ap_condition_if_range(r, r->headers_out);
Packit 90a5c9
    if (cond > AP_CONDITION_NONE) {
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (not_modified == 1) {
Packit 90a5c9
        return HTTP_NOT_MODIFIED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * Singleton registry of additional methods. This maps new method names
Packit 90a5c9
 * such as "MYGET" to methnums, which are int offsets into bitmasks.
Packit 90a5c9
 *
Packit 90a5c9
 * This follows the same technique as standard M_GET, M_POST, etc. These
Packit 90a5c9
 * are dynamically assigned when modules are loaded and <Limit GET MYGET>
Packit 90a5c9
 * directives are processed.
Packit 90a5c9
 */
Packit 90a5c9
static apr_hash_t *methods_registry = NULL;
Packit 90a5c9
static int cur_method_number = METHOD_NUMBER_FIRST;
Packit 90a5c9
Packit 90a5c9
/* internal function to register one method/number pair */
Packit 90a5c9
static void register_one_method(apr_pool_t *p, const char *methname,
Packit 90a5c9
                                int methnum)
Packit 90a5c9
{
Packit 90a5c9
    int *pnum = apr_palloc(p, sizeof(*pnum));
Packit 90a5c9
Packit 90a5c9
    *pnum = methnum;
Packit 90a5c9
    apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* This internal function is used to clear the method registry
Packit 90a5c9
 * and reset the cur_method_number counter.
Packit 90a5c9
 */
Packit 90a5c9
static apr_status_t ap_method_registry_destroy(void *notused)
Packit 90a5c9
{
Packit 90a5c9
    methods_registry = NULL;
Packit 90a5c9
    cur_method_number = METHOD_NUMBER_FIRST;
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    methods_registry = apr_hash_make(p);
Packit 90a5c9
    apr_pool_cleanup_register(p, NULL,
Packit 90a5c9
                              ap_method_registry_destroy,
Packit 90a5c9
                              apr_pool_cleanup_null);
Packit 90a5c9
Packit 90a5c9
    /* put all the standard methods into the registry hash to ease the
Packit 90a5c9
     * mapping operations between name and number
Packit 90a5c9
     * HEAD is a special-instance of the GET method and shares the same ID
Packit 90a5c9
     */
Packit 90a5c9
    register_one_method(p, "GET", M_GET);
Packit 90a5c9
    register_one_method(p, "HEAD", M_GET);
Packit 90a5c9
    register_one_method(p, "PUT", M_PUT);
Packit 90a5c9
    register_one_method(p, "POST", M_POST);
Packit 90a5c9
    register_one_method(p, "DELETE", M_DELETE);
Packit 90a5c9
    register_one_method(p, "CONNECT", M_CONNECT);
Packit 90a5c9
    register_one_method(p, "OPTIONS", M_OPTIONS);
Packit 90a5c9
    register_one_method(p, "TRACE", M_TRACE);
Packit 90a5c9
    register_one_method(p, "PATCH", M_PATCH);
Packit 90a5c9
    register_one_method(p, "PROPFIND", M_PROPFIND);
Packit 90a5c9
    register_one_method(p, "PROPPATCH", M_PROPPATCH);
Packit 90a5c9
    register_one_method(p, "MKCOL", M_MKCOL);
Packit 90a5c9
    register_one_method(p, "COPY", M_COPY);
Packit 90a5c9
    register_one_method(p, "MOVE", M_MOVE);
Packit 90a5c9
    register_one_method(p, "LOCK", M_LOCK);
Packit 90a5c9
    register_one_method(p, "UNLOCK", M_UNLOCK);
Packit 90a5c9
    register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
Packit 90a5c9
    register_one_method(p, "CHECKOUT", M_CHECKOUT);
Packit 90a5c9
    register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
Packit 90a5c9
    register_one_method(p, "CHECKIN", M_CHECKIN);
Packit 90a5c9
    register_one_method(p, "UPDATE", M_UPDATE);
Packit 90a5c9
    register_one_method(p, "LABEL", M_LABEL);
Packit 90a5c9
    register_one_method(p, "REPORT", M_REPORT);
Packit 90a5c9
    register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
Packit 90a5c9
    register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
Packit 90a5c9
    register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
Packit 90a5c9
    register_one_method(p, "MERGE", M_MERGE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
Packit 90a5c9
{
Packit 90a5c9
    int *methnum;
Packit 90a5c9
Packit 90a5c9
    if (methods_registry == NULL) {
Packit 90a5c9
        ap_method_registry_init(p);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (methname == NULL) {
Packit 90a5c9
        return M_INVALID;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Check if the method was previously registered.  If it was
Packit 90a5c9
     * return the associated method number.
Packit 90a5c9
     */
Packit 90a5c9
    methnum = (int *)apr_hash_get(methods_registry, methname,
Packit 90a5c9
                                  APR_HASH_KEY_STRING);
Packit 90a5c9
    if (methnum != NULL)
Packit 90a5c9
        return *methnum;
Packit 90a5c9
Packit 90a5c9
    if (cur_method_number > METHOD_NUMBER_LAST) {
Packit 90a5c9
        /* The method registry  has run out of dynamically
Packit 90a5c9
         * assignable method numbers. Log this and return M_INVALID.
Packit 90a5c9
         */
Packit 90a5c9
        ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, APLOGNO(01610)
Packit 90a5c9
                      "Maximum new request methods %d reached while "
Packit 90a5c9
                      "registering method %s.",
Packit 90a5c9
                      METHOD_NUMBER_LAST, methname);
Packit 90a5c9
        return M_INVALID;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    register_one_method(p, methname, cur_method_number);
Packit 90a5c9
    return cur_method_number++;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#define UNKNOWN_METHOD (-1)
Packit 90a5c9
Packit 90a5c9
static int lookup_builtin_method(const char *method, apr_size_t len)
Packit 90a5c9
{
Packit 90a5c9
    /* Note: the following code was generated by the "shilka" tool from
Packit 90a5c9
       the "cocom" parsing/compilation toolkit. It is an optimized lookup
Packit 90a5c9
       based on analysis of the input keywords. Postprocessing was done
Packit 90a5c9
       on the shilka output, but the basic structure and analysis is
Packit 90a5c9
       from there. Should new HTTP methods be added, then manual insertion
Packit 90a5c9
       into this code is fine, or simply re-running the shilka tool on
Packit 90a5c9
       the appropriate input. */
Packit 90a5c9
Packit 90a5c9
    /* Note: it is also quite reasonable to just use our method_registry,
Packit 90a5c9
       but I'm assuming (probably incorrectly) we want more speed here
Packit 90a5c9
       (based on the optimizations the previous code was doing). */
Packit 90a5c9
Packit 90a5c9
    switch (len)
Packit 90a5c9
    {
Packit 90a5c9
    case 3:
Packit 90a5c9
        switch (method[0])
Packit 90a5c9
        {
Packit 90a5c9
        case 'P':
Packit 90a5c9
            return (method[1] == 'U'
Packit 90a5c9
                    && method[2] == 'T'
Packit 90a5c9
                    ? M_PUT : UNKNOWN_METHOD);
Packit 90a5c9
        case 'G':
Packit 90a5c9
            return (method[1] == 'E'
Packit 90a5c9
                    && method[2] == 'T'
Packit 90a5c9
                    ? M_GET : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 4:
Packit 90a5c9
        switch (method[0])
Packit 90a5c9
        {
Packit 90a5c9
        case 'H':
Packit 90a5c9
            return (method[1] == 'E'
Packit 90a5c9
                    && method[2] == 'A'
Packit 90a5c9
                    && method[3] == 'D'
Packit 90a5c9
                    ? M_GET : UNKNOWN_METHOD);
Packit 90a5c9
        case 'P':
Packit 90a5c9
            return (method[1] == 'O'
Packit 90a5c9
                    && method[2] == 'S'
Packit 90a5c9
                    && method[3] == 'T'
Packit 90a5c9
                    ? M_POST : UNKNOWN_METHOD);
Packit 90a5c9
        case 'M':
Packit 90a5c9
            return (method[1] == 'O'
Packit 90a5c9
                    && method[2] == 'V'
Packit 90a5c9
                    && method[3] == 'E'
Packit 90a5c9
                    ? M_MOVE : UNKNOWN_METHOD);
Packit 90a5c9
        case 'L':
Packit 90a5c9
            return (method[1] == 'O'
Packit 90a5c9
                    && method[2] == 'C'
Packit 90a5c9
                    && method[3] == 'K'
Packit 90a5c9
                    ? M_LOCK : UNKNOWN_METHOD);
Packit 90a5c9
        case 'C':
Packit 90a5c9
            return (method[1] == 'O'
Packit 90a5c9
                    && method[2] == 'P'
Packit 90a5c9
                    && method[3] == 'Y'
Packit 90a5c9
                    ? M_COPY : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 5:
Packit 90a5c9
        switch (method[2])
Packit 90a5c9
        {
Packit 90a5c9
        case 'T':
Packit 90a5c9
            return (memcmp(method, "PATCH", 5) == 0
Packit 90a5c9
                    ? M_PATCH : UNKNOWN_METHOD);
Packit 90a5c9
        case 'R':
Packit 90a5c9
            return (memcmp(method, "MERGE", 5) == 0
Packit 90a5c9
                    ? M_MERGE : UNKNOWN_METHOD);
Packit 90a5c9
        case 'C':
Packit 90a5c9
            return (memcmp(method, "MKCOL", 5) == 0
Packit 90a5c9
                    ? M_MKCOL : UNKNOWN_METHOD);
Packit 90a5c9
        case 'B':
Packit 90a5c9
            return (memcmp(method, "LABEL", 5) == 0
Packit 90a5c9
                    ? M_LABEL : UNKNOWN_METHOD);
Packit 90a5c9
        case 'A':
Packit 90a5c9
            return (memcmp(method, "TRACE", 5) == 0
Packit 90a5c9
                    ? M_TRACE : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 6:
Packit 90a5c9
        switch (method[0])
Packit 90a5c9
        {
Packit 90a5c9
        case 'U':
Packit 90a5c9
            switch (method[5])
Packit 90a5c9
            {
Packit 90a5c9
            case 'K':
Packit 90a5c9
                return (memcmp(method, "UNLOCK", 6) == 0
Packit 90a5c9
                        ? M_UNLOCK : UNKNOWN_METHOD);
Packit 90a5c9
            case 'E':
Packit 90a5c9
                return (memcmp(method, "UPDATE", 6) == 0
Packit 90a5c9
                        ? M_UPDATE : UNKNOWN_METHOD);
Packit 90a5c9
            default:
Packit 90a5c9
                return UNKNOWN_METHOD;
Packit 90a5c9
            }
Packit 90a5c9
        case 'R':
Packit 90a5c9
            return (memcmp(method, "REPORT", 6) == 0
Packit 90a5c9
                    ? M_REPORT : UNKNOWN_METHOD);
Packit 90a5c9
        case 'D':
Packit 90a5c9
            return (memcmp(method, "DELETE", 6) == 0
Packit 90a5c9
                    ? M_DELETE : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 7:
Packit 90a5c9
        switch (method[1])
Packit 90a5c9
        {
Packit 90a5c9
        case 'P':
Packit 90a5c9
            return (memcmp(method, "OPTIONS", 7) == 0
Packit 90a5c9
                    ? M_OPTIONS : UNKNOWN_METHOD);
Packit 90a5c9
        case 'O':
Packit 90a5c9
            return (memcmp(method, "CONNECT", 7) == 0
Packit 90a5c9
                    ? M_CONNECT : UNKNOWN_METHOD);
Packit 90a5c9
        case 'H':
Packit 90a5c9
            return (memcmp(method, "CHECKIN", 7) == 0
Packit 90a5c9
                    ? M_CHECKIN : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 8:
Packit 90a5c9
        switch (method[0])
Packit 90a5c9
        {
Packit 90a5c9
        case 'P':
Packit 90a5c9
            return (memcmp(method, "PROPFIND", 8) == 0
Packit 90a5c9
                    ? M_PROPFIND : UNKNOWN_METHOD);
Packit 90a5c9
        case 'C':
Packit 90a5c9
            return (memcmp(method, "CHECKOUT", 8) == 0
Packit 90a5c9
                    ? M_CHECKOUT : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 9:
Packit 90a5c9
        return (memcmp(method, "PROPPATCH", 9) == 0
Packit 90a5c9
                ? M_PROPPATCH : UNKNOWN_METHOD);
Packit 90a5c9
Packit 90a5c9
    case 10:
Packit 90a5c9
        switch (method[0])
Packit 90a5c9
        {
Packit 90a5c9
        case 'U':
Packit 90a5c9
            return (memcmp(method, "UNCHECKOUT", 10) == 0
Packit 90a5c9
                    ? M_UNCHECKOUT : UNKNOWN_METHOD);
Packit 90a5c9
        case 'M':
Packit 90a5c9
            return (memcmp(method, "MKACTIVITY", 10) == 0
Packit 90a5c9
                    ? M_MKACTIVITY : UNKNOWN_METHOD);
Packit 90a5c9
        default:
Packit 90a5c9
            return UNKNOWN_METHOD;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    case 11:
Packit 90a5c9
        return (memcmp(method, "MKWORKSPACE", 11) == 0
Packit 90a5c9
                ? M_MKWORKSPACE : UNKNOWN_METHOD);
Packit 90a5c9
Packit 90a5c9
    case 15:
Packit 90a5c9
        return (memcmp(method, "VERSION-CONTROL", 15) == 0
Packit 90a5c9
                ? M_VERSION_CONTROL : UNKNOWN_METHOD);
Packit 90a5c9
Packit 90a5c9
    case 16:
Packit 90a5c9
        return (memcmp(method, "BASELINE-CONTROL", 16) == 0
Packit 90a5c9
                ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
Packit 90a5c9
Packit 90a5c9
    default:
Packit 90a5c9
        return UNKNOWN_METHOD;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* NOTREACHED */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* Get the method number associated with the given string, assumed to
Packit 90a5c9
 * contain an HTTP method.  Returns M_INVALID if not recognized.
Packit 90a5c9
 *
Packit 90a5c9
 * This is the first step toward placing method names in a configurable
Packit 90a5c9
 * list.  Hopefully it (and other routines) can eventually be moved to
Packit 90a5c9
 * something like a mod_http_methods.c, complete with config stuff.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(int) ap_method_number_of(const char *method)
Packit 90a5c9
{
Packit 90a5c9
    int len = strlen(method);
Packit 90a5c9
    int which = lookup_builtin_method(method, len);
Packit 90a5c9
Packit 90a5c9
    if (which != UNKNOWN_METHOD)
Packit 90a5c9
        return which;
Packit 90a5c9
Packit 90a5c9
    /* check if the method has been dynamically registered */
Packit 90a5c9
    if (methods_registry != NULL) {
Packit 90a5c9
        int *methnum = apr_hash_get(methods_registry, method, len);
Packit 90a5c9
Packit 90a5c9
        if (methnum != NULL) {
Packit 90a5c9
            return *methnum;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return M_INVALID;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Turn a known method number into a name.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
Packit 90a5c9
{
Packit 90a5c9
    apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
Packit 90a5c9
Packit 90a5c9
    /* scan through the hash table, looking for a value that matches
Packit 90a5c9
       the provided method number. */
Packit 90a5c9
    for (; hi; hi = apr_hash_next(hi)) {
Packit 90a5c9
        const void *key;
Packit 90a5c9
        void *val;
Packit 90a5c9
Packit 90a5c9
        apr_hash_this(hi, &key, NULL, &val;;
Packit 90a5c9
        if (*(int *)val == methnum)
Packit 90a5c9
            return key;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* it wasn't found in the hash */
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* The index is found by its offset from the x00 code of each level.
Packit 90a5c9
 * Although this is fast, it will need to be replaced if some nutcase
Packit 90a5c9
 * decides to define a high-numbered code before the lower numbers.
Packit 90a5c9
 * If that sad event occurs, replace the code below with a linear search
Packit 90a5c9
 * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
Packit 90a5c9
 * or use NULL to fill the gaps.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(int) ap_index_of_response(int status)
Packit 90a5c9
{
Packit 90a5c9
    static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
Packit 90a5c9
    LEVEL_500, RESPONSE_CODES};
Packit 90a5c9
    int i, pos;
Packit 90a5c9
Packit 90a5c9
    if (status < 100) {               /* Below 100 is illegal for HTTP status */
Packit 90a5c9
        return LEVEL_500;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < 5; i++) {
Packit 90a5c9
        status -= 100;
Packit 90a5c9
        if (status < 100) {
Packit 90a5c9
            pos = (status + shortcut[i]);
Packit 90a5c9
            if (pos < shortcut[i + 1] && status_lines[pos] != NULL) {
Packit 90a5c9
                return pos;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                return LEVEL_500;            /* status unknown (falls in gap) */
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return LEVEL_500;                         /* 600 or above is also illegal */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(const char *) ap_get_status_line(int status)
Packit 90a5c9
{
Packit 90a5c9
    return status_lines[ap_index_of_response(status)];
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* Build the Allow field-value from the request handler method mask.
Packit 90a5c9
 */
Packit 90a5c9
static char *make_allow(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    apr_int64_t mask;
Packit 90a5c9
    apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
Packit 90a5c9
    apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
Packit 90a5c9
    /* For TRACE below */
Packit 90a5c9
    core_server_config *conf =
Packit 90a5c9
        ap_get_core_module_config(r->server->module_config);
Packit 90a5c9
Packit 90a5c9
    mask = r->allowed_methods->method_mask;
Packit 90a5c9
Packit 90a5c9
    for (; hi; hi = apr_hash_next(hi)) {
Packit 90a5c9
        const void *key;
Packit 90a5c9
        void *val;
Packit 90a5c9
Packit 90a5c9
        apr_hash_this(hi, &key, NULL, &val;;
Packit 90a5c9
        if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
Packit 90a5c9
            APR_ARRAY_PUSH(allow, const char *) = key;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* TRACE is tested on a per-server basis */
Packit 90a5c9
    if (conf->trace_enable != AP_TRACE_DISABLE)
Packit 90a5c9
        *(const char **)apr_array_push(allow) = "TRACE";
Packit 90a5c9
Packit 90a5c9
    /* ### this is rather annoying. we should enforce registration of
Packit 90a5c9
       ### these methods */
Packit 90a5c9
    if ((mask & (AP_METHOD_BIT << M_INVALID))
Packit 90a5c9
        && (r->allowed_methods->method_list != NULL)
Packit 90a5c9
        && (r->allowed_methods->method_list->nelts != 0)) {
Packit 90a5c9
        apr_array_cat(allow, r->allowed_methods->method_list);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return apr_array_pstrcat(r->pool, allow, ',');
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(int) ap_send_http_options(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    if (r->assbackwards) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_table_setn(r->headers_out, "Allow", make_allow(r));
Packit 90a5c9
Packit 90a5c9
    /* the request finalization will send an EOS, which will flush all
Packit 90a5c9
     * the headers out (including the Allow header)
Packit 90a5c9
     */
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
Packit 90a5c9
{
Packit 90a5c9
    if (!ct) {
Packit 90a5c9
        r->content_type = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!r->content_type || strcmp(r->content_type, ct)) {
Packit 90a5c9
        r->content_type = ct;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(void) ap_set_accept_ranges(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
Packit 90a5c9
    apr_table_setn(r->headers_out, "Accept-Ranges",
Packit 90a5c9
                  (d->max_ranges == AP_MAXRANGES_NORANGES) ? "none"
Packit 90a5c9
                                                           : "bytes");
Packit 90a5c9
}
Packit 90a5c9
static const char *add_optional_notes(request_rec *r,
Packit 90a5c9
                                      const char *prefix,
Packit 90a5c9
                                      const char *key,
Packit 90a5c9
                                      const char *suffix)
Packit 90a5c9
{
Packit 90a5c9
    const char *notes, *result;
Packit 90a5c9
Packit 90a5c9
    if ((notes = apr_table_get(r->notes, key)) == NULL) {
Packit 90a5c9
        result = apr_pstrcat(r->pool, prefix, suffix, NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return result;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* construct and return the default error message for a given
Packit 90a5c9
 * HTTP defined error code
Packit 90a5c9
 */
Packit 90a5c9
static const char *get_canned_error_string(int status,
Packit 90a5c9
                                           request_rec *r,
Packit 90a5c9
                                           const char *location)
Packit 90a5c9
{
Packit 90a5c9
    apr_pool_t *p = r->pool;
Packit 90a5c9
    const char *error_notes, *h1, *s1;
Packit 90a5c9
Packit 90a5c9
    switch (status) {
Packit 90a5c9
    case HTTP_MOVED_PERMANENTLY:
Packit 90a5c9
    case HTTP_MOVED_TEMPORARILY:
Packit 90a5c9
    case HTTP_TEMPORARY_REDIRECT:
Packit 90a5c9
    case HTTP_PERMANENT_REDIRECT:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The document has moved

Packit 90a5c9
                           ap_escape_html(r->pool, location),
Packit 90a5c9
                           "\">here.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_SEE_OTHER:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The answer to your request is located "

Packit 90a5c9
                           "
Packit 90a5c9
                           ap_escape_html(r->pool, location),
Packit 90a5c9
                           "\">here.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_USE_PROXY:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

This resource is only accessible "

Packit 90a5c9
                           "through the proxy\n",
Packit 90a5c9
                           ap_escape_html(r->pool, location),
Packit 90a5c9
                           "
\nYou will need to configure "
Packit 90a5c9
                           "your client to use that proxy.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_PROXY_AUTHENTICATION_REQUIRED:
Packit 90a5c9
    case HTTP_UNAUTHORIZED:
Packit 90a5c9
        return("

This server could not verify that you\n"

Packit 90a5c9
               "are authorized to access the document\n"
Packit 90a5c9
               "requested.  Either you supplied the wrong\n"
Packit 90a5c9
               "credentials (e.g., bad password), or your\n"
Packit 90a5c9
               "browser doesn't understand how to supply\n"
Packit 90a5c9
               "the credentials required.

\n");
Packit 90a5c9
    case HTTP_BAD_REQUEST:
Packit 90a5c9
        return(add_optional_notes(r,
Packit 90a5c9
                                  "

Your browser sent a request that "

Packit 90a5c9
                                  "this server could not understand.
\n",
Packit 90a5c9
                                  "error-notes",
Packit 90a5c9
                                  "

\n"));
Packit 90a5c9
    case HTTP_FORBIDDEN:
Packit 90a5c9
        s1 = apr_pstrcat(p,
Packit 90a5c9
                         "

You don't have permission to access ",

Packit 90a5c9
                         ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                         "\non this server.
\n",
Packit 90a5c9
                         NULL);
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    case HTTP_NOT_FOUND:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The requested URL ",

Packit 90a5c9
                           ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                           " was not found on this server.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_METHOD_NOT_ALLOWED:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The requested method ",

Packit 90a5c9
                           ap_escape_html(r->pool, r->method),
Packit 90a5c9
                           " is not allowed for the URL ",
Packit 90a5c9
                           ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                           ".

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_NOT_ACCEPTABLE:
Packit 90a5c9
        s1 = apr_pstrcat(p,
Packit 90a5c9
                         "

An appropriate representation of the "

Packit 90a5c9
                         "requested resource ",
Packit 90a5c9
                         ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                         " could not be found on this server.

\n",
Packit 90a5c9
                         NULL);
Packit 90a5c9
        return(add_optional_notes(r, s1, "variant-list", ""));
Packit 90a5c9
    case HTTP_MULTIPLE_CHOICES:
Packit 90a5c9
        return(add_optional_notes(r, "", "variant-list", ""));
Packit 90a5c9
    case HTTP_LENGTH_REQUIRED:
Packit 90a5c9
        s1 = apr_pstrcat(p,
Packit 90a5c9
                         "

A request of the requested method ",

Packit 90a5c9
                         ap_escape_html(r->pool, r->method),
Packit 90a5c9
                         " requires a valid Content-length.
\n",
Packit 90a5c9
                         NULL);
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    case HTTP_PRECONDITION_FAILED:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The precondition on the request "

Packit 90a5c9
                           "for the URL ",
Packit 90a5c9
                           ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                           " evaluated to false.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_NOT_IMPLEMENTED:
Packit 90a5c9
        s1 = apr_pstrcat(p,
Packit 90a5c9
                         "

",

Packit 90a5c9
                         ap_escape_html(r->pool, r->method), " to ",
Packit 90a5c9
                         ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                         " not supported.
\n",
Packit 90a5c9
                         NULL);
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    case HTTP_BAD_GATEWAY:
Packit 90a5c9
        s1 = "

The proxy server received an invalid" CRLF

Packit 90a5c9
            "response from an upstream server.
" CRLF;
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    case HTTP_VARIANT_ALSO_VARIES:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

A variant for the requested "

Packit 90a5c9
                           "resource\n
\n",
Packit 90a5c9
                           ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                           "\n\nis itself a negotiable resource. "
Packit 90a5c9
                           "This indicates a configuration error.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_REQUEST_TIME_OUT:
Packit 90a5c9
        return("

Server timeout waiting for the HTTP request from the client.

\n");
Packit 90a5c9
    case HTTP_GONE:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "

The requested resource
",

Packit 90a5c9
                           ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                           "
\nis no longer available on this server "
Packit 90a5c9
                           "and there is no forwarding address.\n"
Packit 90a5c9
                           "Please remove all references to this "
Packit 90a5c9
                           "resource.

\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_REQUEST_ENTITY_TOO_LARGE:
Packit 90a5c9
        return(apr_pstrcat(p,
Packit 90a5c9
                           "The requested resource
",
Packit 90a5c9
                           ap_escape_html(r->pool, r->uri), "
\n",
Packit 90a5c9
                           "does not allow request data with ",
Packit 90a5c9
                           ap_escape_html(r->pool, r->method),
Packit 90a5c9
                           " requests, or the amount of data provided in\n"
Packit 90a5c9
                           "the request exceeds the capacity limit.\n",
Packit 90a5c9
                           NULL));
Packit 90a5c9
    case HTTP_REQUEST_URI_TOO_LARGE:
Packit 90a5c9
        s1 = "

The requested URL's length exceeds the capacity\n"

Packit 90a5c9
             "limit for this server.
\n";
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    case HTTP_UNSUPPORTED_MEDIA_TYPE:
Packit 90a5c9
        return("

The supplied request data is not in a format\n"

Packit 90a5c9
               "acceptable for processing by this resource.

\n");
Packit 90a5c9
    case HTTP_RANGE_NOT_SATISFIABLE:
Packit 90a5c9
        return("

None of the range-specifier values in the Range\n"

Packit 90a5c9
               "request-header field overlap the current extent\n"
Packit 90a5c9
               "of the selected resource.

\n");
Packit 90a5c9
    case HTTP_EXPECTATION_FAILED:
Packit 90a5c9
        s1 = apr_table_get(r->headers_in, "Expect");
Packit 90a5c9
        if (s1)
Packit 90a5c9
            s1 = apr_pstrcat(p,
Packit 90a5c9
                     "

The expectation given in the Expect request-header\n"

Packit 90a5c9
                     "field could not be met by this server.\n"
Packit 90a5c9
                     "The client sent
\n    Expect: ",
Packit 90a5c9
                     ap_escape_html(r->pool, s1), "\n\n",
Packit 90a5c9
                     NULL);
Packit 90a5c9
        else
Packit 90a5c9
            s1 = "

No expectation was seen, the Expect request-header \n"

Packit 90a5c9
                 "field was not presented by the client.\n";
Packit 90a5c9
        return add_optional_notes(r, s1, "error-notes", "

"
Packit 90a5c9
                   "

Only the 100-continue expectation is supported.

\n");
Packit 90a5c9
    case HTTP_UNPROCESSABLE_ENTITY:
Packit 90a5c9
        return("

The server understands the media type of the\n"

Packit 90a5c9
               "request entity, but was unable to process the\n"
Packit 90a5c9
               "contained instructions.

\n");
Packit 90a5c9
    case HTTP_LOCKED:
Packit 90a5c9
        return("

The requested resource is currently locked.\n"

Packit 90a5c9
               "The lock must be released or proper identification\n"
Packit 90a5c9
               "given before the method can be applied.

\n");
Packit 90a5c9
    case HTTP_FAILED_DEPENDENCY:
Packit 90a5c9
        return("

The method could not be performed on the resource\n"

Packit 90a5c9
               "because the requested action depended on another\n"
Packit 90a5c9
               "action and that other action failed.

\n");
Packit 90a5c9
    case HTTP_UPGRADE_REQUIRED:
Packit 90a5c9
        return("

The requested resource can only be retrieved\n"

Packit 90a5c9
               "using SSL.  The server is willing to upgrade the current\n"
Packit 90a5c9
               "connection to SSL, but your client doesn't support it.\n"
Packit 90a5c9
               "Either upgrade your client, or try requesting the page\n"
Packit 90a5c9
               "using https://\n");
Packit 90a5c9
    case HTTP_PRECONDITION_REQUIRED:
Packit 90a5c9
        return("

The request is required to be conditional.

\n");
Packit 90a5c9
    case HTTP_TOO_MANY_REQUESTS:
Packit 90a5c9
        return("

The user has sent too many requests\n"

Packit 90a5c9
               "in a given amount of time.

\n");
Packit 90a5c9
    case HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE:
Packit 90a5c9
        return("

The server refused this request because\n"

Packit 90a5c9
               "the request header fields are too large.

\n");
Packit 90a5c9
    case HTTP_INSUFFICIENT_STORAGE:
Packit 90a5c9
        return("

The method could not be performed on the resource\n"

Packit 90a5c9
               "because the server is unable to store the\n"
Packit 90a5c9
               "representation needed to successfully complete the\n"
Packit 90a5c9
               "request.  There is insufficient free space left in\n"
Packit 90a5c9
               "your storage allocation.

\n");
Packit 90a5c9
    case HTTP_SERVICE_UNAVAILABLE:
Packit 90a5c9
        return("

The server is temporarily unable to service your\n"

Packit 90a5c9
               "request due to maintenance downtime or capacity\n"
Packit 90a5c9
               "problems. Please try again later.

\n");
Packit 90a5c9
    case HTTP_GATEWAY_TIME_OUT:
Packit 90a5c9
        return("

The gateway did not receive a timely response\n"

Packit 90a5c9
               "from the upstream server or application.

\n");
Packit 90a5c9
    case HTTP_LOOP_DETECTED:
Packit 90a5c9
        return("

The server terminated an operation because\n"

Packit 90a5c9
               "it encountered an infinite loop.

\n");
Packit 90a5c9
    case HTTP_NOT_EXTENDED:
Packit 90a5c9
        return("

A mandatory extension policy in the request is not\n"

Packit 90a5c9
               "accepted by the server for this resource.

\n");
Packit 90a5c9
    case HTTP_NETWORK_AUTHENTICATION_REQUIRED:
Packit 90a5c9
        return("

The client needs to authenticate to gain\n"

Packit 90a5c9
               "network access.

\n");
Packit 90a5c9
    case HTTP_MISDIRECTED_REQUEST:
Packit 90a5c9
        return("

The client needs a new connection for this\n"

Packit 90a5c9
               "request as the requested host name does not match\n"
Packit 90a5c9
               "the Server Name Indication (SNI) in use for this\n"
Packit 90a5c9
               "connection.

\n");
Packit 90a5c9
    case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
Packit 90a5c9
        s1 = apr_pstrcat(p,
Packit 90a5c9
                         "

Access to ", ap_escape_html(r->pool, r->uri),

Packit 90a5c9
                         "\nhas been denied for legal reasons.
\n",
Packit 90a5c9
                         NULL);
Packit 90a5c9
        return(add_optional_notes(r, s1, "error-notes", "

\n"));
Packit 90a5c9
    default:                    /* HTTP_INTERNAL_SERVER_ERROR */
Packit 90a5c9
        /*
Packit 90a5c9
         * This comparison to expose error-notes could be modified to
Packit 90a5c9
         * use a configuration directive and export based on that
Packit 90a5c9
         * directive.  For now "*" is used to designate an error-notes
Packit 90a5c9
         * that is totally safe for any user to see (ie lacks paths,
Packit 90a5c9
         * database passwords, etc.)
Packit 90a5c9
         */
Packit 90a5c9
        if (((error_notes = apr_table_get(r->notes,
Packit 90a5c9
                                          "error-notes")) != NULL)
Packit 90a5c9
            && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
Packit 90a5c9
            && (strcmp(h1, "*") == 0)) {
Packit 90a5c9
            return(apr_pstrcat(p, error_notes, "

\n", NULL));

Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            return(apr_pstrcat(p,
Packit 90a5c9
                               "

The server encountered an internal "

Packit 90a5c9
                               "error or\n"
Packit 90a5c9
                               "misconfiguration and was unable to complete\n"
Packit 90a5c9
                               "your request.

\n"
Packit 90a5c9
                               "

Please contact the server "

Packit 90a5c9
                               "administrator at \n ",
Packit 90a5c9
                               ap_escape_html(r->pool,
Packit 90a5c9
                                              r->server->server_admin),
Packit 90a5c9
                               " to inform them of the time this "
Packit 90a5c9
                               "error occurred,\n"
Packit 90a5c9
                               " and the actions you performed just before "
Packit 90a5c9
                               "this error.

\n"
Packit 90a5c9
                               "

More information about this error "

Packit 90a5c9
                               "may be available\n"
Packit 90a5c9
                               "in the server error log.

\n",
Packit 90a5c9
                               NULL));
Packit 90a5c9
        }
Packit 90a5c9
        /*
Packit 90a5c9
         * It would be nice to give the user the information they need to
Packit 90a5c9
         * fix the problem directly since many users don't have access to
Packit 90a5c9
         * the error_log (think University sites) even though they can easily
Packit 90a5c9
         * get this error by misconfiguring an htaccess file.  However, the
Packit 90a5c9
         * e error notes tend to include the real file pathname in this case,
Packit 90a5c9
         * which some people consider to be a breach of privacy.  Until we
Packit 90a5c9
         * can figure out a way to remove the pathname, leave this commented.
Packit 90a5c9
         *
Packit 90a5c9
         * if ((error_notes = apr_table_get(r->notes,
Packit 90a5c9
         *                                  "error-notes")) != NULL) {
Packit 90a5c9
         *     return(apr_pstrcat(p, error_notes, "

\n", NULL);

Packit 90a5c9
         * }
Packit 90a5c9
         * else {
Packit 90a5c9
         *     return "";
Packit 90a5c9
         * }
Packit 90a5c9
         */
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* We should have named this send_canned_response, since it is used for any
Packit 90a5c9
 * response that can be generated by the server from the request record.
Packit 90a5c9
 * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
Packit 90a5c9
 * and 5xx (server error) messages that have not been redirected to another
Packit 90a5c9
 * handler via the ErrorDocument feature.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
Packit 90a5c9
{
Packit 90a5c9
    int status = r->status;
Packit 90a5c9
    int idx = ap_index_of_response(status);
Packit 90a5c9
    char *custom_response;
Packit 90a5c9
    const char *location = apr_table_get(r->headers_out, "Location");
Packit 90a5c9
Packit 90a5c9
    /* At this point, we are starting the response over, so we have to reset
Packit 90a5c9
     * this value.
Packit 90a5c9
     */
Packit 90a5c9
    r->eos_sent = 0;
Packit 90a5c9
Packit 90a5c9
    /* and we need to get rid of any RESOURCE filters that might be lurking
Packit 90a5c9
     * around, thinking they are in the middle of the original request
Packit 90a5c9
     */
Packit 90a5c9
Packit 90a5c9
    r->output_filters = r->proto_output_filters;
Packit 90a5c9
Packit 90a5c9
    ap_run_insert_error_filter(r);
Packit 90a5c9
Packit 90a5c9
    /* We need to special-case the handling of 204 and 304 responses,
Packit 90a5c9
     * since they have specific HTTP requirements and do not include a
Packit 90a5c9
     * message body.  Note that being assbackwards here is not an option.
Packit 90a5c9
     */
Packit 90a5c9
    if (AP_STATUS_IS_HEADER_ONLY(status)) {
Packit 90a5c9
        ap_finalize_request_protocol(r);
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * It's possible that the Location field might be in r->err_headers_out
Packit 90a5c9
     * instead of r->headers_out; use the latter if possible, else the
Packit 90a5c9
     * former.
Packit 90a5c9
     */
Packit 90a5c9
    if (location == NULL) {
Packit 90a5c9
        location = apr_table_get(r->err_headers_out, "Location");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!r->assbackwards) {
Packit 90a5c9
        apr_table_t *tmp = r->headers_out;
Packit 90a5c9
Packit 90a5c9
        /* For all HTTP/1.x responses for which we generate the message,
Packit 90a5c9
         * we need to avoid inheriting the "normal status" header fields
Packit 90a5c9
         * that may have been set by the request handler before the
Packit 90a5c9
         * error or redirect, except for Location on external redirects.
Packit 90a5c9
         */
Packit 90a5c9
        r->headers_out = r->err_headers_out;
Packit 90a5c9
        r->err_headers_out = tmp;
Packit 90a5c9
        apr_table_clear(r->err_headers_out);
Packit 90a5c9
Packit 90a5c9
        if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
Packit 90a5c9
            if ((location != NULL) && *location) {
Packit 90a5c9
                apr_table_setn(r->headers_out, "Location", location);
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                location = "";   /* avoids coredump when printing, below */
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        r->content_languages = NULL;
Packit 90a5c9
        r->content_encoding = NULL;
Packit 90a5c9
        r->clength = 0;
Packit 90a5c9
Packit 90a5c9
        if (apr_table_get(r->subprocess_env,
Packit 90a5c9
                          "suppress-error-charset") != NULL) {
Packit 90a5c9
            core_request_config *request_conf =
Packit 90a5c9
                        ap_get_core_module_config(r->request_config);
Packit 90a5c9
            request_conf->suppress_charset = 1; /* avoid adding default
Packit 90a5c9
                                                 * charset later
Packit 90a5c9
                                                 */
Packit 90a5c9
            ap_set_content_type(r, "text/html");
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            ap_set_content_type(r, "text/html; charset=iso-8859-1");
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if ((status == HTTP_METHOD_NOT_ALLOWED)
Packit 90a5c9
            || (status == HTTP_NOT_IMPLEMENTED)) {
Packit 90a5c9
            apr_table_setn(r->headers_out, "Allow", make_allow(r));
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (r->header_only) {
Packit 90a5c9
            ap_finalize_request_protocol(r);
Packit 90a5c9
            return;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if ((custom_response = ap_response_code_string(r, idx))) {
Packit 90a5c9
        /*
Packit 90a5c9
         * We have a custom response output. This should only be
Packit 90a5c9
         * a text-string to write back. But if the ErrorDocument
Packit 90a5c9
         * was a local redirect and the requested resource failed
Packit 90a5c9
         * for any reason, the custom_response will still hold the
Packit 90a5c9
         * redirect URL. We don't really want to output this URL
Packit 90a5c9
         * as a text message, so first check the custom response
Packit 90a5c9
         * string to ensure that it is a text-string (using the
Packit 90a5c9
         * same test used in ap_die(), i.e. does it start with a ").
Packit 90a5c9
         *
Packit 90a5c9
         * If it's not a text string, we've got a recursive error or
Packit 90a5c9
         * an external redirect.  If it's a recursive error, ap_die passes
Packit 90a5c9
         * us the second error code so we can write both, and has already
Packit 90a5c9
         * backed up to the original error.  If it's an external redirect,
Packit 90a5c9
         * it hasn't happened yet; we may never know if it fails.
Packit 90a5c9
         */
Packit 90a5c9
        if (custom_response[0] == '\"') {
Packit 90a5c9
            ap_rputs(custom_response + 1, r);
Packit 90a5c9
            ap_finalize_request_protocol(r);
Packit 90a5c9
            return;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    {
Packit 90a5c9
        const char *title = status_lines[idx];
Packit 90a5c9
        const char *h1;
Packit 90a5c9
Packit 90a5c9
        /* Accept a status_line set by a module, but only if it begins
Packit 90a5c9
         * with the correct 3 digit status code
Packit 90a5c9
         */
Packit 90a5c9
        if (r->status_line) {
Packit 90a5c9
            char *end;
Packit 90a5c9
            int len = strlen(r->status_line);
Packit 90a5c9
            if (len >= 3
Packit 90a5c9
                && apr_strtoi64(r->status_line, &end, 10) == r->status
Packit 90a5c9
                && (end - 3) == r->status_line
Packit 90a5c9
                && (len < 4 || apr_isspace(r->status_line[3]))
Packit 90a5c9
                && (len < 5 || apr_isalnum(r->status_line[4]))) {
Packit 90a5c9
                /* Since we passed the above check, we know that length three
Packit 90a5c9
                 * is equivalent to only a 3 digit numeric http status.
Packit 90a5c9
                 * RFC2616 mandates a trailing space, let's add it.
Packit 90a5c9
                 * If we have an empty reason phrase, we also add "Unknown Reason".
Packit 90a5c9
                 */
Packit 90a5c9
                if (len == 3) {
Packit 90a5c9
                    r->status_line = apr_pstrcat(r->pool, r->status_line, " Unknown Reason", NULL);
Packit 90a5c9
                } else if (len == 4) {
Packit 90a5c9
                    r->status_line = apr_pstrcat(r->pool, r->status_line, "Unknown Reason", NULL);
Packit 90a5c9
                }
Packit 90a5c9
                title = r->status_line;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* folks decided they didn't want the error code in the H1 text */
Packit 90a5c9
        h1 = &title[4];
Packit 90a5c9
Packit 90a5c9
        /* can't count on a charset filter being in place here,
Packit 90a5c9
         * so do ebcdic->ascii translation explicitly (if needed)
Packit 90a5c9
         */
Packit 90a5c9
Packit 90a5c9
        ap_rvputs_proto_in_ascii(r,
Packit 90a5c9
                  DOCTYPE_HTML_2_0
Packit 90a5c9
                  "<html><head>\n<title>", title,
Packit 90a5c9
                  "</title>\n</head><body>\n

", h1, "

\n",
Packit 90a5c9
                  NULL);
Packit 90a5c9
Packit 90a5c9
        ap_rvputs_proto_in_ascii(r,
Packit 90a5c9
                                 get_canned_error_string(status, r, location),
Packit 90a5c9
                                 NULL);
Packit 90a5c9
Packit 90a5c9
        if (recursive_error) {
Packit 90a5c9
            ap_rvputs_proto_in_ascii(r, "

Additionally, a ",

Packit 90a5c9
                      status_lines[ap_index_of_response(recursive_error)],
Packit 90a5c9
                      "\nerror was encountered while trying to use an "
Packit 90a5c9
                      "ErrorDocument to handle the request.

\n", NULL);
Packit 90a5c9
        }
Packit 90a5c9
        ap_rvputs_proto_in_ascii(r, ap_psignature("
\n", r), NULL);
Packit 90a5c9
        ap_rvputs_proto_in_ascii(r, "</body></html>\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    ap_finalize_request_protocol(r);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Create a new method list with the specified number of preallocated
Packit 90a5c9
 * extension slots.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
Packit 90a5c9
{
Packit 90a5c9
    ap_method_list_t *ml;
Packit 90a5c9
Packit 90a5c9
    ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
Packit 90a5c9
    ml->method_mask = 0;
Packit 90a5c9
    ml->method_list = apr_array_make(p, nelts, sizeof(char *));
Packit 90a5c9
    return ml;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Make a copy of a method list (primarily for subrequests that may
Packit 90a5c9
 * subsequently change it; don't want them changing the parent's, too!).
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
Packit 90a5c9
                                     ap_method_list_t *src)
Packit 90a5c9
{
Packit 90a5c9
    int i;
Packit 90a5c9
    char **imethods;
Packit 90a5c9
    char **omethods;
Packit 90a5c9
Packit 90a5c9
    dest->method_mask = src->method_mask;
Packit 90a5c9
    imethods = (char **) src->method_list->elts;
Packit 90a5c9
    for (i = 0; i < src->method_list->nelts; ++i) {
Packit 90a5c9
        omethods = (char **) apr_array_push(dest->method_list);
Packit 90a5c9
        *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Return true if the specified HTTP method is in the provided
Packit 90a5c9
 * method list.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
Packit 90a5c9
{
Packit 90a5c9
    int methnum;
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * If it's one of our known methods, use the shortcut and check the
Packit 90a5c9
     * bitmask.
Packit 90a5c9
     */
Packit 90a5c9
    methnum = ap_method_number_of(method);
Packit 90a5c9
    if (methnum != M_INVALID) {
Packit 90a5c9
        return !!(l->method_mask & (AP_METHOD_BIT << methnum));
Packit 90a5c9
    }
Packit 90a5c9
    /*
Packit 90a5c9
     * Otherwise, see if the method name is in the array of string names.
Packit 90a5c9
     */
Packit 90a5c9
    if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
Packit 90a5c9
        return 0;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return ap_array_str_contains(l->method_list, method);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Add the specified method to a method list (if it isn't already there).
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
Packit 90a5c9
{
Packit 90a5c9
    int methnum;
Packit 90a5c9
    const char **xmethod;
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * If it's one of our known methods, use the shortcut and use the
Packit 90a5c9
     * bitmask.
Packit 90a5c9
     */
Packit 90a5c9
    methnum = ap_method_number_of(method);
Packit 90a5c9
    if (methnum != M_INVALID) {
Packit 90a5c9
        l->method_mask |= (AP_METHOD_BIT << methnum);
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
    /*
Packit 90a5c9
     * Otherwise, see if the method name is in the array of string names.
Packit 90a5c9
     */
Packit 90a5c9
    if (ap_array_str_contains(l->method_list, method)) {
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    xmethod = (const char **) apr_array_push(l->method_list);
Packit 90a5c9
    *xmethod = method;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Remove the specified method from a method list.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
Packit 90a5c9
                                       const char *method)
Packit 90a5c9
{
Packit 90a5c9
    int methnum;
Packit 90a5c9
    char **methods;
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * If it's a known methods, either builtin or registered
Packit 90a5c9
     * by a module, use the bitmask.
Packit 90a5c9
     */
Packit 90a5c9
    methnum = ap_method_number_of(method);
Packit 90a5c9
    if (methnum != M_INVALID) {
Packit 90a5c9
        l->method_mask &= ~(AP_METHOD_BIT << methnum);
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
    /*
Packit 90a5c9
     * Otherwise, see if the method name is in the array of string names.
Packit 90a5c9
     */
Packit 90a5c9
    if (l->method_list->nelts != 0) {
Packit 90a5c9
        int i, j, k;
Packit 90a5c9
        methods = (char **)l->method_list->elts;
Packit 90a5c9
        for (i = 0; i < l->method_list->nelts; ) {
Packit 90a5c9
            if (strcmp(method, methods[i]) == 0) {
Packit 90a5c9
                for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
Packit 90a5c9
                    methods[j] = methods[k];
Packit 90a5c9
                }
Packit 90a5c9
                --l->method_list->nelts;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                ++i;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Reset a method list to be completely empty.
Packit 90a5c9
 */
Packit 90a5c9
AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
Packit 90a5c9
{
Packit 90a5c9
    l->method_mask = 0;
Packit 90a5c9
    l->method_list->nelts = 0;
Packit 90a5c9
}
Packit 90a5c9