]> granicus.if.org Git - apache/blob - modules/mappers/mod_negotiation.c
adjust remaining modules to use the new handler hook method (Alan Edwards)
[apache] / modules / mappers / mod_negotiation.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * mod_negotiation.c: keeps track of MIME types the client is willing to
61  * accept, and contains code to handle type arbitration.
62  *
63  * rst
64  */
65
66 #include "apr.h"
67 #include "apr_strings.h"
68 #include "apr_file_io.h"
69
70 #if APR_HAVE_STDIO_H
71 #include <stdio.h>              /* for EOF */
72 #endif
73
74 #include "ap_config.h"
75 #include "httpd.h"
76 #include "http_config.h"
77 #include "http_request.h"
78 #include "http_protocol.h"
79 #include "http_core.h"
80 #include "http_log.h"
81 #include "util_script.h"
82 #ifdef HAVE_STRING_H
83 #include <string.h>
84 #endif
85 #ifdef HAVE_STRINGS_H
86 #include <strings.h>
87 #endif
88
89 #define MAP_FILE_MAGIC_TYPE "application/x-type-map"
90
91 /* Commands --- configuring document caching on a per (virtual?)
92  * server basis... 
93  */
94
95 typedef struct {
96     apr_array_header_t *language_priority;
97 } neg_dir_config;
98
99 module AP_MODULE_DECLARE_DATA negotiation_module;
100
101 static void *create_neg_dir_config(apr_pool_t *p, char *dummy)
102 {
103     neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
104
105     new->language_priority = apr_make_array(p, 4, sizeof(char *));
106     return new;
107 }
108
109 static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
110 {
111     neg_dir_config *base = (neg_dir_config *) basev;
112     neg_dir_config *add = (neg_dir_config *) addv;
113     neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
114
115     /* give priority to the config in the subdirectory */
116     new->language_priority = apr_append_arrays(p, add->language_priority,
117                                            base->language_priority);
118     return new;
119 }
120
121 static const char *set_language_priority(cmd_parms *cmd, void *n,
122                                          const char *lang)
123 {
124     apr_array_header_t *arr = ((neg_dir_config *) n)->language_priority;
125     const char **langp = (const char **) apr_push_array(arr);
126
127     *langp = lang;
128     return NULL;
129 }
130
131 static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
132                                          int arg)
133 {
134     void *server_conf = cmd->server->module_config;
135
136     ap_set_module_config(server_conf, &negotiation_module, 
137         (arg ? "Cache" : NULL));
138     return NULL;
139 }
140
141 static int do_cache_negotiated_docs(server_rec *s)
142 {
143     return (ap_get_module_config(s->module_config, &negotiation_module) != NULL);
144 }
145
146 static const command_rec negotiation_cmds[] =
147 {
148     AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, 
149                  "Either 'on' or 'off' (default)"),
150     AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL, OR_FILEINFO, 
151                     "space-delimited list of MIME language abbreviations"),
152     {NULL}
153 };
154
155 /*
156  * Record of available info on a media type specified by the client
157  * (we also use 'em for encodings and languages)
158  */
159
160 typedef struct accept_rec {
161     char *name;                 /* MUST be lowercase */
162     float quality;
163     float level;
164     char *charset;              /* for content-type only */
165 } accept_rec;
166
167 /*
168  * Record of available info on a particular variant
169  *
170  * Note that a few of these fields are updated by the actual negotiation
171  * code.  These are:
172  *
173  * level_matched --- initialized to zero.  Set to the value of level
174  *             if the client actually accepts this media type at that
175  *             level (and *not* if it got in on a wildcard).  See level_cmp
176  *             below.
177  * mime_stars -- initialized to zero.  Set to the number of stars
178  *               present in the best matching Accept header element.
179  *               1 for star/star, 2 for type/star and 3 for
180  *               type/subtype.
181  *
182  * definite -- initialized to 1.  Set to 0 if there is a match which
183  *             makes the variant non-definite according to the rules
184  *             in rfc2296.
185  */
186
187 typedef struct var_rec {
188     request_rec *sub_req;       /* May be NULL (is, for map files) */
189     char *mime_type;            /* MUST be lowercase */
190     char *file_name;
191     const char *content_encoding;
192     apr_array_header_t *content_languages;   /* list of languages for this variant */
193     char *content_charset;
194     char *description;
195
196     /* The next five items give the quality values for the dimensions
197      * of negotiation for this variant. They are obtained from the
198      * appropriate header lines, except for source_quality, which
199      * is obtained from the variant itself (the 'qs' parameter value
200      * from the variant's mime-type). Apart from source_quality,
201      * these values are set when we find the quality for each variant
202      * (see best_match()). source_quality is set from the 'qs' parameter
203      * of the variant description or mime type: see set_mime_fields().
204      */
205     float lang_quality;         /* quality of this variant's language */
206     float encoding_quality;     /* ditto encoding */
207     float charset_quality;      /* ditto charset */
208     float mime_type_quality;    /* ditto media type */
209     float source_quality;       /* source quality for this variant */
210
211     /* Now some special values */
212     float level;                /* Auxiliary to content-type... */
213     float bytes;                /* content length, if known */
214     int lang_index;             /* pre HTTP/1.1 language priority stuff */
215     int is_pseudo_html;         /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
216
217     /* Above are all written-once properties of the variant.  The
218      * three fields below are changed during negotiation:
219      */
220
221     float level_matched;
222     int mime_stars;
223     int definite;
224 } var_rec;
225
226 /* Something to carry around the state of negotiation (and to keep
227  * all of this thread-safe)...
228  */
229
230 typedef struct {
231     apr_pool_t *pool;
232     request_rec *r;
233     char *dir_name;
234     int accept_q;               /* 1 if an Accept item has a q= param */
235     float default_lang_quality; /* fiddle lang q for variants with no lang */
236
237     /* the array pointers below are NULL if the corresponding accept
238      * headers are not present
239      */
240     apr_array_header_t *accepts;            /* accept_recs */
241     apr_array_header_t *accept_encodings;   /* accept_recs */
242     apr_array_header_t *accept_charsets;    /* accept_recs */
243     apr_array_header_t *accept_langs;       /* accept_recs */
244
245     apr_array_header_t *avail_vars;         /* available variants */
246
247     int count_multiviews_variants;    /* number of variants found on disk */
248
249     int is_transparent;       /* 1 if this resource is trans. negotiable */
250
251     int dont_fiddle_headers;  /* 1 if we may not fiddle with accept hdrs */
252     int ua_supports_trans;    /* 1 if ua supports trans negotiation */
253     int send_alternates;      /* 1 if we want to send an Alternates header */
254     int may_choose;           /* 1 if we may choose a variant for the client */
255     int use_rvsa;             /* 1 if we must use RVSA/1.0 negotiation algo */
256 } negotiation_state;
257
258 /* A few functions to manipulate var_recs.
259  * Cleaning out the fields...
260  */
261
262 static void clean_var_rec(var_rec *mime_info)
263 {
264     mime_info->sub_req = NULL;
265     mime_info->mime_type = "";
266     mime_info->file_name = "";
267     mime_info->content_encoding = NULL;
268     mime_info->content_languages = NULL;
269     mime_info->content_charset = "";
270     mime_info->description = "";
271
272     mime_info->is_pseudo_html = 0;
273     mime_info->level = 0.0f;
274     mime_info->level_matched = 0.0f;
275     mime_info->bytes = 0.0f;
276     mime_info->lang_index = -1;
277     mime_info->mime_stars = 0;
278     mime_info->definite = 1;
279
280     mime_info->charset_quality = 1.0f;
281     mime_info->encoding_quality = 1.0f;
282     mime_info->lang_quality = 1.0f;
283     mime_info->mime_type_quality = 1.0f;
284     mime_info->source_quality = 0.0f;
285 }
286
287 /* Initializing the relevant fields of a variant record from the
288  * accept_info read out of its content-type, one way or another.
289  */
290
291 static void set_mime_fields(var_rec *var, accept_rec *mime_info)
292 {
293     var->mime_type = mime_info->name;
294     var->source_quality = mime_info->quality;
295     var->level = mime_info->level;
296     var->content_charset = mime_info->charset;
297
298     var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
299                            || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
300                            || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
301 }
302
303 /* Create a variant list validator in r using info from vlistr. */
304
305 static void set_vlist_validator(request_rec *r, request_rec *vlistr)
306 {
307     /* Calculating the variant list validator is similar to
308      * calculating an etag for the source of the variant list
309      * information, so we use ap_make_etag().  Note that this
310      * validator can be 'weak' in extreme case.
311      */
312     ap_update_mtime(vlistr, vlistr->finfo.mtime);
313     r->vlist_validator = ap_make_etag(vlistr, 0);
314
315     /* ap_set_etag will later take r->vlist_validator into account
316      * when creating the etag header
317      */
318 }
319
320
321 /*****************************************************************
322  *
323  * Parsing (lists of) media types and their parameters, as seen in
324  * HTTPD header lines and elsewhere.
325  */
326
327 /*
328  * Get a single mime type entry --- one media type and parameters;
329  * enter the values we recognize into the argument accept_rec
330  */
331
332 static const char *get_entry(apr_pool_t *p, accept_rec *result,
333                              const char *accept_line)
334 {
335     result->quality = 1.0f;
336     result->level = 0.0f;
337     result->charset = "";
338
339     /*
340      * Note that this handles what I gather is the "old format",
341      *
342      *    Accept: text/html text/plain moo/zot
343      *
344      * without any compatibility kludges --- if the token after the
345      * MIME type begins with a semicolon, we know we're looking at parms,
346      * otherwise, we know we aren't.  (So why all the pissing and moaning
347      * in the CERN server code?  I must be missing something).
348      */
349
350     result->name = ap_get_token(p, &accept_line, 0);
351     ap_str_tolower(result->name);     /* You want case insensitive,
352                                        * you'll *get* case insensitive.
353                                        */
354
355     /* KLUDGE!!! Default HTML to level 2.0 unless the browser
356      * *explicitly* says something else.
357      */
358
359     if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
360         result->level = 2.0f;
361     }
362     else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
363         result->level = 2.0f;
364     }
365     else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
366         result->level = 3.0f;
367     }
368
369     while (*accept_line == ';') {
370         /* Parameters ... */
371
372         char *parm;
373         char *cp;
374         char *end;
375
376         ++accept_line;
377         parm = ap_get_token(p, &accept_line, 1);
378
379         /* Look for 'var = value' --- and make sure the var is in lcase. */
380
381         for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
382             *cp = apr_tolower(*cp);
383         }
384
385         if (!*cp) {
386             continue;           /* No '='; just ignore it. */
387         }
388
389         *cp++ = '\0';           /* Delimit var */
390         while (*cp && (apr_isspace(*cp) || *cp == '=')) {
391             ++cp;
392         }
393
394         if (*cp == '"') {
395             ++cp;
396             for (end = cp;
397                  (*end && *end != '\n' && *end != '\r' && *end != '\"');
398                  end++);
399         }
400         else {
401             for (end = cp; (*end && !apr_isspace(*end)); end++);
402         }
403         if (*end) {
404             *end = '\0';        /* strip ending quote or return */
405         }
406         ap_str_tolower(cp);
407
408         if (parm[0] == 'q'
409             && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
410             result->quality = atof(cp);
411         }
412         else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
413             result->level = atof(cp);
414         }
415         else if (!strcmp(parm, "charset")) {
416             result->charset = cp;
417         }
418     }
419
420     if (*accept_line == ',') {
421         ++accept_line;
422     }
423
424     return accept_line;
425 }
426
427 /*****************************************************************
428  *
429  * Dealing with header lines ...
430  *
431  * Accept, Accept-Charset, Accept-Language and Accept-Encoding
432  * are handled by do_header_line() - they all have the same
433  * basic structure of a list of items of the format
434  *    name; q=N; charset=TEXT
435  *
436  * where charset is only valid in Accept.
437  */
438
439 static apr_array_header_t *do_header_line(apr_pool_t *p, const char *accept_line)
440 {
441     apr_array_header_t *accept_recs;
442
443     if (!accept_line) {
444         return NULL;
445     }
446
447     accept_recs = apr_make_array(p, 40, sizeof(accept_rec));
448
449     while (*accept_line) {
450         accept_rec *new = (accept_rec *) apr_push_array(accept_recs);
451         accept_line = get_entry(p, new, accept_line);
452     }
453
454     return accept_recs;
455 }
456
457 /* Given the text of the Content-Languages: line from the var map file,
458  * return an array containing the languages of this variant
459  */
460
461 static apr_array_header_t *do_languages_line(apr_pool_t *p, const char **lang_line)
462 {
463     apr_array_header_t *lang_recs = apr_make_array(p, 2, sizeof(char *));
464
465     if (!lang_line) {
466         return lang_recs;
467     }
468
469     while (**lang_line) {
470         char **new = (char **) apr_push_array(lang_recs);
471         *new = ap_get_token(p, lang_line, 0);
472         ap_str_tolower(*new);
473         if (**lang_line == ',' || **lang_line == ';') {
474             ++(*lang_line);
475         }
476     }
477
478     return lang_recs;
479 }
480
481 /*****************************************************************
482  *
483  * Handling header lines from clients...
484  */
485
486 static negotiation_state *parse_accept_headers(request_rec *r)
487 {
488     negotiation_state *new =
489         (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
490     accept_rec *elts;
491     apr_table_t *hdrs = r->headers_in;
492     int i;
493
494     new->pool = r->pool;
495     new->r = r;
496     new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
497
498     new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
499
500     /* calculate new->accept_q value */
501     if (new->accepts) {
502         elts = (accept_rec *) new->accepts->elts;
503
504         for (i = 0; i < new->accepts->nelts; ++i) {
505             if (elts[i].quality < 1.0) {
506                 new->accept_q = 1;
507             }
508         }
509     }
510
511     new->accept_encodings =
512         do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
513     new->accept_langs =
514         do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
515     new->accept_charsets =
516         do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
517
518     new->avail_vars = apr_make_array(r->pool, 40, sizeof(var_rec));
519
520     return new;
521 }
522
523
524 static void parse_negotiate_header(request_rec *r, negotiation_state *neg)
525 {
526     const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
527     char *tok;
528     
529     /* First, default to no TCN, no Alternates, and the original Apache
530      * negotiation algorithm with fiddles for broken browser configs.
531      *
532      * To save network bandwidth, we do not configure to send an
533      * Alternates header to the user agent by default.  User
534      * agents that want an Alternates header for agent-driven
535      * negotiation will have to request it by sending an
536      * appropriate Negotiate header.
537      */
538     neg->ua_supports_trans   = 0;
539     neg->send_alternates     = 0;
540     neg->may_choose          = 1;
541     neg->use_rvsa            = 0;
542     neg->dont_fiddle_headers = 0;
543
544     if (!negotiate)
545         return;
546
547     if (strcmp(negotiate, "trans") == 0) {
548         /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
549          * do not support transparent content negotiation, so for Lynx we
550          * ignore the negotiate header when its contents are exactly "trans".
551          * If future versions of Lynx ever need to say 'negotiate: trans',
552          * they can send the equivalent 'negotiate: trans, trans' instead
553          * to avoid triggering the workaround below. 
554          */
555         const char *ua = apr_table_get(r->headers_in, "User-Agent");
556
557         if (ua && (strncmp(ua, "Lynx", 4) == 0))
558             return;
559     }
560
561     neg->may_choose = 0;  /* An empty Negotiate would require 300 response */
562
563     while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
564
565         if (strcmp(tok, "trans") == 0 ||
566             strcmp(tok, "vlist") == 0 ||
567             strcmp(tok, "guess-small") == 0 ||
568             apr_isdigit(tok[0]) ||
569             strcmp(tok, "*") == 0) {
570
571             /* The user agent supports transparent negotiation */
572             neg->ua_supports_trans = 1;
573
574             /* Send-alternates could be configurable, but note
575              * that it must be 1 if we have 'vlist' in the
576              * negotiate header.
577              */
578             neg->send_alternates = 1;
579
580             if (strcmp(tok, "1.0") == 0) {
581                 /* we may use the RVSA/1.0 algorithm, configure for it */
582                 neg->may_choose = 1;
583                 neg->use_rvsa = 1;
584                 neg->dont_fiddle_headers = 1;
585             }
586             else if (tok[0] == '*') {
587                 /* we may use any variant selection algorithm, configure
588                  * to use the Apache algorithm
589                  */
590                 neg->may_choose = 1;
591                 
592                 /* We disable header fiddles on the assumption that a
593                  * client sending Negotiate knows how to send correct
594                  * headers which don't need fiddling.
595                  */
596                 neg->dont_fiddle_headers = 1; 
597             }
598         }
599     }
600
601 #ifdef NEG_DEBUG
602     ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
603             "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
604             "send_alternates=%d, may_choose=%d",
605             neg->dont_fiddle_headers, neg->use_rvsa,  
606             neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
607 #endif
608
609 }
610
611 /* Sometimes clients will give us no Accept info at all; this routine sets
612  * up the standard default for that case, and also arranges for us to be
613  * willing to run a CGI script if we find one.  (In fact, we set up to
614  * dramatically prefer CGI scripts in cases where that's appropriate,
615  * e.g., POST or when URI includes query args or extra path info).
616  */
617 static void maybe_add_default_accepts(negotiation_state *neg, 
618                                       int prefer_scripts)
619 {
620     accept_rec *new_accept;
621
622     if (!neg->accepts) {
623         neg->accepts = apr_make_array(neg->pool, 4, sizeof(accept_rec));
624
625         new_accept = (accept_rec *) apr_push_array(neg->accepts);
626         
627         new_accept->name = "*/*";
628         new_accept->quality = 1.0f;
629         new_accept->level = 0.0f;
630     }    
631
632     new_accept = (accept_rec *) apr_push_array(neg->accepts);
633
634     new_accept->name = CGI_MAGIC_TYPE;
635     if (neg->use_rvsa) {
636         new_accept->quality = 0;
637     }
638     else {
639         new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
640     }
641     new_accept->level = 0.0f;
642 }
643
644 /*****************************************************************
645  *
646  * Parsing type-map files, in Roy's meta/http format augmented with
647  * #-comments.
648  */
649
650 /* Reading RFC822-style header lines, ignoring #-comments and
651  * handling continuations.
652  */
653
654 enum header_state {
655     header_eof, header_seen, header_sep
656 };
657
658 static enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
659 {
660     char *buf_end = buffer + len;
661     char *cp;
662     char c;
663
664     /* Get a noncommented line */
665
666     do {
667         if (apr_fgets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
668             return header_eof;
669         }
670     } while (buffer[0] == '#');
671
672     /* If blank, just return it --- this ends information on this variant */
673
674     for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
675         continue;
676     }
677
678     if (*cp == '\0') {
679         return header_sep;
680     }
681
682     /* If non-blank, go looking for header lines, but note that we still
683      * have to treat comments specially...
684      */
685
686     cp += strlen(cp);
687
688     while (apr_getc(&c, map) != APR_EOF) {
689         if (c == '#') {
690             /* Comment line */
691             while (apr_getc(&c, map) != EOF && c != '\n') {
692                 continue;
693             }
694         }
695         else if (apr_isspace(c)) {
696             /* Leading whitespace.  POSSIBLE continuation line
697              * Also, possibly blank --- if so, we ungetc() the final newline
698              * so that we will pick up the blank line the next time 'round.
699              */
700
701             while (c != '\n' && apr_isspace(c)) {
702                 if(apr_getc(&c, map) != APR_SUCCESS)
703                     break;
704             }
705
706             apr_ungetc(c, map);
707
708             if (c == '\n') {
709                 return header_seen;     /* Blank line */
710             }
711
712             /* Continuation */
713
714             while (cp < buf_end - 2 && (apr_getc(&c, map)) != EOF && c != '\n') {
715                 *cp++ = c;
716             }
717
718             *cp++ = '\n';
719             *cp = '\0';
720         }
721         else {
722
723             /* Line beginning with something other than whitespace */
724
725             apr_ungetc(c, map);
726             return header_seen;
727         }
728     }
729
730     return header_seen;
731 }
732
733 /* Stripping out RFC822 comments */
734
735 static void strip_paren_comments(char *hdr)
736 {
737     /* Hmmm... is this correct?  In Roy's latest draft, (comments) can nest! */
738     /* Nope, it isn't correct.  Fails to handle backslash escape as well.    */
739
740     while (*hdr) {
741         if (*hdr == '"') {
742             hdr = strchr(hdr, '"');
743             if (hdr == NULL) {
744                 return;
745             }
746             ++hdr;
747         }
748         else if (*hdr == '(') {
749             while (*hdr && *hdr != ')') {
750                 *hdr++ = ' ';
751             }
752
753             if (*hdr) {
754                 *hdr++ = ' ';
755             }
756         }
757         else {
758             ++hdr;
759         }
760     }
761 }
762
763 /* Getting to a header body from the header */
764
765 static char *lcase_header_name_return_body(char *header, request_rec *r)
766 {
767     char *cp = header;
768
769     for ( ; *cp && *cp != ':' ; ++cp) {
770         *cp = apr_tolower(*cp);
771     }
772
773     if (!*cp) {
774         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
775                       "Syntax error in type map --- no ':': %s", r->filename);
776         return NULL;
777     }
778
779     do {
780         ++cp;
781     } while (*cp && apr_isspace(*cp));
782
783     if (!*cp) {
784         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
785                       "Syntax error in type map --- no header body: %s",
786                       r->filename);
787         return NULL;
788     }
789
790     return cp;
791 }
792
793 static int read_type_map(negotiation_state *neg, request_rec *rr)
794 {
795     request_rec *r = neg->r;
796     apr_file_t *map = NULL;
797     apr_status_t status;
798     char buffer[MAX_STRING_LEN];
799     enum header_state hstate;
800     struct var_rec mime_info;
801     int has_content;
802
803     /* We are not using multiviews */
804     neg->count_multiviews_variants = 0;
805
806     if ((status = apr_open(&map, rr->filename, APR_READ,
807                 APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
808         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
809                       "cannot access type map file: %s", rr->filename);
810         return HTTP_FORBIDDEN;
811     }
812
813     clean_var_rec(&mime_info);
814     has_content = 0;
815
816     do {
817         hstate = get_header_line(buffer, MAX_STRING_LEN, map);
818
819         if (hstate == header_seen) {
820             char *body1 = lcase_header_name_return_body(buffer, neg->r);
821             const char *body;
822
823             if (body1 == NULL) {
824                 return HTTP_INTERNAL_SERVER_ERROR;
825             }
826
827             strip_paren_comments(body1);
828             body = body1;
829
830             if (!strncmp(buffer, "uri:", 4)) {
831                 mime_info.file_name = ap_get_token(neg->pool, &body, 0);
832             }
833             else if (!strncmp(buffer, "content-type:", 13)) {
834                 struct accept_rec accept_info;
835
836                 get_entry(neg->pool, &accept_info, body);
837                 set_mime_fields(&mime_info, &accept_info);
838                 has_content = 1;
839             }
840             else if (!strncmp(buffer, "content-length:", 15)) {
841                 mime_info.bytes = atof(body);
842                 has_content = 1;
843             }
844             else if (!strncmp(buffer, "content-language:", 17)) {
845                 mime_info.content_languages = do_languages_line(neg->pool,
846                                                                 &body);
847                 has_content = 1;
848             }
849             else if (!strncmp(buffer, "content-encoding:", 17)) {
850                 mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
851                 has_content = 1;
852             }
853             else if (!strncmp(buffer, "description:", 12)) {
854                 char *desc = apr_pstrdup(neg->pool, body);
855                 char *cp;
856
857                 for (cp = desc; *cp; ++cp) {
858                     if (*cp=='\n') *cp=' ';
859                 }
860                 if (cp>desc) *(cp-1)=0;
861                 mime_info.description = desc;
862             }
863         }
864         else {
865             if (*mime_info.file_name && has_content) {
866                 void *new_var = apr_push_array(neg->avail_vars);
867
868                 memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
869             }
870
871             clean_var_rec(&mime_info);
872             has_content = 0;
873         }
874     } while (hstate != header_eof);
875
876     apr_close(map);
877
878     set_vlist_validator(r, rr);
879
880     return OK;
881 }
882
883
884 /* Sort function used by read_types_multi. */
885 static int variantsortf(var_rec *a, var_rec *b) {
886
887     /* First key is the source quality, sort in descending order. */
888
889     /* XXX: note that we currently implement no method of setting the
890      * source quality for multiviews variants, so we are always comparing
891      * 1.0 to 1.0 for now
892      */
893     if (a->source_quality < b->source_quality)
894         return 1;
895     if (a->source_quality > b->source_quality)
896         return -1;
897
898     /* Second key is the variant name */
899     return strcmp(a->file_name, b->file_name);
900 }
901
902 /*****************************************************************
903  *
904  * Same as read_type_map, except we use a filtered directory listing
905  * as the map...  
906  */
907
908 static int read_types_multi(negotiation_state *neg)
909 {
910     request_rec *r = neg->r;
911
912     char *filp;
913     int prefix_len;
914     apr_dir_t *dirp;
915     apr_status_t status;
916     struct var_rec mime_info;
917     struct accept_rec accept_info;
918     void *new_var;
919
920     clean_var_rec(&mime_info);
921
922     if (!(filp = strrchr(r->filename, '/'))) {
923         return DECLINED;        /* Weird... */
924     }
925
926     if (strncmp(r->filename, "proxy:", 6) == 0) {
927         return DECLINED;
928     }
929
930     ++filp;
931     prefix_len = strlen(filp);
932
933     if ((status = apr_dir_open(&dirp, neg->dir_name, neg->pool)) != APR_SUCCESS) {
934         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
935                     "cannot read directory for multi: %s", neg->dir_name);
936         return HTTP_FORBIDDEN;
937     }
938
939     while (apr_readdir(dirp) == APR_SUCCESS) {
940         request_rec *sub_req;
941         const char *d_name;
942
943         apr_get_dir_filename(&d_name, dirp);
944         /* Do we have a match? */
945
946         if (strncmp(d_name, filp, prefix_len)) {
947             continue;
948         }
949         if (d_name[prefix_len] != '.') {
950             continue;
951         }
952
953         /* Yep.  See if it's something which we have access to, and 
954          * which has a known type and encoding (as opposed to something
955          * which we'll be slapping default_type on later).
956          */
957
958         sub_req = ap_sub_req_lookup_file(d_name, r, NULL);
959
960         /* If it has a handler, we'll pretend it's a CGI script,
961          * since that's a good indication of the sort of thing it
962          * might be doing.
963          */
964         if (sub_req->handler && !sub_req->content_type) {
965             sub_req->content_type = CGI_MAGIC_TYPE;
966         }
967
968         if (sub_req->status != HTTP_OK || !sub_req->content_type) {
969             ap_destroy_sub_req(sub_req);
970             continue;
971         }
972
973         /* If it's a map file, we use that instead of the map
974          * we're building...
975          */
976
977         if (((sub_req->content_type) &&
978              !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
979             ((sub_req->handler) &&
980              !strcmp(sub_req->handler, "type-map"))) {
981
982             apr_closedir(dirp);
983             neg->avail_vars->nelts = 0;
984             if (sub_req->status != HTTP_OK) {
985                 return sub_req->status;
986             }
987             return read_type_map(neg, sub_req);
988         }
989
990         /* Have reasonable variant --- gather notes. */
991
992         mime_info.sub_req = sub_req;
993         mime_info.file_name = apr_pstrdup(neg->pool, d_name);
994         if (sub_req->content_encoding) {
995             mime_info.content_encoding = sub_req->content_encoding;
996         }
997         if (sub_req->content_languages) {
998             mime_info.content_languages = sub_req->content_languages;
999         }
1000
1001         get_entry(neg->pool, &accept_info, sub_req->content_type);
1002         set_mime_fields(&mime_info, &accept_info);
1003
1004         new_var = apr_push_array(neg->avail_vars);
1005         memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1006
1007         neg->count_multiviews_variants++;
1008
1009         clean_var_rec(&mime_info);
1010     }
1011
1012     apr_closedir(dirp);
1013
1014     set_vlist_validator(r, r);
1015
1016     /* Sort the variants into a canonical order.  The negotiation
1017      * result sometimes depends on the order of the variants.  By
1018      * sorting the variants into a canonical order, rather than using
1019      * the order in which readdir() happens to return them, we ensure
1020      * that the negotiation result will be consistent over filesystem
1021      * backup/restores and over all mirror sites.
1022      */
1023        
1024     qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
1025           sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
1026
1027     return OK;
1028 }
1029
1030
1031 /*****************************************************************
1032  * And now for the code you've been waiting for... actually
1033  * finding a match to the client's requirements.  
1034  */
1035
1036 /* Matching MIME types ... the star/star and foo/star commenting conventions
1037  * are implemented here.  (You know what I mean by star/star, but just
1038  * try mentioning those three characters in a C comment).  Using strcmp()
1039  * is legit, because everything has already been smashed to lowercase.
1040  *
1041  * Note also that if we get an exact match on the media type, we update
1042  * level_matched for use in level_cmp below...
1043  * 
1044  * We also give a value for mime_stars, which is used later. It should
1045  * be 1 for star/star, 2 for type/star and 3 for type/subtype.
1046  */
1047
1048 static int mime_match(accept_rec *accept_r, var_rec *avail)
1049 {
1050     char *accept_type = accept_r->name;
1051     char *avail_type = avail->mime_type;
1052     int len = strlen(accept_type);
1053
1054     if (accept_type[0] == '*') {        /* Anything matches star/star */
1055         if (avail->mime_stars < 1) {
1056             avail->mime_stars = 1;
1057         }
1058         return 1;
1059     }
1060     else if ((accept_type[len - 1] == '*') &&
1061              !strncmp(accept_type, avail_type, len - 2)) {
1062         if (avail->mime_stars < 2) {
1063             avail->mime_stars = 2;
1064         }
1065         return 1;
1066     }
1067     else if (!strcmp(accept_type, avail_type)
1068              || (!strcmp(accept_type, "text/html")
1069                  && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
1070                      || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
1071         if (accept_r->level >= avail->level) {
1072             avail->level_matched = avail->level;
1073             avail->mime_stars = 3;
1074             return 1;
1075         }
1076     }
1077
1078     return OK;
1079 }
1080
1081 /* This code implements a piece of the tie-breaking algorithm between
1082  * variants of equal quality.  This piece is the treatment of variants
1083  * of the same base media type, but different levels.  What we want to
1084  * return is the variant at the highest level that the client explicitly
1085  * claimed to accept.
1086  *
1087  * If all the variants available are at a higher level than that, or if
1088  * the client didn't say anything specific about this media type at all
1089  * and these variants just got in on a wildcard, we prefer the lowest
1090  * level, on grounds that that's the one that the client is least likely
1091  * to choke on.
1092  *
1093  * (This is all motivated by treatment of levels in HTML --- we only
1094  * want to give level 3 to browsers that explicitly ask for it; browsers
1095  * that don't, including HTTP/0.9 browsers that only get the implicit
1096  * "Accept: * / *" [space added to avoid confusing cpp --- no, that
1097  * syntax doesn't really work] should get HTML2 if available).
1098  *
1099  * (Note that this code only comes into play when we are choosing among
1100  * variants of equal quality, where the draft standard gives us a fair
1101  * bit of leeway about what to do.  It ain't specified by the standard;
1102  * rather, it is a choice made by this server about what to do in cases
1103  * where the standard does not specify a unique course of action).
1104  */
1105
1106 static int level_cmp(var_rec *var1, var_rec *var2)
1107 {
1108     /* Levels are only comparable between matching media types */
1109
1110     if (var1->is_pseudo_html && !var2->is_pseudo_html) {
1111         return 0;
1112     }
1113
1114     if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
1115         return 0;
1116     }
1117     /* The result of the above if statements is that, if we get to
1118      * here, both variants have the same mime_type or both are
1119      * pseudo-html.
1120      */    
1121
1122     /* Take highest level that matched, if either did match. */
1123
1124     if (var1->level_matched > var2->level_matched) {
1125         return 1;
1126     }
1127     if (var1->level_matched < var2->level_matched) {
1128         return -1;
1129     }
1130
1131     /* Neither matched.  Take lowest level, if there's a difference. */
1132
1133     if (var1->level < var2->level) {
1134         return 1;
1135     }
1136     if (var1->level > var2->level) {
1137         return -1;
1138     }
1139
1140     /* Tied */
1141
1142     return 0;
1143 }
1144
1145 /* Finding languages.  The main entry point is set_language_quality()
1146  * which is called for each variant. It sets two elements in the
1147  * variant record:
1148  *    language_quality  - the 'q' value of the 'best' matching language
1149  *                        from Accept-Language: header (HTTP/1.1)
1150  *    lang_index    -     Pre HTTP/1.1 language priority, using
1151  *                        position of language on the Accept-Language:
1152  *                        header, if present, else LanguagePriority
1153  *                        directive order.
1154  *
1155  * When we do the variant checking for best variant, we use language
1156  * quality first, and if a tie, language_index next (this only applies
1157  * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
1158  * algorithm, lang_index is never used.
1159  *
1160  * set_language_quality() calls find_lang_index() and find_default_index()
1161  * to set lang_index.  
1162  */
1163
1164 static int find_lang_index(apr_array_header_t *accept_langs, char *lang)
1165 {
1166     accept_rec *accs;
1167     int i;
1168
1169     if (!lang || !accept_langs) {
1170         return -1;
1171     }
1172
1173     accs = (accept_rec *) accept_langs->elts;
1174
1175     for (i = 0; i < accept_langs->nelts; ++i) {
1176         if (!strncmp(lang, accs[i].name, strlen(accs[i].name))) {
1177             return i;
1178         }
1179     }
1180
1181     return -1;
1182 }
1183
1184 /* This function returns the priority of a given language
1185  * according to LanguagePriority.  It is used in case of a tie
1186  * between several languages.
1187  */
1188
1189 static int find_default_index(neg_dir_config *conf, char *lang)
1190 {
1191     apr_array_header_t *arr;
1192     int nelts;
1193     char **elts;
1194     int i;
1195
1196     if (!lang) {
1197         return -1;
1198     }
1199
1200     arr = conf->language_priority;
1201     nelts = arr->nelts;
1202     elts = (char **) arr->elts;
1203
1204     for (i = 0; i < nelts; ++i) {
1205         if (!strcasecmp(elts[i], lang)) {
1206             return i;
1207         }
1208     }
1209
1210     return -1;
1211 }
1212
1213 /* set_default_lang_quality() sets the quality we apply to variants
1214  * which have no language assigned to them. If none of the variants
1215  * have a language, we are not negotiating on language, so all are
1216  * acceptable, and we set the default q value to 1.0. However if
1217  * some of the variants have languages, we set this default to 0.001.
1218  * The value of this default will be applied to all variants with
1219  * no explicit language -- which will have the effect of making them
1220  * acceptable, but only if no variants with an explicit language
1221  * are acceptable. The default q value set here is assigned to variants
1222  * with no language type in set_language_quality().
1223  *
1224  * Note that if using the RVSA/1.0 algorithm, we don't use this
1225  * fiddle.  
1226  */
1227
1228 static void set_default_lang_quality(negotiation_state *neg)
1229 {
1230     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
1231     int j;
1232
1233     if (!neg->dont_fiddle_headers) {
1234         for (j = 0; j < neg->avail_vars->nelts; ++j) {
1235             var_rec *variant = &avail_recs[j];
1236             if (variant->content_languages &&
1237                 variant->content_languages->nelts) {
1238                 neg->default_lang_quality = 0.001f;
1239                 return;
1240             }
1241         }
1242     }
1243
1244     neg->default_lang_quality = 1.0f;
1245 }
1246
1247 /* Set the language_quality value in the variant record. Also
1248  * assigns lang_index for back-compat. 
1249  *
1250  * To find the language_quality value, we look for the 'q' value
1251  * of the 'best' matching language on the Accept-Language
1252  * header. The 'best' match is the language on Accept-Language
1253  * header which matches the language of this variant either fully,
1254  * or as far as the prefix marker (-). If two or more languages
1255  * match, use the longest string from the Accept-Language header
1256  * (see HTTP/1.1 [14.4])
1257  *
1258  * When a variant has multiple languages, we find the 'best'
1259  * match for each variant language tag as above, then select the
1260  * one with the highest q value. Because both the accept-header
1261  * and variant can have multiple languages, we now have a hairy
1262  * loop-within-a-loop here.
1263  *
1264  * If the variant has no language and we have no Accept-Language
1265  * items, leave the quality at 1.0 and return.
1266  *
1267  * If the variant has no language, we use the default as set by
1268  * set_default_lang_quality() (1.0 if we are not negotiating on
1269  * language, 0.001 if we are).
1270  *
1271  * Following the setting of the language quality, we drop through to
1272  * set the old 'lang_index'. This is set based on either the order
1273  * of the languages on the Accept-Language header, or the
1274  * order on the LanguagePriority directive. This is only used
1275  * in the negotiation if the language qualities tie.
1276  */
1277
1278 static void set_language_quality(negotiation_state *neg, var_rec *variant)
1279 {
1280     char *firstlang;
1281     int idx;
1282
1283     if (!variant->content_languages || !variant->content_languages->nelts) {
1284         /* This variant has no content-language, so use the default
1285          * quality factor for variants with no content-language
1286          * (previously set by set_default_lang_quality()).
1287          * Leave the factor alone (it remains at 1.0) when we may not fiddle
1288          * with the headers.
1289          */
1290         if (!neg->dont_fiddle_headers) {
1291             variant->lang_quality = neg->default_lang_quality;
1292         }
1293         if (!neg->accept_langs) {
1294             return;             /* no accept-language header */
1295         }
1296
1297     }
1298     else {
1299         /* Variant has one (or more) languages.  Look for the best
1300          * match. We do this by going through each language on the
1301          * variant description looking for a match on the
1302          * Accept-Language header. The best match is the longest
1303          * matching language on the header. The final result is the
1304          * best q value from all the languages on the variant
1305          * description.
1306          */
1307
1308         if (!neg->accept_langs) {
1309             /* no accept-language header makes the variant indefinite */
1310             variant->definite = 0;
1311         }
1312         else {    /* There is an accept-language with 0 or more items */
1313             accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
1314             accept_rec *best = NULL, *star = NULL;
1315             accept_rec *bestthistag;
1316             char *lang, *p;
1317             float fiddle_q = 0.0f;
1318             int any_match_on_star = 0;
1319             int i, j;
1320             size_t alen, longest_lang_range_len;
1321
1322             for (j = 0; j < variant->content_languages->nelts; ++j) {
1323                 p = NULL;
1324                 bestthistag = NULL;
1325                 longest_lang_range_len = 0;
1326                 alen = 0;
1327                 
1328                 /* lang is the variant's language-tag, which is the one
1329                  * we are allowed to use the prefix of in HTTP/1.1
1330                  */
1331                 lang = ((char **) (variant->content_languages->elts))[j];
1332                 
1333                 /* now find the best (i.e. longest) matching
1334                  * Accept-Language header language. We put the best match
1335                  * for this tag in bestthistag. We cannot update the
1336                  * overall best (based on q value) because the best match
1337                  * for this tag is the longest language item on the accept
1338                  * header, not necessarily the highest q.
1339                  */
1340                 for (i = 0; i < neg->accept_langs->nelts; ++i) {
1341                     if (!strcmp(accs[i].name, "*")) {
1342                         if (!star) {
1343                             star = &accs[i];
1344                         }
1345                         continue;
1346                     }
1347                     /* Find language. We match if either the variant
1348                      * language tag exactly matches the language range
1349                      * from the accept header, or a prefix of the variant
1350                      * language tag up to a '-' character matches the
1351                      * whole of the language range in the Accept-Language
1352                      * header.  Note that HTTP/1.x allows any number of
1353                      * '-' characters in a tag or range, currently only
1354                      * tags with zero or one '-' characters are defined
1355                      * for general use (see rfc1766).
1356                      *  
1357                      * We only use language range in the Accept-Language
1358                      * header the best match for the variant language tag
1359                      * if it is longer than the previous best match.
1360                      */
1361                     
1362                     alen = strlen(accs[i].name);
1363                     
1364                     if ((strlen(lang) >= alen) &&
1365                         !strncmp(lang, accs[i].name, alen) &&
1366                         ((lang[alen] == 0) || (lang[alen] == '-')) ) {
1367                         
1368                         if (alen > longest_lang_range_len) {
1369                             longest_lang_range_len = alen;
1370                             bestthistag = &accs[i];
1371                         }
1372                     }
1373                     
1374                     if (!bestthistag && !neg->dont_fiddle_headers) {
1375                         /* The next bit is a fiddle. Some browsers might
1376                          * be configured to send more specific language
1377                          * ranges than desirable. For example, an
1378                          * Accept-Language of en-US should never match
1379                          * variants with languages en or en-GB. But US
1380                          * English speakers might pick en-US as their
1381                          * language choice.  So this fiddle checks if the
1382                          * language range has a prefix, and if so, it
1383                          * matches variants which match that prefix with a
1384                          * priority of 0.001. So a request for en-US would
1385                          * match variants of types en and en-GB, but at
1386                          * much lower priority than matches of en-US
1387                          * directly, or of any other language listed on
1388                          * the Accept-Language header. Note that this
1389                          * fiddle does not handle multi-level prefixes.
1390                          */
1391                         if ((p = strchr(accs[i].name, '-'))) {
1392                             int plen = p - accs[i].name;
1393
1394                             if (!strncmp(lang, accs[i].name, plen)) {
1395                                 fiddle_q = 0.001f;
1396                             }
1397                         }
1398                     }
1399                 }
1400                 /* Finished looking at Accept-Language headers, the best
1401                  * (longest) match is in bestthistag, or NULL if no match
1402                  */
1403                 if (!best ||
1404                     (bestthistag && bestthistag->quality > best->quality)) {
1405                     best = bestthistag;
1406                 }
1407                 
1408                 /* See if the tag matches on a * in the Accept-Language
1409                  * header. If so, record this fact for later use 
1410                  */
1411                 if (!bestthistag && star) {
1412                     any_match_on_star = 1;
1413                 }
1414             }
1415             
1416             /* If one of the language tags of the variant matched on *, we
1417              * need to see if its q is better than that of any non-* match
1418              * on any other tag of the variant.  If so the * match takes
1419              * precedence and the overall match is not definite.
1420              */
1421             if ( any_match_on_star &&
1422                 ((best && star->quality > best->quality) ||
1423                  (!best)) ) {
1424                 best = star;
1425                 variant->definite = 0;
1426             }
1427             
1428             variant->lang_quality = best ? best->quality : fiddle_q;
1429         }
1430     }
1431
1432     /* Now set the old lang_index field. Since this is old 
1433      * stuff anyway, don't bother with handling multiple languages
1434      * per variant, just use the first one assigned to it 
1435      */
1436     idx = 0;
1437     if (variant->content_languages && variant->content_languages->nelts) {
1438         firstlang = ((char **) variant->content_languages->elts)[0];
1439     }
1440     else {
1441         firstlang = "";
1442     }
1443     if (!neg->accept_langs) {   /* Client doesn't care */
1444         idx = find_default_index((neg_dir_config *) ap_get_module_config(
1445                                   neg->r->per_dir_config, &negotiation_module),
1446                                  firstlang);
1447     }
1448     else {                      /* Client has Accept-Language */
1449         idx = find_lang_index(neg->accept_langs, firstlang);
1450     }
1451     variant->lang_index = idx;
1452
1453     return;
1454 }
1455
1456 /* Determining the content length --- if the map didn't tell us,
1457  * we have to do a stat() and remember for next time.
1458  *
1459  * Grump.  For Apache, even the first stat here may well be
1460  * redundant (for multiviews) with a stat() done by the sub_req
1461  * machinery.  At some point, that ought to be fixed.
1462  */
1463
1464 static float find_content_length(negotiation_state *neg, var_rec *variant)
1465 {
1466     apr_finfo_t statb;
1467
1468     if (variant->bytes == 0) {
1469         char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
1470                                            variant->file_name);
1471
1472         if (apr_stat(&statb, fullname, neg->pool) == APR_SUCCESS) {
1473             /* Note, precision may be lost */
1474             variant->bytes = (float) statb.size;
1475         }
1476     }
1477
1478     return variant->bytes;
1479 }
1480
1481 /* For a given variant, find the best matching Accept: header
1482  * and assign the Accept: header's quality value to the
1483  * mime_type_quality field of the variant, for later use in
1484  * determining the best matching variant.
1485  */
1486
1487 static void set_accept_quality(negotiation_state *neg, var_rec *variant)
1488 {
1489     int i;
1490     accept_rec *accept_recs;
1491     float q = 0.0f;
1492     int q_definite = 1;
1493
1494     /* if no Accept: header, leave quality alone (will
1495      * remain at the default value of 1) 
1496      *
1497      * XXX: This if is currently never true because of the effect of
1498      * maybe_add_default_accepts().
1499      */
1500     if (!neg->accepts) {
1501         if (variant->mime_type && *variant->mime_type)
1502             variant->definite = 0;
1503         return;
1504     }
1505
1506     accept_recs = (accept_rec *) neg->accepts->elts;
1507
1508     /*
1509      * Go through each of the ranges on the Accept: header,
1510      * looking for the 'best' match with this variant's
1511      * content-type. We use the best match's quality
1512      * value (from the Accept: header) for this variant's
1513      * mime_type_quality field.
1514      *
1515      * The best match is determined like this:
1516      *    type/type is better than type/ * is better than * / *
1517      *    if match is type/type, use the level mime param if available
1518      */
1519     for (i = 0; i < neg->accepts->nelts; ++i) {
1520
1521         accept_rec *type = &accept_recs[i];
1522         int prev_mime_stars;
1523
1524         prev_mime_stars = variant->mime_stars;
1525
1526         if (!mime_match(type, variant)) {
1527             continue;           /* didn't match the content type at all */
1528         }
1529         else {
1530             /* did match - see if there were less or more stars than
1531              * in previous match
1532              */
1533             if (prev_mime_stars == variant->mime_stars) {
1534                 continue;       /* more stars => not as good a match */
1535             }
1536         }
1537
1538         /* If we are allowed to mess with the q-values
1539          * and have no explicit q= parameters in the accept header,
1540          * make wildcards very low, so we have a low chance
1541          * of ending up with them if there's something better.
1542          */
1543
1544         if (!neg->dont_fiddle_headers && !neg->accept_q &&
1545             variant->mime_stars == 1) {
1546             q = 0.01f;
1547         }
1548         else if (!neg->dont_fiddle_headers && !neg->accept_q &&
1549                  variant->mime_stars == 2) {
1550             q = 0.02f;
1551         }
1552         else {
1553             q = type->quality;
1554         }
1555
1556         q_definite = (variant->mime_stars == 3);
1557     }
1558     variant->mime_type_quality = q;
1559     variant->definite = variant->definite && q_definite;
1560
1561 }
1562
1563 /* For a given variant, find the 'q' value of the charset given
1564  * on the Accept-Charset line. If no charsets are listed,
1565  * assume value of '1'.
1566  */
1567 static void set_charset_quality(negotiation_state *neg, var_rec *variant)
1568 {
1569     int i;
1570     accept_rec *accept_recs;
1571     char *charset = variant->content_charset;
1572     accept_rec *star = NULL;
1573
1574     /* if no Accept-Charset: header, leave quality alone (will
1575      * remain at the default value of 1)
1576      */
1577     if (!neg->accept_charsets) {
1578         if (charset && *charset)
1579             variant->definite = 0;
1580         return;
1581     }
1582
1583     accept_recs = (accept_rec *) neg->accept_charsets->elts;
1584
1585     if (charset == NULL || !*charset) {
1586         /* Charset of variant not known */
1587
1588         /* if not a text / * type, leave quality alone */
1589         if (!(!strncmp(variant->mime_type, "text/", 5)
1590               || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
1591               || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3) 
1592               ))
1593             return;
1594
1595         /* Don't go guessing if we are in strict header mode,
1596          * e.g. when running the rvsa, as any guess won't be reflected
1597          * in the variant list or content-location headers.
1598          */
1599         if (neg->dont_fiddle_headers)
1600             return;
1601
1602         charset = "iso-8859-1"; /* The default charset for HTTP text types */
1603     }
1604
1605     /*
1606      * Go through each of the items on the Accept-Charset header,
1607      * looking for a match with this variant's charset. If none
1608      * match, charset is unacceptable, so set quality to 0.
1609      */
1610     for (i = 0; i < neg->accept_charsets->nelts; ++i) {
1611
1612         accept_rec *type = &accept_recs[i];
1613
1614         if (!strcmp(type->name, charset)) {
1615             variant->charset_quality = type->quality;
1616             return;
1617         }
1618         else if (strcmp(type->name, "*") == 0) {
1619             star = type;
1620         }
1621     }
1622     /* No explicit match */
1623     if (star) {
1624         variant->charset_quality = star->quality;
1625         variant->definite = 0;
1626         return;
1627     }
1628     /* If this variant is in charset iso-8859-1, the default is 1.0 */
1629     if (strcmp(charset, "iso-8859-1") == 0) {
1630         variant->charset_quality = 1.0f;
1631     }
1632     else {
1633         variant->charset_quality = 0.0f;
1634     }
1635 }
1636
1637
1638 /* is_identity_encoding is included for back-compat, but does anyone
1639  * use 7bit, 8bin or binary in their var files??
1640  */
1641
1642 static int is_identity_encoding(const char *enc)
1643 {
1644     return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
1645             || !strcmp(enc, "binary"));
1646 }
1647
1648 /*
1649  * set_encoding_quality determines whether the encoding for a particular
1650  * variant is acceptable for the user-agent.
1651  *
1652  * The rules for encoding are that if the user-agent does not supply
1653  * any Accept-Encoding header, then all encodings are allowed but a
1654  * variant with no encoding should be preferred.
1655  * If there is an empty Accept-Encoding header, then no encodings are 
1656  * acceptable. If there is a non-empty Accept-Encoding header, then
1657  * any of the listed encodings are acceptable, as well as no encoding
1658  * unless the "identity" encoding is specifically excluded.
1659  */
1660 static void set_encoding_quality(negotiation_state *neg, var_rec *variant)
1661 {
1662     accept_rec *accept_recs;
1663     const char *enc = variant->content_encoding;
1664     accept_rec *star = NULL;
1665     float value_if_not_found = 0.0f;
1666     int i;
1667
1668     if (!neg->accept_encodings) {
1669         /* We had no Accept-Encoding header, assume that all
1670          * encodings are acceptable with a low quality,
1671          * but we prefer no encoding if available.
1672          */
1673         if (!enc || is_identity_encoding(enc))
1674             variant->encoding_quality = 1.0f;
1675         else
1676             variant->encoding_quality = 0.5f;
1677
1678         return;
1679     }
1680
1681     if (!enc || is_identity_encoding(enc)) {
1682         enc = "identity";
1683         value_if_not_found = 0.0001f;
1684     }
1685
1686     accept_recs = (accept_rec *) neg->accept_encodings->elts;
1687
1688     /* Go through each of the encodings on the Accept-Encoding: header,
1689      * looking for a match with our encoding. x- prefixes are ignored.
1690      */
1691     if (enc[0] == 'x' && enc[1] == '-') {
1692         enc += 2;
1693     }
1694     for (i = 0; i < neg->accept_encodings->nelts; ++i) {
1695
1696         char *name = accept_recs[i].name;
1697
1698         if (name[0] == 'x' && name[1] == '-') {
1699             name += 2;
1700         }
1701
1702         if (!strcmp(name, enc)) {
1703             variant->encoding_quality = accept_recs[i].quality;
1704             return;
1705         }
1706
1707         if (strcmp(name, "*") == 0) {
1708             star = &accept_recs[i];
1709         }
1710
1711     }
1712     /* No explicit match */
1713     if (star) {
1714         variant->encoding_quality = star->quality;
1715         return;
1716     }
1717
1718     /* Encoding not found on Accept-Encoding: header, so it is
1719      * _not_ acceptable unless it is the identity (no encoding)
1720      */
1721     variant->encoding_quality = value_if_not_found;
1722 }
1723
1724 /************************************************************* 
1725  * Possible results of the variant selection algorithm 
1726  */
1727 enum algorithm_results {
1728     alg_choice = 1,              /* choose variant */
1729     alg_list                     /* list variants */
1730 };
1731
1732 /* Below is the 'best_match' function. It returns an int, which has
1733  * one of the two values alg_choice or alg_list, which give the result
1734  * of the variant selection algorithm.  alg_list means that no best
1735  * variant was found by the algorithm, alg_choice means that a best
1736  * variant was found and should be returned.  The list/choice
1737  * terminology comes from TCN (rfc2295), but is used in a more generic
1738  * way here.  The best variant is returned in *pbest. best_match has
1739  * two possible algorithms for determining the best variant: the
1740  * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
1741  * algorithm. These are split out into separate functions
1742  * (is_variant_better_rvsa() and is_variant_better()).  Selection of
1743  * one is through the neg->use_rvsa flag.
1744  *
1745  * The call to best_match also creates full information, including
1746  * language, charset, etc quality for _every_ variant. This is needed
1747  * for generating a correct Vary header, and can be used for the
1748  * Alternates header, the human-readable list responses and 406 errors.
1749  */
1750
1751 /* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
1752  * v1.0) from rfc2296.  This is the algorithm that goes together with
1753  * transparent content negotiation (TCN).
1754  */
1755 static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
1756                                   var_rec *best, float *p_bestq)
1757 {
1758     float bestq = *p_bestq, q;
1759
1760     /* TCN does not cover negotiation on content-encoding.  For now,
1761      * we ignore the encoding unless it was explicitly excluded.
1762      */
1763     if (variant->encoding_quality == 0.0f)
1764         return 0;
1765     
1766     q = variant->mime_type_quality *
1767         variant->source_quality *
1768         variant->charset_quality *
1769         variant->lang_quality;
1770
1771    /* RFC 2296 calls for the result to be rounded to 5 decimal places,
1772     * but we don't do that because it serves no useful purpose other
1773     * than to ensure that a remote algorithm operates on the same
1774     * precision as ours.  That is silly, since what we obviously want
1775     * is for the algorithm to operate on the best available precision
1776     * regardless of who runs it.  Since the above calculation may
1777     * result in significant variance at 1e-12, rounding would be bogus.
1778     */
1779
1780 #ifdef NEG_DEBUG
1781     ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1782            "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
1783            "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
1784            "q=%1.5f definite=%d",            
1785             (variant->file_name ? variant->file_name : ""),
1786             (variant->mime_type ? variant->mime_type : ""),
1787             (variant->content_languages
1788              ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
1789              : ""),
1790             variant->source_quality,
1791             variant->mime_type_quality,
1792             variant->lang_quality,
1793             variant->charset_quality,
1794             variant->encoding_quality,
1795             q,
1796             variant->definite);
1797 #endif
1798
1799     if (q <= 0.0f) {
1800         return 0;
1801     }
1802     if (q > bestq) {
1803         *p_bestq = q;
1804         return 1;
1805     }
1806     if (q == bestq) {
1807         /* If the best variant's encoding is of lesser quality than
1808          * this variant, then we prefer this variant
1809          */
1810         if (variant->encoding_quality > best->encoding_quality) {
1811             *p_bestq = q;
1812             return 1;
1813         }
1814     }
1815     return 0;
1816 }
1817
1818 /* Negotiation algorithm as used by previous versions of Apache
1819  * (just about). 
1820  */
1821
1822 static int is_variant_better(negotiation_state *neg, var_rec *variant,
1823                              var_rec *best, float *p_bestq)
1824 {
1825     float bestq = *p_bestq, q;
1826     int levcmp;
1827
1828     /* For non-transparent negotiation, server can choose how
1829      * to handle the negotiation. We'll use the following in
1830      * order: content-type, language, content-type level, charset,
1831      * content encoding, content length.
1832      *
1833      * For each check, we have three possible outcomes:
1834      *   This variant is worse than current best: return 0
1835      *   This variant is better than the current best:
1836      *          assign this variant's q to *p_bestq, and return 1
1837      *   This variant is just as desirable as the current best:
1838      *          drop through to the next test.
1839      *
1840      * This code is written in this long-winded way to allow future
1841      * customisation, either by the addition of additional
1842      * checks, or to allow the order of the checks to be determined
1843      * by configuration options (e.g. we might prefer to check
1844      * language quality _before_ content type).
1845      */
1846
1847     /* First though, eliminate this variant if it is not
1848      * acceptable by type, charset, encoding or language.
1849      */
1850
1851 #ifdef NEG_DEBUG
1852     ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1853            "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
1854            "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
1855             (variant->file_name ? variant->file_name : ""),
1856             (variant->mime_type ? variant->mime_type : ""),
1857             (variant->content_languages
1858              ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
1859              : ""),
1860             variant->source_quality,
1861             variant->mime_type_quality,
1862             variant->lang_quality,
1863             variant->lang_index,
1864             variant->charset_quality,
1865             variant->encoding_quality);
1866 #endif
1867
1868     if (variant->encoding_quality == 0.0f ||
1869         variant->lang_quality == 0.0f ||
1870         variant->source_quality == 0.0f ||
1871         variant->charset_quality == 0.0f ||
1872         variant->mime_type_quality == 0.0f) {
1873         return 0;               /* don't consider unacceptables */
1874     }
1875
1876     q = variant->mime_type_quality * variant->source_quality;
1877     if (q == 0.0 || q < bestq) {
1878         return 0;
1879     }
1880     if (q > bestq || !best) {
1881         *p_bestq = q;
1882         return 1;
1883     }
1884
1885     /* language */
1886     if (variant->lang_quality < best->lang_quality) {
1887         return 0;
1888     }
1889     if (variant->lang_quality > best->lang_quality) {
1890         *p_bestq = q;
1891         return 1;
1892     }
1893
1894     /* if language qualities were equal, try the LanguagePriority stuff */
1895     if (best->lang_index != -1 &&
1896         (variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
1897         return 0;
1898     }
1899     if (variant->lang_index != -1 &&
1900         (best->lang_index == -1 || variant->lang_index < best->lang_index)) {
1901         *p_bestq = q;
1902         return 1;
1903     }
1904
1905     /* content-type level (sometimes used with text/html, though we
1906      * support it on other types too)
1907      */
1908     levcmp = level_cmp(variant, best);
1909     if (levcmp == -1) {
1910         return 0;
1911     }
1912     if (levcmp == 1) {
1913         *p_bestq = q;
1914         return 1;
1915     }
1916
1917     /* charset */
1918     if (variant->charset_quality < best->charset_quality) {
1919         return 0;
1920     }
1921     /* If the best variant's charset is ISO-8859-1 and this variant has
1922      * the same charset quality, then we prefer this variant
1923      */
1924
1925     if (variant->charset_quality > best->charset_quality ||
1926         ((variant->content_charset != NULL &&
1927           *variant->content_charset != '\0' &&
1928           strcmp(variant->content_charset, "iso-8859-1") != 0) &&
1929          (best->content_charset == NULL ||
1930           *best->content_charset == '\0' ||
1931           strcmp(best->content_charset, "iso-8859-1") == 0))) {
1932         *p_bestq = q;
1933         return 1;
1934     }
1935
1936     /* Prefer the highest value for encoding_quality.
1937      */
1938     if (variant->encoding_quality < best->encoding_quality) {
1939        return 0;
1940     }
1941     if (variant->encoding_quality > best->encoding_quality) {
1942        *p_bestq = q;
1943        return 1;
1944     }
1945
1946     /* content length if all else equal */
1947     if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
1948         return 0;
1949     }
1950
1951     /* ok, to get here means every thing turned out equal, except
1952      * we have a shorter content length, so use this variant
1953      */
1954     *p_bestq = q;
1955     return 1;
1956 }
1957
1958 static int best_match(negotiation_state *neg, var_rec **pbest)
1959 {
1960     int j;
1961     var_rec *best = NULL;
1962     float bestq = 0.0f;
1963     enum algorithm_results algorithm_result;
1964
1965     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
1966
1967     set_default_lang_quality(neg);
1968
1969     /*
1970      * Find the 'best' variant 
1971      */
1972
1973     for (j = 0; j < neg->avail_vars->nelts; ++j) {
1974         var_rec *variant = &avail_recs[j];
1975
1976         /* Find all the relevant 'quality' values from the
1977          * Accept... headers, and store in the variant.  This also
1978          * prepares for sending an Alternates header etc so we need to
1979          * do it even if we do not actually plan to find a best
1980          * variant.  
1981          */
1982         set_accept_quality(neg, variant);
1983         set_language_quality(neg, variant);
1984         set_encoding_quality(neg, variant);
1985         set_charset_quality(neg, variant);
1986
1987         /* Only do variant selection if we may actually choose a
1988          * variant for the client 
1989          */
1990         if (neg->may_choose) {
1991
1992             /* Now find out if this variant is better than the current
1993              * best, either using the RVSA/1.0 algorithm, or Apache's
1994              * internal server-driven algorithm. Presumably other
1995              * server-driven algorithms are possible, and could be
1996              * implemented here.
1997              */
1998      
1999             if (neg->use_rvsa) {
2000                 if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
2001                     best = variant;
2002                 }
2003             }
2004             else {
2005                 if (is_variant_better(neg, variant, best, &bestq)) {
2006                     best = variant;
2007                 }
2008             }
2009         }
2010     }
2011
2012     /* We now either have a best variant, or no best variant */
2013
2014     if (neg->use_rvsa)    {
2015         /* calculate result for RVSA/1.0 algorithm:
2016          * only a choice response if the best variant has q>0
2017          * and is definite
2018          */
2019         algorithm_result = (best && best->definite) && (bestq > 0) ?
2020                            alg_choice : alg_list;
2021     }
2022     else {
2023         /* calculate result for Apache negotiation algorithm */
2024         algorithm_result = bestq > 0 ? alg_choice : alg_list;        
2025     }
2026
2027     /* Returning a choice response with a non-neighboring variant is a
2028      * protocol security error in TCN (see rfc2295).  We do *not*
2029      * verify here that the variant and URI are neighbors, even though
2030      * we may return alg_choice.  We depend on the environment (the
2031      * caller) to only declare the resource transparently negotiable if
2032      * all variants are neighbors.
2033      */
2034     *pbest = best;
2035     return algorithm_result;
2036 }
2037
2038 /* Sets response headers for a negotiated response.
2039  * neg->is_transparent determines whether a transparently negotiated
2040  * response or a plain `server driven negotiation' response is
2041  * created.   Applicable headers are Alternates, Vary, and TCN.
2042  *
2043  * The Vary header we create is sometimes longer than is required for
2044  * the correct caching of negotiated results by HTTP/1.1 caches.  For
2045  * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
2046  * the Accept: header assigns a 0 quality to .ps, then the results of
2047  * the two server-side negotiation algorithms we currently implement
2048  * will never depend on Accept-Language so we could return `Vary:
2049  * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
2050  * accept-language' which the code below will return.  A routine for
2051  * computing the exact minimal Vary header would be a huge pain to code
2052  * and maintain though, especially because we need to take all possible
2053  * twiddles in the server-side negotiation algorithms into account.
2054  */
2055 static void set_neg_headers(request_rec *r, negotiation_state *neg,
2056                             int alg_result)
2057 {
2058     apr_table_t *hdrs;
2059     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2060     const char *sample_type = NULL;
2061     const char *sample_language = NULL;
2062     const char *sample_encoding = NULL;
2063     const char *sample_charset = NULL;
2064     char *lang;
2065     char *qstr;
2066     char *lenstr;
2067     long len;
2068     apr_array_header_t *arr;
2069     int max_vlist_array = (neg->avail_vars->nelts * 21);
2070     int first_variant = 1;
2071     int vary_by_type = 0;
2072     int vary_by_language = 0;
2073     int vary_by_charset = 0;
2074     int vary_by_encoding = 0;
2075     int j;
2076
2077     /* In order to avoid O(n^2) memory copies in building Alternates,
2078      * we preallocate a apr_table_t with the maximum substrings possible,
2079      * fill it with the variant list, and then concatenate the entire array.
2080      * Note that if you change the number of substrings pushed, you also
2081      * need to change the calculation of max_vlist_array above.
2082      */
2083     if (neg->send_alternates && neg->avail_vars->nelts)
2084         arr = apr_make_array(r->pool, max_vlist_array, sizeof(char *));
2085     else
2086         arr = NULL;
2087
2088     /* Put headers into err_headers_out, since send_http_header()
2089      * outputs both headers_out and err_headers_out.
2090      */
2091     hdrs = r->err_headers_out;
2092
2093     for (j = 0; j < neg->avail_vars->nelts; ++j) {
2094         var_rec *variant = &avail_recs[j];
2095
2096         if (variant->content_languages && variant->content_languages->nelts) {
2097             lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
2098         }
2099         else {
2100             lang = NULL;
2101         }
2102
2103         /* Calculate Vary by looking for any difference between variants */
2104
2105         if (first_variant) {
2106             sample_type     = variant->mime_type;
2107             sample_charset  = variant->content_charset;
2108             sample_language = lang;
2109             sample_encoding = variant->content_encoding;
2110         }
2111         else {
2112             if (!vary_by_type &&
2113                 strcmp(sample_type ? sample_type : "", 
2114                        variant->mime_type ? variant->mime_type : "")) {
2115                 vary_by_type = 1;
2116             }
2117             if (!vary_by_charset &&
2118                 strcmp(sample_charset ? sample_charset : "",
2119                        variant->content_charset ?
2120                        variant->content_charset : "")) {
2121                 vary_by_charset = 1;
2122             }
2123             if (!vary_by_language &&
2124                 strcmp(sample_language ? sample_language : "", 
2125                        lang ? lang : "")) {
2126                 vary_by_language = 1;
2127             }
2128             if (!vary_by_encoding &&
2129                 strcmp(sample_encoding ? sample_encoding : "",
2130                        variant->content_encoding ? 
2131                        variant->content_encoding : "")) {
2132                 vary_by_encoding = 1;
2133             }
2134         }
2135         first_variant = 0;
2136
2137         if (!neg->send_alternates)
2138             continue;
2139
2140         /* Generate the string components for this Alternates entry */
2141
2142         *((const char **) apr_push_array(arr)) = "{\"";
2143         *((const char **) apr_push_array(arr)) = variant->file_name;
2144         *((const char **) apr_push_array(arr)) = "\" ";
2145
2146         qstr = (char *) apr_palloc(r->pool, 6);
2147         apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
2148
2149         /* Strip trailing zeros (saves those valuable network bytes) */
2150         if (qstr[4] == '0') {
2151             qstr[4] = '\0';
2152             if (qstr[3] == '0') {
2153                 qstr[3] = '\0';
2154                 if (qstr[2] == '0') {
2155                     qstr[1] = '\0';
2156                 }
2157             }
2158         }
2159         *((const char **) apr_push_array(arr)) = qstr;
2160
2161         if (variant->mime_type && *variant->mime_type) {
2162             *((const char **) apr_push_array(arr)) = " {type ";
2163             *((const char **) apr_push_array(arr)) = variant->mime_type;
2164             *((const char **) apr_push_array(arr)) = "}";
2165         }
2166         if (variant->content_charset && *variant->content_charset) {
2167             *((const char **) apr_push_array(arr)) = " {charset ";
2168             *((const char **) apr_push_array(arr)) = variant->content_charset;
2169             *((const char **) apr_push_array(arr)) = "}";
2170         }
2171         if (lang) {
2172             *((const char **) apr_push_array(arr)) = " {language ";
2173             *((const char **) apr_push_array(arr)) = lang;
2174             *((const char **) apr_push_array(arr)) = "}";
2175         }
2176         if (variant->content_encoding && *variant->content_encoding) {
2177             /* Strictly speaking, this is non-standard, but so is TCN */
2178
2179             *((const char **) apr_push_array(arr)) = " {encoding ";
2180             *((const char **) apr_push_array(arr)) = variant->content_encoding;
2181             *((const char **) apr_push_array(arr)) = "}";
2182         }
2183
2184         /* Note that the Alternates specification (in rfc2295) does
2185          * not require that we include {length x}, so we could omit it
2186          * if determining the length is too expensive.  We currently
2187          * always include it though.  22 bytes is enough for 2^64.
2188          *
2189          * If the variant is a CGI script, find_content_length would
2190          * return the length of the script, not the output it
2191          * produces, so we check for the presence of a handler and if
2192          * there is one we don't add a length.
2193          * 
2194          * XXX: TODO: This check does not detect a CGI script if we
2195          * get the variant from a type map.  This needs to be fixed
2196          * (without breaking things if the type map specifies a
2197          * content-length, which currently leads to the correct result).
2198          */
2199         if (!(variant->sub_req && variant->sub_req->handler)
2200             && (len = find_content_length(neg, variant)) != 0) {
2201
2202             lenstr = (char *) apr_palloc(r->pool, 22);
2203             apr_snprintf(lenstr, 22, "%ld", len);
2204             *((const char **) apr_push_array(arr)) = " {length ";
2205             *((const char **) apr_push_array(arr)) = lenstr;
2206             *((const char **) apr_push_array(arr)) = "}";
2207         }
2208       
2209         *((const char **) apr_push_array(arr)) = "}";
2210         *((const char **) apr_push_array(arr)) = ", "; /* trimmed below */
2211     }
2212
2213     if (neg->send_alternates && neg->avail_vars->nelts) {
2214         arr->nelts--;                                 /* remove last comma */
2215         apr_table_mergen(hdrs, "Alternates",
2216                         apr_array_pstrcat(r->pool, arr, '\0'));
2217     } 
2218
2219     if (neg->is_transparent || vary_by_type || vary_by_language ||
2220         vary_by_language || vary_by_charset || vary_by_encoding) {
2221
2222         apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
2223             neg->is_transparent ? ", negotiate"       : "",
2224             vary_by_type        ? ", accept"          : "",
2225             vary_by_language    ? ", accept-language" : "",
2226             vary_by_charset     ? ", accept-charset"  : "",
2227             vary_by_encoding    ? ", accept-encoding" : "", NULL));
2228     }
2229
2230     if (neg->is_transparent) { /* Create TCN response header */
2231         apr_table_setn(hdrs, "TCN",
2232                       alg_result == alg_list ? "list" : "choice");
2233     }
2234 }
2235
2236 /**********************************************************************
2237  *
2238  * Return an HTML list of variants. This is output as part of the
2239  * choice response or 406 status body.
2240  */
2241
2242 static char *make_variant_list(request_rec *r, negotiation_state *neg)
2243 {
2244     apr_array_header_t *arr;
2245     int i;
2246     int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
2247
2248     /* In order to avoid O(n^2) memory copies in building the list,
2249      * we preallocate a apr_table_t with the maximum substrings possible,
2250      * fill it with the variant list, and then concatenate the entire array.
2251      */
2252     arr = apr_make_array(r->pool, max_vlist_array, sizeof(char *));
2253
2254     *((const char **) apr_push_array(arr)) = "Available variants:\n<ul>\n";
2255
2256     for (i = 0; i < neg->avail_vars->nelts; ++i) {
2257         var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
2258         char *filename = variant->file_name ? variant->file_name : "";
2259         apr_array_header_t *languages = variant->content_languages;
2260         char *description = variant->description ? variant->description : "";
2261
2262         /* The format isn't very neat, and it would be nice to make
2263          * the tags human readable (eg replace 'language en' with 'English').
2264          * Note that if you change the number of substrings pushed, you also
2265          * need to change the calculation of max_vlist_array above.
2266          */
2267         *((const char **) apr_push_array(arr)) = "<li><a href=\"";
2268         *((const char **) apr_push_array(arr)) = filename;
2269         *((const char **) apr_push_array(arr)) = "\">";
2270         *((const char **) apr_push_array(arr)) = filename;
2271         *((const char **) apr_push_array(arr)) = "</a> ";
2272         *((const char **) apr_push_array(arr)) = description;
2273
2274         if (variant->mime_type && *variant->mime_type) {
2275             *((const char **) apr_push_array(arr)) = ", type ";
2276             *((const char **) apr_push_array(arr)) = variant->mime_type;
2277         }
2278         if (languages && languages->nelts) {
2279             *((const char **) apr_push_array(arr)) = ", language ";
2280             *((const char **) apr_push_array(arr)) = apr_array_pstrcat(r->pool,
2281                                                        languages, ',');
2282         }
2283         if (variant->content_charset && *variant->content_charset) {
2284             *((const char **) apr_push_array(arr)) = ", charset ";
2285             *((const char **) apr_push_array(arr)) = variant->content_charset;
2286         }
2287         if (variant->content_encoding) {
2288             *((const char **) apr_push_array(arr)) = ", encoding ";
2289             *((const char **) apr_push_array(arr)) = variant->content_encoding;
2290         }
2291         *((const char **) apr_push_array(arr)) = "\n";
2292     }
2293     *((const char **) apr_push_array(arr)) = "</ul>\n";
2294
2295     return apr_array_pstrcat(r->pool, arr, '\0');
2296 }
2297
2298 static void store_variant_list(request_rec *r, negotiation_state *neg)
2299 {
2300     if (r->main == NULL) {
2301         apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
2302     }
2303     else {
2304         apr_table_setn(r->main->notes, "variant-list",
2305                       make_variant_list(r->main, neg));
2306     }
2307 }
2308
2309 /* Called if we got a "Choice" response from the variant selection algorithm.
2310  * It checks the result of the chosen variant to see if it
2311  * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
2312  * Otherwise, add the appropriate headers to the current response.
2313  */
2314
2315 static int setup_choice_response(request_rec *r, negotiation_state *neg,
2316                                  var_rec *variant)
2317 {
2318     request_rec *sub_req;
2319     const char *sub_vary;
2320
2321     if (!variant->sub_req) {
2322         int status;
2323
2324         sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
2325         status = sub_req->status;
2326
2327         if (status != HTTP_OK && 
2328             !apr_table_get(sub_req->err_headers_out, "TCN")) {
2329             ap_destroy_sub_req(sub_req);
2330             return status;
2331         }
2332         variant->sub_req = sub_req;
2333     }
2334     else {
2335         sub_req = variant->sub_req;
2336     }
2337
2338     /* The variant selection algorithm told us to return a "Choice"
2339      * response. This is the normal variant response, with
2340      * some extra headers. First, ensure that the chosen
2341      * variant did or will not itself engage in transparent negotiation.
2342      * If not, set the appropriate headers, and fall through to
2343      * the normal variant handling 
2344      */
2345
2346     /* This catches the error that a transparent type map selects a
2347      * transparent multiviews resource as the best variant.
2348      *
2349      * XXX: We do not signal an error if a transparent type map
2350      * selects a _non_transparent multiviews resource as the best
2351      * variant, because we can generate a legal negotiation response
2352      * in this case.  In this case, the vlist_validator of the
2353      * nontransparent subrequest will be lost however.  This could
2354      * lead to cases in which a change in the set of variants or the
2355      * negotiation algorithm of the nontransparent resource is never
2356      * propagated up to a HTTP/1.1 cache which interprets Vary.  To be
2357      * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
2358      * for this type of recursive negotiation too.
2359      */
2360     if (neg->is_transparent &&
2361         apr_table_get(sub_req->err_headers_out, "TCN")) {
2362         return HTTP_VARIANT_ALSO_VARIES;
2363     }
2364
2365     /* This catches the error that a transparent type map recursively
2366      * selects, as the best variant, another type map which itself
2367      * causes transparent negotiation to be done.
2368      *
2369      * XXX: Actually, we catch this error by catching all cases of
2370      * type map recursion.  There are some borderline recursive type
2371      * map arrangements which would not produce transparent
2372      * negotiation protocol errors or lack of cache propagation
2373      * problems, but such arrangements are very hard to detect at this
2374      * point in the control flow, so we do not bother to single them
2375      * out.
2376      *
2377      * Recursive type maps imply a recursive arrangement of negotiated
2378      * resources which is visible to outside clients, and this is not
2379      * supported by the transparent negotiation caching protocols, so
2380      * if we are to have generic support for recursive type maps, we
2381      * have to create some configuration setting which makes all type
2382      * maps non-transparent when recursion is enabled.  Also, if we
2383      * want recursive type map support which ensures propagation of
2384      * type map changes into HTTP/1.1 caches that handle Vary, we
2385      * would have to extend the current mechanism for generating
2386      * variant list validators.
2387      */
2388     if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
2389         return HTTP_VARIANT_ALSO_VARIES;
2390     }
2391
2392     /* This adds an appropriate Variant-Vary header if the subrequest
2393      * is a multiviews resource.
2394      *
2395      * XXX: TODO: Note that this does _not_ handle any Vary header
2396      * returned by a CGI if sub_req is a CGI script, because we don't
2397      * see that Vary header yet at this point in the control flow.
2398      * This won't cause any cache consistency problems _unless_ the
2399      * CGI script also returns a Cache-Control header marking the
2400      * response as cachable.  This needs to be fixed, also there are
2401      * problems if a CGI returns an Etag header which also need to be
2402      * fixed.
2403      */
2404     if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
2405         apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
2406
2407         /* Move the subreq Vary header into the main request to
2408          * prevent having two Vary headers in the response, which
2409          * would be legal but strange.
2410          */
2411         apr_table_setn(r->err_headers_out, "Vary", sub_vary);
2412         apr_table_unset(sub_req->err_headers_out, "Vary");
2413     }
2414     
2415     apr_table_setn(r->err_headers_out, "Content-Location",
2416                   apr_pstrdup(r->pool, variant->file_name));
2417
2418     set_neg_headers(r, neg, alg_choice);         /* add Alternates and Vary */
2419
2420     /* Still to do by caller: add Expires */
2421
2422     return 0;
2423 }
2424
2425 /****************************************************************
2426  *
2427  * Executive...
2428  */
2429
2430 static int do_negotiation(request_rec *r, negotiation_state *neg, 
2431                           var_rec **bestp, int prefer_scripts) 
2432 {
2433     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2434     int alg_result;              /* result of variant selection algorithm */
2435     int res;
2436     int j;
2437
2438     /* Decide if resource is transparently negotiable */
2439
2440     /* GET or HEAD? (HEAD has same method number as GET) */
2441     if (r->method_number == M_GET) {
2442
2443         /* maybe this should be configurable, see also the comment
2444          * about recursive type maps in setup_choice_response()
2445          */
2446         neg->is_transparent = 1;       
2447
2448         /* We can't be transparent if we are a map file in the middle
2449          * of the request URI.
2450          */
2451         if (r->path_info && *r->path_info)
2452             neg->is_transparent = 0;
2453
2454         for (j = 0; j < neg->avail_vars->nelts; ++j) {
2455             var_rec *variant = &avail_recs[j];
2456
2457             /* We can't be transparent, because of internal
2458              * assumptions in best_match(), if there is a
2459              * non-neighboring variant.  We can have a non-neighboring
2460              * variant when processing a type map.  
2461              */
2462             if (strchr(variant->file_name, '/'))
2463                 neg->is_transparent = 0;
2464         }
2465     }
2466
2467     if (neg->is_transparent)  {
2468         parse_negotiate_header(r, neg);
2469     }
2470     else { /* configure negotiation on non-transparent resource */
2471         neg->may_choose = 1;
2472     }
2473
2474     maybe_add_default_accepts(neg, prefer_scripts);
2475
2476     alg_result = best_match(neg, bestp);
2477
2478     /* alg_result is one of
2479      *   alg_choice: a best variant is chosen
2480      *   alg_list: no best variant is chosen
2481      */
2482
2483     if (alg_result == alg_list) {
2484         /* send a list response or HTTP_NOT_ACCEPTABLE error response  */
2485
2486         neg->send_alternates = 1; /* always include Alternates header */
2487         set_neg_headers(r, neg, alg_result); 
2488         store_variant_list(r, neg);
2489
2490         if (neg->is_transparent && neg->ua_supports_trans) {
2491             /* XXX todo: expires? cachability? */
2492             
2493             /* Some HTTP/1.0 clients are known to choke when they get
2494              * a 300 (multiple choices) response without a Location
2495              * header.  However the 300 code response we are are about
2496              * to generate will only reach 1.0 clients which support
2497              * transparent negotiation, and they should be OK. The
2498              * response should never reach older 1.0 clients, even if
2499              * we have CacheNegotiatedDocs enabled, because no 1.0
2500              * proxy cache (we know of) will cache and return 300
2501              * responses (they certainly won't if they conform to the
2502              * HTTP/1.0 specification).
2503              */
2504             return HTTP_MULTIPLE_CHOICES;
2505         }
2506         
2507         if (!*bestp) {
2508             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2509                           "no acceptable variant: %s", r->filename);
2510             return HTTP_NOT_ACCEPTABLE;
2511         }
2512     }
2513
2514     /* Variant selection chose a variant */
2515
2516     /* XXX todo: merge the two cases in the if statement below */
2517     if (neg->is_transparent) {
2518
2519         if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
2520             return res; /* return if error */
2521         }
2522     }
2523     else {
2524         set_neg_headers(r, neg, alg_result);
2525     }
2526
2527     /* Make sure caching works - Vary should handle HTTP/1.1, but for
2528      * HTTP/1.0, we can't allow caching at all.
2529      */
2530
2531     /* XXX: Note that we only set r->no_cache to 1, which causes
2532      * Expires: <now> to be added, when responding to a HTTP/1.0
2533      * client.  If we return the response to a 1.1 client, we do not
2534      * add Expires <now>, because doing so would degrade 1.1 cache
2535      * performance by preventing re-use of the response without prior
2536      * revalidation.  On the other hand, if the 1.1 client is a proxy
2537      * which was itself contacted by a 1.0 client, or a proxy cache
2538      * which can be contacted later by 1.0 clients, then we currently
2539      * rely on this 1.1 proxy to add the Expires: <now> when it
2540      * forwards the response.
2541      *
2542      * XXX: TODO: Find out if the 1.1 spec requires proxies and
2543      * tunnels to add Expires: <now> when forwarding the response to
2544      * 1.0 clients.  I (kh) recall it is rather vague on this point.
2545      * Testing actual 1.1 proxy implementations would also be nice. If
2546      * Expires: <now> is not added by proxies then we need to always
2547      * include Expires: <now> ourselves to ensure correct caching, but
2548      * this would degrade HTTP/1.1 cache efficiency unless we also add
2549      * Cache-Control: max-age=N, which we currently don't.
2550      *
2551      * Roy: No, we are not going to screw over HTTP future just to
2552      *      ensure that people who can't be bothered to upgrade their
2553      *      clients will always receive perfect server-side negotiation.
2554      *      Hell, those clients are sending bogus accept headers anyway.
2555      *
2556      *      Manual setting of cache-control/expires always overrides this
2557      *      automated kluge, on purpose.
2558      */
2559     
2560     if ((!do_cache_negotiated_docs(r->server)
2561          && (r->proto_num < HTTP_VERSION(1,1)))        
2562          && neg->count_multiviews_variants != 1) {
2563         r->no_cache = 1;
2564     }
2565
2566     return OK;
2567 }
2568
2569 static int handle_map_file(request_rec *r)
2570 {
2571     negotiation_state *neg;
2572     var_rec *best;
2573     int res;
2574     char *udir;
2575
2576     if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
2577         return DECLINED;
2578
2579     neg = parse_accept_headers(r);
2580     if ((res = read_type_map(neg, r))) {
2581         return res;
2582     }
2583
2584     res = do_negotiation(r, neg, &best, 0);
2585     if (res != 0) return res;
2586
2587     if (r->path_info && *r->path_info) {
2588         r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
2589     }
2590     udir = ap_make_dirstr_parent(r->pool, r->uri);
2591     udir = ap_escape_uri(r->pool, udir);
2592     ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
2593                                     r->path_info, NULL), r);
2594     return OK;
2595 }
2596
2597 static int handle_multi(request_rec *r)
2598 {
2599     negotiation_state *neg;
2600     var_rec *best, *avail_recs;
2601     request_rec *sub_req;
2602     int res;
2603     int j;
2604
2605     if (r->finfo.protection != 0 || !(ap_allow_options(r) & OPT_MULTI)) {
2606         return DECLINED;
2607     }
2608
2609     neg = parse_accept_headers(r);
2610
2611     if ((res = read_types_multi(neg))) {
2612       return_from_multi:
2613         /* free all allocated memory from subrequests */
2614         avail_recs = (var_rec *) neg->avail_vars->elts;
2615         for (j = 0; j < neg->avail_vars->nelts; ++j) {
2616             var_rec *variant = &avail_recs[j];
2617             if (variant->sub_req) {
2618                 ap_destroy_sub_req(variant->sub_req);
2619             }
2620         }
2621         return res;
2622     }
2623     if (neg->avail_vars->nelts == 0) {
2624         return DECLINED;
2625     }
2626
2627     res = do_negotiation(r, neg, &best,
2628                          (r->method_number != M_GET) || r->args ||
2629                          (r->path_info && *r->path_info));
2630     if (res != 0)
2631         goto return_from_multi;
2632
2633     if (!(sub_req = best->sub_req)) {
2634         /* We got this out of a map file, so we don't actually have
2635          * a sub_req structure yet.  Get one now.
2636          */
2637
2638         sub_req = ap_sub_req_lookup_file(best->file_name, r, NULL);
2639         if (sub_req->status != HTTP_OK) {
2640             res = sub_req->status;
2641             ap_destroy_sub_req(sub_req);
2642             goto return_from_multi;
2643         }
2644     }
2645
2646     /* BLECH --- don't multi-resolve non-ordinary files */
2647
2648     if (sub_req->finfo.filetype != APR_REG) {
2649         res = HTTP_NOT_FOUND;
2650         goto return_from_multi;
2651     }
2652
2653     /* Otherwise, use it. */
2654
2655     /* now do a "fast redirect" ... promote the sub_req into the main req */
2656     /* We need to tell POOL_DEBUG that we're guaranteeing that sub_req->pool
2657      * will exist as long as r->pool.  Otherwise we run into troubles because
2658      * some values in this request will be allocated in r->pool, and others in
2659      * sub_req->pool.
2660      */
2661     apr_pool_join(r->pool, sub_req->pool);
2662     r->mtime = 0; /* reset etag info for subrequest */
2663     r->filename = sub_req->filename;
2664     r->handler = sub_req->handler;
2665     r->content_type = sub_req->content_type;
2666     r->content_encoding = sub_req->content_encoding;
2667     r->content_languages = sub_req->content_languages;
2668     r->content_language = sub_req->content_language;
2669     r->finfo = sub_req->finfo;
2670     r->per_dir_config = sub_req->per_dir_config;
2671     /* copy output headers from subrequest, but leave negotiation headers */
2672     r->notes = apr_overlay_tables(r->pool, sub_req->notes, r->notes);
2673     r->headers_out = apr_overlay_tables(r->pool, sub_req->headers_out,
2674                                     r->headers_out);
2675     r->err_headers_out = apr_overlay_tables(r->pool, sub_req->err_headers_out,
2676                                         r->err_headers_out);
2677     r->subprocess_env = apr_overlay_tables(r->pool, sub_req->subprocess_env,
2678                                        r->subprocess_env);
2679     avail_recs = (var_rec *) neg->avail_vars->elts;
2680     for (j = 0; j < neg->avail_vars->nelts; ++j) {
2681         var_rec *variant = &avail_recs[j];
2682         if (variant != best && variant->sub_req) {
2683             ap_destroy_sub_req(variant->sub_req);
2684         }
2685     }
2686     return OK;
2687 }
2688
2689 /********************************************************************** 
2690  * There is a problem with content-encoding, as some clients send and
2691  * expect an x- token (e.g. x-gzip) while others expect the plain token
2692  * (i.e. gzip). To try and deal with this as best as possible we do
2693  * the following: if the client sent an Accept-Encoding header and it
2694  * contains a plain token corresponding to the content encoding of the
2695  * response, then set content encoding using the plain token. Else if
2696  * the A-E header contains the x- token use the x- token in the C-E
2697  * header. Else don't do anything.
2698  *
2699  * Note that if no A-E header was sent, or it does not contain a token
2700  * compatible with the final content encoding, then the token in the
2701  * C-E header will be whatever was specified in the AddEncoding
2702  * directive.
2703  */
2704 static int fix_encoding(request_rec *r)
2705 {
2706     const char *enc = r->content_encoding;
2707     char *x_enc = NULL;
2708     apr_array_header_t *accept_encodings;
2709     accept_rec *accept_recs;
2710     int i;
2711
2712     if (!enc || !*enc) {
2713         return DECLINED;
2714     }
2715
2716     if (enc[0] == 'x' && enc[1] == '-') {
2717         enc += 2;
2718     }
2719
2720     if ((accept_encodings = do_header_line(r->pool,
2721              apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
2722         return DECLINED;
2723     }
2724
2725     accept_recs = (accept_rec *) accept_encodings->elts;
2726
2727     for (i = 0; i < accept_encodings->nelts; ++i) {
2728         char *name = accept_recs[i].name;
2729
2730         if (!strcmp(name, enc)) {
2731             r->content_encoding = name;
2732             return OK;
2733         }
2734
2735         if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
2736             x_enc = name;
2737         }
2738     }
2739
2740     if (x_enc) {
2741         r->content_encoding = x_enc;
2742         return OK;
2743     }
2744
2745     return DECLINED;
2746 }
2747
2748 static void register_hooks(void)
2749 {
2750     ap_hook_fixups(fix_encoding,NULL,NULL,AP_HOOK_MIDDLE);
2751     ap_hook_type_checker(handle_multi,NULL,NULL,AP_HOOK_FIRST);
2752     ap_hook_handler(handle_map_file,NULL,NULL,AP_HOOK_MIDDLE);
2753 }
2754
2755 module AP_MODULE_DECLARE_DATA negotiation_module =
2756 {
2757     STANDARD20_MODULE_STUFF,
2758     create_neg_dir_config,      /* dir config creator */
2759     merge_neg_dir_configs,      /* dir merger --- default is to override */
2760     NULL,                       /* server config */
2761     NULL,                       /* merge server config */
2762     negotiation_cmds,           /* command apr_table_t */
2763     register_hooks              /* register hooks */
2764 };