]> granicus.if.org Git - apache/blob - modules/http/http_protocol.c
Update copyright year to 2005 and standardize on current copyright owner line.
[apache] / modules / http / http_protocol.c
1 /* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * http_protocol.c --- routines which directly communicate with the client.
19  *
20  * Code originally by Rob McCool; much redone by Robert S. Thau
21  * and the Apache Software Foundation.
22  */
23
24 #include "apr.h"
25 #include "apr_strings.h"
26 #include "apr_buckets.h"
27 #include "apr_lib.h"
28 #include "apr_signal.h"
29
30 #define APR_WANT_STDIO          /* for sscanf */
31 #define APR_WANT_STRFUNC
32 #define APR_WANT_MEMFUNC
33 #include "apr_want.h"
34
35 #define CORE_PRIVATE
36 #include "util_filter.h"
37 #include "ap_config.h"
38 #include "httpd.h"
39 #include "http_config.h"
40 #include "http_core.h"
41 #include "http_protocol.h"
42 #include "http_main.h"
43 #include "http_request.h"
44 #include "http_vhost.h"
45 #include "http_log.h"           /* For errors detected in basic auth common
46                                  * support code... */
47 #include "apr_date.h"           /* For apr_date_parse_http and APR_DATE_BAD */
48 #include "util_charset.h"
49 #include "util_ebcdic.h"
50 #include "util_time.h"
51
52 #include "mod_core.h"
53
54 #if APR_HAVE_STDARG_H
55 #include <stdarg.h>
56 #endif
57 #if APR_HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60
61 /* New Apache routine to map status codes into array indicies
62  *  e.g.  100 -> 0,  101 -> 1,  200 -> 2 ...
63  * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
64  * and must be listed in order.
65  */
66
67 #ifdef UTS21
68 /* The second const triggers an assembler bug on UTS 2.1.
69  * Another workaround is to move some code out of this file into another,
70  *   but this is easier.  Dave Dykstra, 3/31/99
71  */
72 static const char * status_lines[RESPONSE_CODES] =
73 #else
74 static const char * const status_lines[RESPONSE_CODES] =
75 #endif
76 {
77     "100 Continue",
78     "101 Switching Protocols",
79     "102 Processing",
80 #define LEVEL_200  3
81     "200 OK",
82     "201 Created",
83     "202 Accepted",
84     "203 Non-Authoritative Information",
85     "204 No Content",
86     "205 Reset Content",
87     "206 Partial Content",
88     "207 Multi-Status",
89 #define LEVEL_300 11
90     "300 Multiple Choices",
91     "301 Moved Permanently",
92     "302 Found",
93     "303 See Other",
94     "304 Not Modified",
95     "305 Use Proxy",
96     "306 unused",
97     "307 Temporary Redirect",
98 #define LEVEL_400 19
99     "400 Bad Request",
100     "401 Authorization Required",
101     "402 Payment Required",
102     "403 Forbidden",
103     "404 Not Found",
104     "405 Method Not Allowed",
105     "406 Not Acceptable",
106     "407 Proxy Authentication Required",
107     "408 Request Time-out",
108     "409 Conflict",
109     "410 Gone",
110     "411 Length Required",
111     "412 Precondition Failed",
112     "413 Request Entity Too Large",
113     "414 Request-URI Too Large",
114     "415 Unsupported Media Type",
115     "416 Requested Range Not Satisfiable",
116     "417 Expectation Failed",
117     "418 unused",
118     "419 unused",
119     "420 unused",
120     "421 unused",
121     "422 Unprocessable Entity",
122     "423 Locked",
123     "424 Failed Dependency",
124     /* This is a hack, but it is required for ap_index_of_response
125      * to work with 426.
126      */
127     "425 No code",
128     "426 Upgrade Required",
129 #define LEVEL_500 46
130     "500 Internal Server Error",
131     "501 Method Not Implemented",
132     "502 Bad Gateway",
133     "503 Service Temporarily Unavailable",
134     "504 Gateway Time-out",
135     "505 HTTP Version Not Supported",
136     "506 Variant Also Negotiates",
137     "507 Insufficient Storage",
138     "508 unused",
139     "509 unused",
140     "510 Not Extended"
141 };
142
143 APR_HOOK_STRUCT(
144     APR_HOOK_LINK(insert_error_filter)
145 )
146
147 AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
148
149 /* The index of the first bit field that is used to index into a limit
150  * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
151  */
152 #define METHOD_NUMBER_FIRST (M_INVALID + 1)
153
154 /* The max method number. Method numbers are used to shift bitmasks,
155  * so this cannot exceed 63, and all bits high is equal to -1, which is a
156  * special flag, so the last bit used has index 62.
157  */
158 #define METHOD_NUMBER_LAST  62
159
160
161 AP_DECLARE(int) ap_set_keepalive(request_rec *r)
162 {
163     int ka_sent = 0;
164     int wimpy = ap_find_token(r->pool,
165                               apr_table_get(r->headers_out, "Connection"),
166                               "close");
167     const char *conn = apr_table_get(r->headers_in, "Connection");
168
169     /* The following convoluted conditional determines whether or not
170      * the current connection should remain persistent after this response
171      * (a.k.a. HTTP Keep-Alive) and whether or not the output message
172      * body should use the HTTP/1.1 chunked transfer-coding.  In English,
173      *
174      *   IF  we have not marked this connection as errored;
175      *   and the response body has a defined length due to the status code
176      *       being 304 or 204, the request method being HEAD, already
177      *       having defined Content-Length or Transfer-Encoding: chunked, or
178      *       the request version being HTTP/1.1 and thus capable of being set
179      *       as chunked [we know the (r->chunked = 1) side-effect is ugly];
180      *   and the server configuration enables keep-alive;
181      *   and the server configuration has a reasonable inter-request timeout;
182      *   and there is no maximum # requests or the max hasn't been reached;
183      *   and the response status does not require a close;
184      *   and the response generator has not already indicated close;
185      *   and the client did not request non-persistence (Connection: close);
186      *   and    we haven't been configured to ignore the buggy twit
187      *       or they're a buggy twit coming through a HTTP/1.1 proxy
188      *   and    the client is requesting an HTTP/1.0-style keep-alive
189      *       or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
190      *   THEN we can be persistent, which requires more headers be output.
191      *
192      * Note that the condition evaluation order is extremely important.
193      */
194     if ((r->connection->keepalive != AP_CONN_CLOSE)
195         && ((r->status == HTTP_NOT_MODIFIED)
196             || (r->status == HTTP_NO_CONTENT)
197             || r->header_only
198             || apr_table_get(r->headers_out, "Content-Length")
199             || ap_find_last_token(r->pool,
200                                   apr_table_get(r->headers_out,
201                                                 "Transfer-Encoding"),
202                                   "chunked")
203             || ((r->proto_num >= HTTP_VERSION(1,1))
204                 && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
205         && r->server->keep_alive
206         && (r->server->keep_alive_timeout > 0)
207         && ((r->server->keep_alive_max == 0)
208             || (r->server->keep_alive_max > r->connection->keepalives))
209         && !ap_status_drops_connection(r->status)
210         && !wimpy
211         && !ap_find_token(r->pool, conn, "close")
212         && (!apr_table_get(r->subprocess_env, "nokeepalive")
213             || apr_table_get(r->headers_in, "Via"))
214         && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
215             || (r->proto_num >= HTTP_VERSION(1,1)))) {
216         int left = r->server->keep_alive_max - r->connection->keepalives;
217
218         r->connection->keepalive = AP_CONN_KEEPALIVE;
219         r->connection->keepalives++;
220
221         /* If they sent a Keep-Alive token, send one back */
222         if (ka_sent) {
223             if (r->server->keep_alive_max) {
224                 apr_table_setn(r->headers_out, "Keep-Alive",
225                        apr_psprintf(r->pool, "timeout=%d, max=%d",
226                             (int)apr_time_sec(r->server->keep_alive_timeout),
227                             left));
228             }
229             else {
230                 apr_table_setn(r->headers_out, "Keep-Alive",
231                       apr_psprintf(r->pool, "timeout=%d",
232                             (int)apr_time_sec(r->server->keep_alive_timeout)));
233             }
234             apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
235         }
236
237         return 1;
238     }
239
240     /* Otherwise, we need to indicate that we will be closing this
241      * connection immediately after the current response.
242      *
243      * We only really need to send "close" to HTTP/1.1 clients, but we
244      * always send it anyway, because a broken proxy may identify itself
245      * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
246      * to a HTTP/1.1 client. Better safe than sorry.
247      */
248     if (!wimpy) {
249         apr_table_mergen(r->headers_out, "Connection", "close");
250     }
251
252     r->connection->keepalive = AP_CONN_CLOSE;
253
254     return 0;
255 }
256
257 AP_DECLARE(int) ap_meets_conditions(request_rec *r)
258 {
259     const char *etag;
260     const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
261     apr_time_t tmp_time;
262     apr_int64_t mtime;
263
264     /* Check for conditional requests --- note that we only want to do
265      * this if we are successful so far and we are not processing a
266      * subrequest or an ErrorDocument.
267      *
268      * The order of the checks is important, since ETag checks are supposed
269      * to be more accurate than checks relative to the modification time.
270      * However, not all documents are guaranteed to *have* ETags, and some
271      * might have Last-Modified values w/o ETags, so this gets a little
272      * complicated.
273      */
274
275     if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
276         return OK;
277     }
278
279     etag = apr_table_get(r->headers_out, "ETag");
280
281     /* All of our comparisons must be in seconds, because that's the
282      * highest time resolution the HTTP specification allows.
283      */
284     /* XXX: we should define a "time unset" constant */
285     tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
286     mtime =  apr_time_sec(tmp_time);
287
288     /* If an If-Match request-header field was given
289      * AND the field value is not "*" (meaning match anything)
290      * AND if our strong ETag does not match any entity tag in that field,
291      *     respond with a status of 412 (Precondition Failed).
292      */
293     if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
294         if (if_match[0] != '*'
295             && (etag == NULL || etag[0] == 'W'
296                 || !ap_find_list_item(r->pool, if_match, etag))) {
297             return HTTP_PRECONDITION_FAILED;
298         }
299     }
300     else {
301         /* Else if a valid If-Unmodified-Since request-header field was given
302          * AND the requested resource has been modified since the time
303          * specified in this field, then the server MUST
304          *     respond with a status of 412 (Precondition Failed).
305          */
306         if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
307         if (if_unmodified != NULL) {
308             apr_time_t ius = apr_date_parse_http(if_unmodified);
309
310             if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
311                 return HTTP_PRECONDITION_FAILED;
312             }
313         }
314     }
315
316     /* If an If-None-Match request-header field was given
317      * AND the field value is "*" (meaning match anything)
318      *     OR our ETag matches any of the entity tags in that field, fail.
319      *
320      * If the request method was GET or HEAD, failure means the server
321      *    SHOULD respond with a 304 (Not Modified) response.
322      * For all other request methods, failure means the server MUST
323      *    respond with a status of 412 (Precondition Failed).
324      *
325      * GET or HEAD allow weak etag comparison, all other methods require
326      * strong comparison.  We can only use weak if it's not a range request.
327      */
328     if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
329     if (if_nonematch != NULL) {
330         if (r->method_number == M_GET) {
331             if (if_nonematch[0] == '*') {
332                 return HTTP_NOT_MODIFIED;
333             }
334             if (etag != NULL) {
335                 if (apr_table_get(r->headers_in, "Range")) {
336                     if (etag[0] != 'W'
337                         && ap_find_list_item(r->pool, if_nonematch, etag)) {
338                         return HTTP_NOT_MODIFIED;
339                     }
340                 }
341                 else if (ap_strstr_c(if_nonematch, etag)) {
342                     return HTTP_NOT_MODIFIED;
343                 }
344             }
345         }
346         else if (if_nonematch[0] == '*'
347                  || (etag != NULL
348                      && ap_find_list_item(r->pool, if_nonematch, etag))) {
349             return HTTP_PRECONDITION_FAILED;
350         }
351     }
352     /* Else if a valid If-Modified-Since request-header field was given
353      * AND it is a GET or HEAD request
354      * AND the requested resource has not been modified since the time
355      * specified in this field, then the server MUST
356      *    respond with a status of 304 (Not Modified).
357      * A date later than the server's current request time is invalid.
358      */
359     else if ((r->method_number == M_GET)
360              && ((if_modified_since =
361                   apr_table_get(r->headers_in,
362                                 "If-Modified-Since")) != NULL)) {
363         apr_time_t ims_time;
364         apr_int64_t ims, reqtime;
365
366         ims_time = apr_date_parse_http(if_modified_since);
367         ims = apr_time_sec(ims_time);
368         reqtime = apr_time_sec(r->request_time);
369
370         if ((ims >= mtime) && (ims <= reqtime)) {
371             return HTTP_NOT_MODIFIED;
372         }
373     }
374     return OK;
375 }
376
377 /**
378  * Singleton registry of additional methods. This maps new method names
379  * such as "MYGET" to methnums, which are int offsets into bitmasks.
380  *
381  * This follows the same technique as standard M_GET, M_POST, etc. These
382  * are dynamically assigned when modules are loaded and <Limit GET MYGET>
383  * directives are processed.
384  */
385 static apr_hash_t *methods_registry = NULL;
386 static int cur_method_number = METHOD_NUMBER_FIRST;
387
388 /* internal function to register one method/number pair */
389 static void register_one_method(apr_pool_t *p, const char *methname,
390                                 int methnum)
391 {
392     int *pnum = apr_palloc(p, sizeof(*pnum));
393
394     *pnum = methnum;
395     apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
396 }
397
398 /* This internal function is used to clear the method registry
399  * and reset the cur_method_number counter.
400  */
401 static apr_status_t ap_method_registry_destroy(void *notused)
402 {
403     methods_registry = NULL;
404     cur_method_number = METHOD_NUMBER_FIRST;
405     return APR_SUCCESS;
406 }
407
408 AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
409 {
410     methods_registry = apr_hash_make(p);
411     apr_pool_cleanup_register(p, NULL,
412                               ap_method_registry_destroy,
413                               apr_pool_cleanup_null);
414
415     /* put all the standard methods into the registry hash to ease the
416        mapping operations between name and number */
417     register_one_method(p, "GET", M_GET);
418     register_one_method(p, "PUT", M_PUT);
419     register_one_method(p, "POST", M_POST);
420     register_one_method(p, "DELETE", M_DELETE);
421     register_one_method(p, "CONNECT", M_CONNECT);
422     register_one_method(p, "OPTIONS", M_OPTIONS);
423     register_one_method(p, "TRACE", M_TRACE);
424     register_one_method(p, "PATCH", M_PATCH);
425     register_one_method(p, "PROPFIND", M_PROPFIND);
426     register_one_method(p, "PROPPATCH", M_PROPPATCH);
427     register_one_method(p, "MKCOL", M_MKCOL);
428     register_one_method(p, "COPY", M_COPY);
429     register_one_method(p, "MOVE", M_MOVE);
430     register_one_method(p, "LOCK", M_LOCK);
431     register_one_method(p, "UNLOCK", M_UNLOCK);
432     register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
433     register_one_method(p, "CHECKOUT", M_CHECKOUT);
434     register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
435     register_one_method(p, "CHECKIN", M_CHECKIN);
436     register_one_method(p, "UPDATE", M_UPDATE);
437     register_one_method(p, "LABEL", M_LABEL);
438     register_one_method(p, "REPORT", M_REPORT);
439     register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
440     register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
441     register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
442     register_one_method(p, "MERGE", M_MERGE);
443 }
444
445 AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
446 {
447     int *methnum;
448
449     if (methods_registry == NULL) {
450         ap_method_registry_init(p);
451     }
452
453     if (methname == NULL) {
454         return M_INVALID;
455     }
456     
457     /* Check if the method was previously registered.  If it was
458      * return the associated method number.
459      */
460     methnum = (int *)apr_hash_get(methods_registry, methname,
461                                   APR_HASH_KEY_STRING);
462     if (methnum != NULL)
463         return *methnum;
464         
465     if (cur_method_number > METHOD_NUMBER_LAST) {
466         /* The method registry  has run out of dynamically
467          * assignable method numbers. Log this and return M_INVALID.
468          */
469         ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p,
470                       "Maximum new request methods %d reached while "
471                       "registering method %s.",
472                       METHOD_NUMBER_LAST, methname);
473         return M_INVALID;
474     }
475
476     register_one_method(p, methname, cur_method_number);
477     return cur_method_number++;
478 }
479
480 #define UNKNOWN_METHOD (-1)
481
482 static int lookup_builtin_method(const char *method, apr_size_t len)
483 {
484     /* Note: the following code was generated by the "shilka" tool from
485        the "cocom" parsing/compilation toolkit. It is an optimized lookup
486        based on analysis of the input keywords. Postprocessing was done
487        on the shilka output, but the basic structure and analysis is
488        from there. Should new HTTP methods be added, then manual insertion
489        into this code is fine, or simply re-running the shilka tool on
490        the appropriate input. */
491
492     /* Note: it is also quite reasonable to just use our method_registry,
493        but I'm assuming (probably incorrectly) we want more speed here
494        (based on the optimizations the previous code was doing). */
495
496     switch (len)
497     {
498     case 3:
499         switch (method[0])
500         {
501         case 'P':
502             return (method[1] == 'U'
503                     && method[2] == 'T'
504                     ? M_PUT : UNKNOWN_METHOD);
505         case 'G':
506             return (method[1] == 'E'
507                     && method[2] == 'T'
508                     ? M_GET : UNKNOWN_METHOD);
509         default:
510             return UNKNOWN_METHOD;
511         }
512
513     case 4:
514         switch (method[0])
515         {
516         case 'H':
517             return (method[1] == 'E'
518                     && method[2] == 'A'
519                     && method[3] == 'D'
520                     ? M_GET : UNKNOWN_METHOD);
521         case 'P':
522             return (method[1] == 'O'
523                     && method[2] == 'S'
524                     && method[3] == 'T'
525                     ? M_POST : UNKNOWN_METHOD);
526         case 'M':
527             return (method[1] == 'O'
528                     && method[2] == 'V'
529                     && method[3] == 'E'
530                     ? M_MOVE : UNKNOWN_METHOD);
531         case 'L':
532             return (method[1] == 'O'
533                     && method[2] == 'C'
534                     && method[3] == 'K'
535                     ? M_LOCK : UNKNOWN_METHOD);
536         case 'C':
537             return (method[1] == 'O'
538                     && method[2] == 'P'
539                     && method[3] == 'Y'
540                     ? M_COPY : UNKNOWN_METHOD);
541         default:
542             return UNKNOWN_METHOD;
543         }
544
545     case 5:
546         switch (method[2])
547         {
548         case 'T':
549             return (memcmp(method, "PATCH", 5) == 0
550                     ? M_PATCH : UNKNOWN_METHOD);
551         case 'R':
552             return (memcmp(method, "MERGE", 5) == 0
553                     ? M_MERGE : UNKNOWN_METHOD);
554         case 'C':
555             return (memcmp(method, "MKCOL", 5) == 0
556                     ? M_MKCOL : UNKNOWN_METHOD);
557         case 'B':
558             return (memcmp(method, "LABEL", 5) == 0
559                     ? M_LABEL : UNKNOWN_METHOD);
560         case 'A':
561             return (memcmp(method, "TRACE", 5) == 0
562                     ? M_TRACE : UNKNOWN_METHOD);
563         default:
564             return UNKNOWN_METHOD;
565         }
566
567     case 6:
568         switch (method[0])
569         {
570         case 'U':
571             switch (method[5])
572             {
573             case 'K':
574                 return (memcmp(method, "UNLOCK", 6) == 0
575                         ? M_UNLOCK : UNKNOWN_METHOD);
576             case 'E':
577                 return (memcmp(method, "UPDATE", 6) == 0
578                         ? M_UPDATE : UNKNOWN_METHOD);
579             default:
580                 return UNKNOWN_METHOD;
581             }
582         case 'R':
583             return (memcmp(method, "REPORT", 6) == 0
584                     ? M_REPORT : UNKNOWN_METHOD);
585         case 'D':
586             return (memcmp(method, "DELETE", 6) == 0
587                     ? M_DELETE : UNKNOWN_METHOD);
588         default:
589             return UNKNOWN_METHOD;
590         }
591
592     case 7:
593         switch (method[1])
594         {
595         case 'P':
596             return (memcmp(method, "OPTIONS", 7) == 0
597                     ? M_OPTIONS : UNKNOWN_METHOD);
598         case 'O':
599             return (memcmp(method, "CONNECT", 7) == 0
600                     ? M_CONNECT : UNKNOWN_METHOD);
601         case 'H':
602             return (memcmp(method, "CHECKIN", 7) == 0
603                     ? M_CHECKIN : UNKNOWN_METHOD);
604         default:
605             return UNKNOWN_METHOD;
606         }
607
608     case 8:
609         switch (method[0])
610         {
611         case 'P':
612             return (memcmp(method, "PROPFIND", 8) == 0
613                     ? M_PROPFIND : UNKNOWN_METHOD);
614         case 'C':
615             return (memcmp(method, "CHECKOUT", 8) == 0
616                     ? M_CHECKOUT : UNKNOWN_METHOD);
617         default:
618             return UNKNOWN_METHOD;
619         }
620
621     case 9:
622         return (memcmp(method, "PROPPATCH", 9) == 0
623                 ? M_PROPPATCH : UNKNOWN_METHOD);
624
625     case 10:
626         switch (method[0])
627         {
628         case 'U':
629             return (memcmp(method, "UNCHECKOUT", 10) == 0
630                     ? M_UNCHECKOUT : UNKNOWN_METHOD);
631         case 'M':
632             return (memcmp(method, "MKACTIVITY", 10) == 0
633                     ? M_MKACTIVITY : UNKNOWN_METHOD);
634         default:
635             return UNKNOWN_METHOD;
636         }
637
638     case 11:
639         return (memcmp(method, "MKWORKSPACE", 11) == 0
640                 ? M_MKWORKSPACE : UNKNOWN_METHOD);
641
642     case 15:
643         return (memcmp(method, "VERSION-CONTROL", 15) == 0
644                 ? M_VERSION_CONTROL : UNKNOWN_METHOD);
645
646     case 16:
647         return (memcmp(method, "BASELINE-CONTROL", 16) == 0
648                 ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
649
650     default:
651         return UNKNOWN_METHOD;
652     }
653
654     /* NOTREACHED */
655 }
656
657 /* Get the method number associated with the given string, assumed to
658  * contain an HTTP method.  Returns M_INVALID if not recognized.
659  *
660  * This is the first step toward placing method names in a configurable
661  * list.  Hopefully it (and other routines) can eventually be moved to
662  * something like a mod_http_methods.c, complete with config stuff.
663  */
664 AP_DECLARE(int) ap_method_number_of(const char *method)
665 {
666     int len = strlen(method);
667     int which = lookup_builtin_method(method, len);
668
669     if (which != UNKNOWN_METHOD)
670         return which;
671
672     /* check if the method has been dynamically registered */
673     if (methods_registry != NULL) {
674         int *methnum = apr_hash_get(methods_registry, method, len);
675
676         if (methnum != NULL) {
677             return *methnum;
678         }
679     }
680
681     return M_INVALID;
682 }
683
684 /*
685  * Turn a known method number into a name.
686  */
687 AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
688 {
689     apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
690
691     /* scan through the hash table, looking for a value that matches
692        the provided method number. */
693     for (; hi; hi = apr_hash_next(hi)) {
694         const void *key;
695         void *val;
696
697         apr_hash_this(hi, &key, NULL, &val);
698         if (*(int *)val == methnum)
699             return key;
700     }
701
702     /* it wasn't found in the hash */
703     return NULL;
704 }
705
706 /* The index is found by its offset from the x00 code of each level.
707  * Although this is fast, it will need to be replaced if some nutcase
708  * decides to define a high-numbered code before the lower numbers.
709  * If that sad event occurs, replace the code below with a linear search
710  * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
711  */
712 AP_DECLARE(int) ap_index_of_response(int status)
713 {
714     static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
715     LEVEL_500, RESPONSE_CODES};
716     int i, pos;
717
718     if (status < 100) {               /* Below 100 is illegal for HTTP status */
719         return LEVEL_500;
720     }
721
722     for (i = 0; i < 5; i++) {
723         status -= 100;
724         if (status < 100) {
725             pos = (status + shortcut[i]);
726             if (pos < shortcut[i + 1]) {
727                 return pos;
728             }
729             else {
730                 return LEVEL_500;            /* status unknown (falls in gap) */
731             }
732         }
733     }
734     return LEVEL_500;                         /* 600 or above is also illegal */
735 }
736
737 AP_DECLARE(const char *) ap_get_status_line(int status)
738 {
739     return status_lines[ap_index_of_response(status)];
740 }
741
742 /* Build the Allow field-value from the request handler method mask.
743  * Note that we always allow TRACE, since it is handled below.
744  */
745 static char *make_allow(request_rec *r)
746 {
747     char *list;
748     apr_int64_t mask;
749     apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
750     apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
751
752     mask = r->allowed_methods->method_mask;
753
754     for (; hi; hi = apr_hash_next(hi)) {
755         const void *key;
756         void *val;
757
758         apr_hash_this(hi, &key, NULL, &val);
759         if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
760             *(const char **)apr_array_push(allow) = key;
761
762             /* the M_GET method actually refers to two methods */
763             if (*(int *)val == M_GET)
764                 *(const char **)apr_array_push(allow) = "HEAD";
765         }
766     }
767
768     /* TRACE is always allowed */
769     *(const char **)apr_array_push(allow) = "TRACE";
770
771     list = apr_array_pstrcat(r->pool, allow, ',');
772
773     /* ### this is rather annoying. we should enforce registration of
774        ### these methods */
775     if ((mask & (AP_METHOD_BIT << M_INVALID))
776         && (r->allowed_methods->method_list != NULL)
777         && (r->allowed_methods->method_list->nelts != 0)) {
778         int i;
779         char **xmethod = (char **) r->allowed_methods->method_list->elts;
780
781         /*
782          * Append all of the elements of r->allowed_methods->method_list
783          */
784         for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
785             list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL);
786         }
787     }
788
789     return list;
790 }
791
792 AP_DECLARE(int) ap_send_http_options(request_rec *r)
793 {
794     if (r->assbackwards) {
795         return DECLINED;
796     }
797
798     apr_table_setn(r->headers_out, "Allow", make_allow(r));
799
800     /* the request finalization will send an EOS, which will flush all
801      * the headers out (including the Allow header)
802      */
803
804     return OK;
805 }
806
807 AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
808 {
809     if (!ct) {
810         r->content_type = NULL;
811     }
812     else if (!r->content_type || strcmp(r->content_type, ct)) {
813         r->content_type = ct;
814
815         /* Insert filters requested by the AddOutputFiltersByType 
816          * configuration directive. Content-type filters must be 
817          * inserted after the content handlers have run because 
818          * only then, do we reliably know the content-type.
819          */
820         ap_add_output_filters_by_type(r);
821     }
822 }
823
824 static const char *add_optional_notes(request_rec *r,
825                                       const char *prefix,
826                                       const char *key,
827                                       const char *suffix)
828 {
829     const char *notes, *result;
830
831     if ((notes = apr_table_get(r->notes, key)) == NULL) {
832         result = apr_pstrcat(r->pool, prefix, suffix, NULL);
833     }
834     else {
835         result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
836     }
837
838     return result;
839 }
840
841 /* construct and return the default error message for a given
842  * HTTP defined error code
843  */
844 static const char *get_canned_error_string(int status,
845                                            request_rec *r,
846                                            const char *location)
847 {
848     apr_pool_t *p = r->pool;
849     const char *error_notes, *h1, *s1;
850
851     switch (status) {
852     case HTTP_MOVED_PERMANENTLY:
853     case HTTP_MOVED_TEMPORARILY:
854     case HTTP_TEMPORARY_REDIRECT:
855         return(apr_pstrcat(p,
856                            "<p>The document has moved <a href=\"",
857                            ap_escape_html(r->pool, location),
858                            "\">here</a>.</p>\n",
859                            NULL));
860     case HTTP_SEE_OTHER:
861         return(apr_pstrcat(p,
862                            "<p>The answer to your request is located "
863                            "<a href=\"",
864                            ap_escape_html(r->pool, location),
865                            "\">here</a>.</p>\n",
866                            NULL));
867     case HTTP_USE_PROXY:
868         return(apr_pstrcat(p,
869                            "<p>This resource is only accessible "
870                            "through the proxy\n",
871                            ap_escape_html(r->pool, location),
872                            "<br />\nYou will need to configure "
873                            "your client to use that proxy.</p>\n",
874                            NULL));
875     case HTTP_PROXY_AUTHENTICATION_REQUIRED:
876     case HTTP_UNAUTHORIZED:
877         return("<p>This server could not verify that you\n"
878                "are authorized to access the document\n"
879                "requested.  Either you supplied the wrong\n"
880                "credentials (e.g., bad password), or your\n"
881                "browser doesn't understand how to supply\n"
882                "the credentials required.</p>\n");
883     case HTTP_BAD_REQUEST:
884         return(add_optional_notes(r,
885                                   "<p>Your browser sent a request that "
886                                   "this server could not understand.<br />\n",
887                                   "error-notes",
888                                   "</p>\n"));
889     case HTTP_FORBIDDEN:
890         return(apr_pstrcat(p,
891                            "<p>You don't have permission to access ",
892                            ap_escape_html(r->pool, r->uri),
893                            "\non this server.</p>\n",
894                            NULL));
895     case HTTP_NOT_FOUND:
896         return(apr_pstrcat(p,
897                            "<p>The requested URL ",
898                            ap_escape_html(r->pool, r->uri),
899                            " was not found on this server.</p>\n",
900                            NULL));
901     case HTTP_METHOD_NOT_ALLOWED:
902         return(apr_pstrcat(p,
903                            "<p>The requested method ", r->method,
904                            " is not allowed for the URL ",
905                            ap_escape_html(r->pool, r->uri),
906                            ".</p>\n",
907                            NULL));
908     case HTTP_NOT_ACCEPTABLE:
909         s1 = apr_pstrcat(p,
910                          "<p>An appropriate representation of the "
911                          "requested resource ",
912                          ap_escape_html(r->pool, r->uri),
913                          " could not be found on this server.</p>\n",
914                          NULL);
915         return(add_optional_notes(r, s1, "variant-list", ""));
916     case HTTP_MULTIPLE_CHOICES:
917         return(add_optional_notes(r, "", "variant-list", ""));
918     case HTTP_LENGTH_REQUIRED:
919         s1 = apr_pstrcat(p,
920                          "<p>A request of the requested method ",
921                          r->method,
922                          " requires a valid Content-length.<br />\n",
923                          NULL);
924         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
925     case HTTP_PRECONDITION_FAILED:
926         return(apr_pstrcat(p,
927                            "<p>The precondition on the request "
928                            "for the URL ",
929                            ap_escape_html(r->pool, r->uri),
930                            " evaluated to false.</p>\n",
931                            NULL));
932     case HTTP_NOT_IMPLEMENTED:
933         s1 = apr_pstrcat(p,
934                          "<p>",
935                          ap_escape_html(r->pool, r->method), " to ",
936                          ap_escape_html(r->pool, r->uri),
937                          " not supported.<br />\n",
938                          NULL);
939         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
940     case HTTP_BAD_GATEWAY:
941         s1 = "<p>The proxy server received an invalid" CRLF
942             "response from an upstream server.<br />" CRLF;
943         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
944     case HTTP_VARIANT_ALSO_VARIES:
945         return(apr_pstrcat(p,
946                            "<p>A variant for the requested "
947                            "resource\n<pre>\n",
948                            ap_escape_html(r->pool, r->uri),
949                            "\n</pre>\nis itself a negotiable resource. "
950                            "This indicates a configuration error.</p>\n",
951                            NULL));
952     case HTTP_REQUEST_TIME_OUT:
953         return("<p>Server timeout waiting for the HTTP request from the client.</p>\n");
954     case HTTP_GONE:
955         return(apr_pstrcat(p,
956                            "<p>The requested resource<br />",
957                            ap_escape_html(r->pool, r->uri),
958                            "<br />\nis no longer available on this server "
959                            "and there is no forwarding address.\n"
960                            "Please remove all references to this "
961                            "resource.</p>\n",
962                            NULL));
963     case HTTP_REQUEST_ENTITY_TOO_LARGE:
964         return(apr_pstrcat(p,
965                            "The requested resource<br />",
966                            ap_escape_html(r->pool, r->uri), "<br />\n",
967                            "does not allow request data with ",
968                            r->method,
969                            " requests, or the amount of data provided in\n"
970                            "the request exceeds the capacity limit.\n",
971                            NULL));
972     case HTTP_REQUEST_URI_TOO_LARGE:
973         s1 = "<p>The requested URL's length exceeds the capacity\n"
974              "limit for this server.<br />\n";
975         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
976     case HTTP_UNSUPPORTED_MEDIA_TYPE:
977         return("<p>The supplied request data is not in a format\n"
978                "acceptable for processing by this resource.</p>\n");
979     case HTTP_RANGE_NOT_SATISFIABLE:
980         return("<p>None of the range-specifier values in the Range\n"
981                "request-header field overlap the current extent\n"
982                "of the selected resource.</p>\n");
983     case HTTP_EXPECTATION_FAILED:
984         return(apr_pstrcat(p,
985                            "<p>The expectation given in the Expect "
986                            "request-header"
987                            "\nfield could not be met by this server.</p>\n"
988                            "<p>The client sent<pre>\n    Expect: ",
989                            apr_table_get(r->headers_in, "Expect"),
990                            "\n</pre>\n"
991                            "but we only allow the 100-continue "
992                            "expectation.</p>\n",
993                            NULL));
994     case HTTP_UNPROCESSABLE_ENTITY:
995         return("<p>The server understands the media type of the\n"
996                "request entity, but was unable to process the\n"
997                "contained instructions.</p>\n");
998     case HTTP_LOCKED:
999         return("<p>The requested resource is currently locked.\n"
1000                "The lock must be released or proper identification\n"
1001                "given before the method can be applied.</p>\n");
1002     case HTTP_FAILED_DEPENDENCY:
1003         return("<p>The method could not be performed on the resource\n"
1004                "because the requested action depended on another\n"
1005                "action and that other action failed.</p>\n");
1006     case HTTP_UPGRADE_REQUIRED:
1007         return("<p>The requested resource can only be retrieved\n"
1008                "using SSL.  The server is willing to upgrade the current\n"
1009                "connection to SSL, but your client doesn't support it.\n"
1010                "Either upgrade your client, or try requesting the page\n"
1011                "using https://\n");
1012     case HTTP_INSUFFICIENT_STORAGE:
1013         return("<p>The method could not be performed on the resource\n"
1014                "because the server is unable to store the\n"
1015                "representation needed to successfully complete the\n"
1016                "request.  There is insufficient free space left in\n"
1017                "your storage allocation.</p>\n");
1018     case HTTP_SERVICE_UNAVAILABLE:
1019         return("<p>The server is temporarily unable to service your\n"
1020                "request due to maintenance downtime or capacity\n"
1021                "problems. Please try again later.</p>\n");
1022     case HTTP_GATEWAY_TIME_OUT:
1023         return("<p>The proxy server did not receive a timely response\n"
1024                "from the upstream server.</p>\n");
1025     case HTTP_NOT_EXTENDED:
1026         return("<p>A mandatory extension policy in the request is not\n"
1027                "accepted by the server for this resource.</p>\n");
1028     default:                    /* HTTP_INTERNAL_SERVER_ERROR */
1029         /*
1030          * This comparison to expose error-notes could be modified to
1031          * use a configuration directive and export based on that
1032          * directive.  For now "*" is used to designate an error-notes
1033          * that is totally safe for any user to see (ie lacks paths,
1034          * database passwords, etc.)
1035          */
1036         if (((error_notes = apr_table_get(r->notes,
1037                                           "error-notes")) != NULL)
1038             && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
1039             && (strcmp(h1, "*") == 0)) {
1040             return(apr_pstrcat(p, error_notes, "<p />\n", NULL));
1041         }
1042         else {
1043             return(apr_pstrcat(p,
1044                                "<p>The server encountered an internal "
1045                                "error or\n"
1046                                "misconfiguration and was unable to complete\n"
1047                                "your request.</p>\n"
1048                                "<p>Please contact the server "
1049                                "administrator,\n ",
1050                                ap_escape_html(r->pool,
1051                                               r->server->server_admin),
1052                                " and inform them of the time the "
1053                                "error occurred,\n"
1054                                "and anything you might have done that "
1055                                "may have\n"
1056                                "caused the error.</p>\n"
1057                                "<p>More information about this error "
1058                                "may be available\n"
1059                                "in the server error log.</p>\n",
1060                                NULL));
1061         }
1062         /*
1063          * It would be nice to give the user the information they need to
1064          * fix the problem directly since many users don't have access to
1065          * the error_log (think University sites) even though they can easily
1066          * get this error by misconfiguring an htaccess file.  However, the
1067          * e error notes tend to include the real file pathname in this case,
1068          * which some people consider to be a breach of privacy.  Until we
1069          * can figure out a way to remove the pathname, leave this commented.
1070          *
1071          * if ((error_notes = apr_table_get(r->notes,
1072          *                                  "error-notes")) != NULL) {
1073          *     return(apr_pstrcat(p, error_notes, "<p />\n", NULL);
1074          * }
1075          * else {
1076          *     return "";
1077          * }
1078          */
1079     }
1080 }
1081
1082 /* We should have named this send_canned_response, since it is used for any
1083  * response that can be generated by the server from the request record.
1084  * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
1085  * and 5xx (server error) messages that have not been redirected to another
1086  * handler via the ErrorDocument feature.
1087  */
1088 AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
1089 {
1090     int status = r->status;
1091     int idx = ap_index_of_response(status);
1092     char *custom_response;
1093     const char *location = apr_table_get(r->headers_out, "Location");
1094
1095     /* At this point, we are starting the response over, so we have to reset
1096      * this value.
1097      */
1098     r->eos_sent = 0;
1099
1100     /* and we need to get rid of any RESOURCE filters that might be lurking 
1101      * around, thinking they are in the middle of the original request
1102      */
1103
1104     r->output_filters = r->proto_output_filters;
1105
1106     ap_run_insert_error_filter(r);
1107
1108     /*
1109      * It's possible that the Location field might be in r->err_headers_out
1110      * instead of r->headers_out; use the latter if possible, else the
1111      * former.
1112      */
1113     if (location == NULL) {
1114         location = apr_table_get(r->err_headers_out, "Location");
1115     }
1116     /* We need to special-case the handling of 204 and 304 responses,
1117      * since they have specific HTTP requirements and do not include a
1118      * message body.  Note that being assbackwards here is not an option.
1119      */
1120     if (status == HTTP_NOT_MODIFIED) {
1121         ap_finalize_request_protocol(r);
1122         return;
1123     }
1124
1125     if (status == HTTP_NO_CONTENT) {
1126         ap_finalize_request_protocol(r);
1127         return;
1128     }
1129
1130     if (!r->assbackwards) {
1131         apr_table_t *tmp = r->headers_out;
1132
1133         /* For all HTTP/1.x responses for which we generate the message,
1134          * we need to avoid inheriting the "normal status" header fields
1135          * that may have been set by the request handler before the
1136          * error or redirect, except for Location on external redirects.
1137          */
1138         r->headers_out = r->err_headers_out;
1139         r->err_headers_out = tmp;
1140         apr_table_clear(r->err_headers_out);
1141
1142         if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
1143             if ((location != NULL) && *location) {
1144                 apr_table_setn(r->headers_out, "Location", location);
1145             }
1146             else {
1147                 location = "";   /* avoids coredump when printing, below */
1148             }
1149         }
1150
1151         r->content_languages = NULL;
1152         r->content_encoding = NULL;
1153         r->clength = 0;
1154         ap_set_content_type(r, "text/html; charset=iso-8859-1");
1155
1156         if ((status == HTTP_METHOD_NOT_ALLOWED)
1157             || (status == HTTP_NOT_IMPLEMENTED)) {
1158             apr_table_setn(r->headers_out, "Allow", make_allow(r));
1159         }
1160
1161         if (r->header_only) {
1162             ap_finalize_request_protocol(r);
1163             return;
1164         }
1165     }
1166
1167     if ((custom_response = ap_response_code_string(r, idx))) {
1168         /*
1169          * We have a custom response output. This should only be
1170          * a text-string to write back. But if the ErrorDocument
1171          * was a local redirect and the requested resource failed
1172          * for any reason, the custom_response will still hold the
1173          * redirect URL. We don't really want to output this URL
1174          * as a text message, so first check the custom response
1175          * string to ensure that it is a text-string (using the
1176          * same test used in ap_die(), i.e. does it start with a ").
1177          * 
1178          * If it's not a text string, we've got a recursive error or 
1179          * an external redirect.  If it's a recursive error, ap_die passes
1180          * us the second error code so we can write both, and has already 
1181          * backed up to the original error.  If it's an external redirect, 
1182          * it hasn't happened yet; we may never know if it fails.
1183          */
1184         if (custom_response[0] == '\"') {
1185             ap_rputs(custom_response + 1, r);
1186             ap_finalize_request_protocol(r);
1187             return;
1188         }
1189     }
1190     {
1191         const char *title = status_lines[idx];
1192         const char *h1;
1193
1194         /* Accept a status_line set by a module, but only if it begins
1195          * with the 3 digit status code
1196          */
1197         if (r->status_line != NULL
1198             && strlen(r->status_line) > 4       /* long enough */
1199             && apr_isdigit(r->status_line[0])
1200             && apr_isdigit(r->status_line[1])
1201             && apr_isdigit(r->status_line[2])
1202             && apr_isspace(r->status_line[3])
1203             && apr_isalnum(r->status_line[4])) {
1204             title = r->status_line;
1205         }
1206
1207         /* folks decided they didn't want the error code in the H1 text */
1208         h1 = &title[4];
1209
1210         /* can't count on a charset filter being in place here,
1211          * so do ebcdic->ascii translation explicitly (if needed)
1212          */
1213
1214         ap_rvputs_proto_in_ascii(r,
1215                   DOCTYPE_HTML_2_0
1216                   "<html><head>\n<title>", title,
1217                   "</title>\n</head><body>\n<h1>", h1, "</h1>\n",
1218                   NULL);
1219
1220         ap_rvputs_proto_in_ascii(r,
1221                                  get_canned_error_string(status, r, location),
1222                                  NULL);
1223
1224         if (recursive_error) {
1225             ap_rvputs_proto_in_ascii(r, "<p>Additionally, a ",
1226                       status_lines[ap_index_of_response(recursive_error)],
1227                       "\nerror was encountered while trying to use an "
1228                       "ErrorDocument to handle the request.</p>\n", NULL);
1229         }
1230         ap_rvputs_proto_in_ascii(r, ap_psignature("<hr>\n", r), NULL);
1231         ap_rvputs_proto_in_ascii(r, "</body></html>\n", NULL);
1232     }
1233     ap_finalize_request_protocol(r);
1234 }
1235
1236 /*
1237  * Create a new method list with the specified number of preallocated
1238  * extension slots.
1239  */
1240 AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
1241 {
1242     ap_method_list_t *ml;
1243
1244     ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
1245     ml->method_mask = 0;
1246     ml->method_list = apr_array_make(p, nelts, sizeof(char *));
1247     return ml;
1248 }
1249
1250 /*
1251  * Make a copy of a method list (primarily for subrequests that may
1252  * subsequently change it; don't want them changing the parent's, too!).
1253  */
1254 AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
1255                                      ap_method_list_t *src)
1256 {
1257     int i;
1258     char **imethods;
1259     char **omethods;
1260
1261     dest->method_mask = src->method_mask;
1262     imethods = (char **) src->method_list->elts;
1263     for (i = 0; i < src->method_list->nelts; ++i) {
1264         omethods = (char **) apr_array_push(dest->method_list);
1265         *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
1266     }
1267 }
1268
1269 /*
1270  * Invoke a callback routine for each method in the specified list.
1271  */
1272 AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec,
1273                                                        const char *mname,
1274                                                        int mnum),
1275                                           void *rec,
1276                                           const ap_method_list_t *ml, ...)
1277 {
1278     va_list vp;
1279     va_start(vp, ml);
1280     ap_method_list_vdo(comp, rec, ml, vp);
1281     va_end(vp);
1282 }
1283
1284 AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
1285                                                  const char *mname,
1286                                                  int mnum),
1287                                     void *rec, const ap_method_list_t *ml,
1288                                     va_list vp)
1289 {
1290
1291 }
1292
1293 /*
1294  * Return true if the specified HTTP method is in the provided
1295  * method list.
1296  */
1297 AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
1298 {
1299     int methnum;
1300     int i;
1301     char **methods;
1302
1303     /*
1304      * If it's one of our known methods, use the shortcut and check the
1305      * bitmask.
1306      */
1307     methnum = ap_method_number_of(method);
1308     if (methnum != M_INVALID) {
1309         return !!(l->method_mask & (AP_METHOD_BIT << methnum));
1310     }
1311     /*
1312      * Otherwise, see if the method name is in the array or string names
1313      */
1314     if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
1315         return 0;
1316     }
1317     methods = (char **)l->method_list->elts;
1318     for (i = 0; i < l->method_list->nelts; ++i) {
1319         if (strcmp(method, methods[i]) == 0) {
1320             return 1;
1321         }
1322     }
1323     return 0;
1324 }
1325
1326 /*
1327  * Add the specified method to a method list (if it isn't already there).
1328  */
1329 AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
1330 {
1331     int methnum;
1332     int i;
1333     const char **xmethod;
1334     char **methods;
1335
1336     /*
1337      * If it's one of our known methods, use the shortcut and use the
1338      * bitmask.
1339      */
1340     methnum = ap_method_number_of(method);
1341     l->method_mask |= (AP_METHOD_BIT << methnum);
1342     if (methnum != M_INVALID) {
1343         return;
1344     }
1345     /*
1346      * Otherwise, see if the method name is in the array of string names.
1347      */
1348     if (l->method_list->nelts != 0) {
1349         methods = (char **)l->method_list->elts;
1350         for (i = 0; i < l->method_list->nelts; ++i) {
1351             if (strcmp(method, methods[i]) == 0) {
1352                 return;
1353             }
1354         }
1355     }
1356     xmethod = (const char **) apr_array_push(l->method_list);
1357     *xmethod = method;
1358 }
1359
1360 /*
1361  * Remove the specified method from a method list.
1362  */
1363 AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
1364                                        const char *method)
1365 {
1366     int methnum;
1367     char **methods;
1368
1369     /*
1370      * If it's a known methods, either builtin or registered
1371      * by a module, use the bitmask.
1372      */
1373     methnum = ap_method_number_of(method);
1374     l->method_mask |= ~(AP_METHOD_BIT << methnum);
1375     if (methnum != M_INVALID) {
1376         return;
1377     }
1378     /*
1379      * Otherwise, see if the method name is in the array of string names.
1380      */
1381     if (l->method_list->nelts != 0) {
1382         register int i, j, k;
1383         methods = (char **)l->method_list->elts;
1384         for (i = 0; i < l->method_list->nelts; ) {
1385             if (strcmp(method, methods[i]) == 0) {
1386                 for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
1387                     methods[j] = methods[k];
1388                 }
1389                 --l->method_list->nelts;
1390             }
1391             else {
1392                 ++i;
1393             }
1394         }
1395     }
1396 }
1397
1398 /*
1399  * Reset a method list to be completely empty.
1400  */
1401 AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
1402 {
1403     l->method_mask = 0;
1404     l->method_list->nelts = 0;
1405 }
1406