bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / http / mod_mime.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * http_mime.c: Sends/gets MIME headers for requests
19  * 
20  * Rob McCool
21  * 
22  */
23
24 #include "apr.h"
25 #include "apr_strings.h"
26 #include "apr_lib.h"
27 #include "apr_hash.h"
28
29 #define APR_WANT_STRFUNC
30 #include "apr_want.h"
31
32 #include "ap_config.h"
33 #include "httpd.h"
34 #include "http_config.h"
35 #include "http_log.h"
36 #include "http_request.h"
37 #include "http_protocol.h"
38
39 /* XXXX - fix me / EBCDIC
40  *        there was a cludge here which would use its
41  *        own version apr_isascii(). Indicating that
42  *        on some platforms that might be needed. 
43  *
44  *        #define OS_ASC(c) (c)             -- for mere mortals 
45  *     or
46  *        #define OS_ASC(c) (ebcdic2ascii[c]) -- for dino's
47  *
48  *        #define apr_isascii(c) ((OS_ASC(c) & 0x80) == 0)
49  */
50
51 /* XXXXX - fix me - See note with NOT_PROXY 
52  */
53
54 typedef struct attrib_info {
55     char *name;
56     int   offset;
57 } attrib_info;
58
59 /* Information to which an extension can be mapped
60  */
61 typedef struct extension_info {
62     char *forced_type;                /* Additional AddTyped stuff */
63     char *encoding_type;              /* Added with AddEncoding... */
64     char *language_type;              /* Added with AddLanguage... */
65     char *handler;                    /* Added with AddHandler... */
66     char *charset_type;               /* Added with AddCharset... */
67     char *input_filters;              /* Added with AddInputFilter... */
68     char *output_filters;             /* Added with AddOutputFilter... */
69 } extension_info;
70
71 #define MULTIMATCH_UNSET      0
72 #define MULTIMATCH_ANY        1
73 #define MULTIMATCH_NEGOTIATED 2
74 #define MULTIMATCH_HANDLERS   4
75 #define MULTIMATCH_FILTERS    8
76
77 typedef struct {
78     apr_hash_t *extension_mappings;  /* Map from extension name to
79                                       * extension_info structure */
80
81     apr_array_header_t *remove_mappings; /* A simple list, walked once */
82
83     char *default_language;     /* Language if no AddLanguage ext found */
84
85     int multimatch;       /* Extensions to include in multiview matching
86                            * for filenames, e.g. Filters and Handlers 
87                            */
88     int use_path_info;    /* If set to 0, only use filename.
89                            * If set to 1, append PATH_INFO to filename for
90                            *   lookups.
91                            * If set to 2, this value is unset and is
92                            *   effectively 0.  
93                            */
94 } mime_dir_config;
95
96 typedef struct param_s {
97     char *attr;
98     char *val;
99     struct param_s *next;
100 } param;
101
102 typedef struct {
103     const char *type;
104     apr_size_t type_len;
105     const char *subtype;
106     apr_size_t subtype_len;
107     param *param;
108 } content_type;
109
110 static char tspecial[] = {
111     '(', ')', '<', '>', '@', ',', ';', ':',
112     '\\', '"', '/', '[', ']', '?', '=',
113     '\0'
114 };
115
116 module AP_MODULE_DECLARE_DATA mime_module;
117
118 static void *create_mime_dir_config(apr_pool_t *p, char *dummy)
119 {
120     mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
121
122     new->extension_mappings = NULL;
123     new->remove_mappings = NULL;
124
125     new->default_language = NULL;
126
127     new->multimatch = MULTIMATCH_UNSET;
128
129     new->use_path_info = 2;
130
131     return new;
132 }
133 /*
134  * Overlay one hash table of extension_mappings onto another
135  */
136 static void *overlay_extension_mappings(apr_pool_t *p,
137                                         const void *key,
138                                         apr_ssize_t klen,
139                                         const void *overlay_val,
140                                         const void *base_val,
141                                         const void *data)
142 {
143     extension_info *new_info = apr_palloc(p, sizeof(extension_info));
144     const extension_info *overlay_info = (const extension_info *)overlay_val;
145     const extension_info *base_info = (const extension_info *)base_val;
146
147     memcpy(new_info, base_info, sizeof(extension_info));
148     if (overlay_info->forced_type) {
149         new_info->forced_type = overlay_info->forced_type;
150     }
151     if (overlay_info->encoding_type) {
152         new_info->encoding_type = overlay_info->encoding_type;
153     }
154     if (overlay_info->language_type) {
155         new_info->language_type = overlay_info->language_type;
156     }
157     if (overlay_info->handler) {
158         new_info->handler = overlay_info->handler;
159     }
160     if (overlay_info->charset_type) {
161         new_info->charset_type = overlay_info->charset_type;
162     }
163     if (overlay_info->input_filters) {
164         new_info->input_filters = overlay_info->input_filters;
165     }
166     if (overlay_info->output_filters) {
167         new_info->output_filters = overlay_info->output_filters;
168     }
169
170     return new_info;
171 }
172
173 /* Member is the offset within an extension_info of the pointer to reset 
174  */
175 static void remove_items(apr_pool_t *p, apr_array_header_t *remove, 
176                          apr_hash_t *mappings)
177 {
178     attrib_info *suffix = (attrib_info *) remove->elts;
179     int i;
180     for (i = 0; i < remove->nelts; i++) {
181         extension_info *exinfo = apr_hash_get(mappings,
182                                               suffix[i].name,
183                                               APR_HASH_KEY_STRING);
184         if (exinfo && *(const char**)((char *)exinfo + suffix[i].offset)) {
185             extension_info *copyinfo = exinfo;
186             exinfo = (extension_info*)apr_palloc(p, sizeof(*exinfo));
187             apr_hash_set(mappings, suffix[i].name, 
188                          APR_HASH_KEY_STRING, exinfo);
189             memcpy(exinfo, copyinfo, sizeof(*exinfo));
190             *(const char**)((char *)exinfo + suffix[i].offset) = NULL;
191         }
192     }
193 }
194
195 static void *merge_mime_dir_configs(apr_pool_t *p, void *basev, void *addv)
196 {
197     mime_dir_config *base = (mime_dir_config *)basev;
198     mime_dir_config *add = (mime_dir_config *)addv;
199     mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
200
201     if (base->extension_mappings && add->extension_mappings) {
202         new->extension_mappings = apr_hash_merge(p, add->extension_mappings,
203                                                  base->extension_mappings,
204                                                  overlay_extension_mappings,
205                                                  NULL);
206     }
207     else {
208         if (base->extension_mappings == NULL) {
209             new->extension_mappings = add->extension_mappings;
210         }
211         else {
212             new->extension_mappings = base->extension_mappings;
213         }
214         /* We may not be merging the tables, but if we potentially will change
215          * an exinfo member, then we are about to trounce it anyways.
216          * We must have a copy for safety.
217          */
218         if (new->extension_mappings && add->remove_mappings) {
219             new->extension_mappings =
220                 apr_hash_copy(p, new->extension_mappings);
221         }
222     }
223
224     if (new->extension_mappings) {
225         if (add->remove_mappings)
226             remove_items(p, add->remove_mappings, new->extension_mappings);
227     }
228     new->remove_mappings = NULL;
229
230     new->default_language = add->default_language ?
231         add->default_language : base->default_language;
232
233     new->multimatch = (add->multimatch != MULTIMATCH_UNSET) ?
234         add->multimatch : base->multimatch;
235
236     if ((add->use_path_info & 2) == 0) {
237         new->use_path_info = add->use_path_info;
238     }
239     else {
240         new->use_path_info = base->use_path_info;
241     }
242
243     return new;
244 }
245
246 static const char *add_extension_info(cmd_parms *cmd, void *m_, 
247                                       const char *value_, const char* ext)
248 {
249     mime_dir_config *m=m_;
250     extension_info *exinfo;
251     int offset = (int) (long) cmd->info;
252     char *key = apr_pstrdup(cmd->temp_pool, ext);
253     char *value = apr_pstrdup(cmd->pool, value_);
254     ap_str_tolower(value);
255     ap_str_tolower(key);
256
257     if (*key == '.') {
258         ++key;
259     }
260     if (!m->extension_mappings) {
261         m->extension_mappings = apr_hash_make(cmd->pool);
262         exinfo = NULL;
263     }
264     else {
265         exinfo = (extension_info*)apr_hash_get(m->extension_mappings, key,
266                                                APR_HASH_KEY_STRING);
267     }
268     if (!exinfo) {
269         exinfo = apr_pcalloc(cmd->pool, sizeof(extension_info));
270         key = apr_pstrdup(cmd->pool, key);
271         apr_hash_set(m->extension_mappings, key, APR_HASH_KEY_STRING, exinfo);
272     }
273     *(const char**)((char *)exinfo + offset) = value;
274     return NULL;
275 }
276
277 /*
278  * Note handler names are un-added with each per_dir_config merge.
279  * This keeps the association from being inherited, but not
280  * from being re-added at a subordinate level.
281  */
282 static const char *remove_extension_info(cmd_parms *cmd, void *m_, 
283                                          const char *ext)
284 {
285     mime_dir_config *m = (mime_dir_config *) m_;
286     attrib_info *suffix;
287     if (*ext == '.') {
288         ++ext;
289     }
290     if (!m->remove_mappings) {
291         m->remove_mappings = apr_array_make(cmd->pool, 4, sizeof(*suffix));
292     }
293     suffix = (attrib_info *)apr_array_push(m->remove_mappings);
294     suffix->name = apr_pstrdup(cmd->pool, ext);
295     ap_str_tolower(suffix->name);
296     suffix->offset = (int) (long) cmd->info;
297     return NULL;
298 }
299
300 /* The sole bit of server configuration that the MIME module has is
301  * the name of its config file, so...
302  */
303
304 static const char *set_types_config(cmd_parms *cmd, void *dummy,
305                                     const char *arg)
306 {
307     ap_set_module_config(cmd->server->module_config, &mime_module,
308                          (void *)arg);
309     return NULL;
310 }
311
312 static const char *multiviews_match(cmd_parms *cmd, void *m_, 
313                                     const char *include)
314 {
315     mime_dir_config *m = (mime_dir_config *) m_;
316
317     if (strcasecmp(include, "Any") == 0) {
318         if (m->multimatch && (m->multimatch & ~MULTIMATCH_ANY)) {
319             return "Any is incompatible with NegotiatedOnly, "
320                    "Filters and Handlers";
321         }
322         m->multimatch |= MULTIMATCH_ANY;
323     }
324     else if (strcasecmp(include, "NegotiatedOnly") == 0) {
325         if (m->multimatch && (m->multimatch & ~MULTIMATCH_NEGOTIATED)) {
326             return "Any is incompatible with NegotiatedOnly, "
327                    "Filters and Handlers";
328         }
329         m->multimatch |= MULTIMATCH_NEGOTIATED;
330     }
331     else if (strcasecmp(include, "Filters") == 0) {
332         if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED 
333                                              | MULTIMATCH_ANY))) {
334             return "Filters is incompatible with Any and NegotiatedOnly";
335         }
336         m->multimatch |= MULTIMATCH_FILTERS;
337     }
338     else if (strcasecmp(include, "Handlers") == 0) {
339         if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED 
340                                              | MULTIMATCH_ANY))) {
341             return "Handlers is incompatible with Any and NegotiatedOnly";
342         }
343         m->multimatch |= MULTIMATCH_HANDLERS;
344     }
345     else {
346         return "Unrecognized option";
347     }
348
349     return NULL;
350 }
351
352 static const command_rec mime_cmds[] =
353 {
354     AP_INIT_ITERATE2("AddCharset", add_extension_info, 
355         (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
356         "a charset (e.g., iso-2022-jp), followed by one or more "
357         "file extensions"),
358     AP_INIT_ITERATE2("AddEncoding", add_extension_info, 
359         (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
360         "an encoding (e.g., gzip), followed by one or more file extensions"),
361     AP_INIT_ITERATE2("AddHandler", add_extension_info, 
362         (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
363         "a handler name followed by one or more file extensions"),
364     AP_INIT_ITERATE2("AddInputFilter", add_extension_info, 
365         (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
366         "input filter name (or ; delimited names) followed by one or "
367         "more file extensions"),
368     AP_INIT_ITERATE2("AddLanguage", add_extension_info, 
369         (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
370         "a language (e.g., fr), followed by one or more file extensions"),
371     AP_INIT_ITERATE2("AddOutputFilter", add_extension_info, 
372         (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO, 
373         "output filter name (or ; delimited names) followed by one or "
374         "more file extensions"),
375     AP_INIT_ITERATE2("AddType", add_extension_info, 
376         (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO, 
377         "a mime type followed by one or more file extensions"),
378     AP_INIT_TAKE1("DefaultLanguage", ap_set_string_slot,
379         (void*)APR_OFFSETOF(mime_dir_config, default_language), OR_FILEINFO,
380         "language to use for documents with no other language file extension"),
381     AP_INIT_ITERATE("MultiviewsMatch", multiviews_match, NULL, OR_FILEINFO,
382         "NegotiatedOnly (default), Handlers and/or Filters, or Any"),
383     AP_INIT_ITERATE("RemoveCharset", remove_extension_info, 
384         (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
385         "one or more file extensions"),
386     AP_INIT_ITERATE("RemoveEncoding", remove_extension_info, 
387         (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
388         "one or more file extensions"),
389     AP_INIT_ITERATE("RemoveHandler", remove_extension_info, 
390         (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
391         "one or more file extensions"),
392     AP_INIT_ITERATE("RemoveInputFilter", remove_extension_info, 
393         (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
394         "one or more file extensions"),
395     AP_INIT_ITERATE("RemoveLanguage", remove_extension_info, 
396         (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
397         "one or more file extensions"),
398     AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info, 
399         (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
400         "one or more file extensions"),
401     AP_INIT_ITERATE("RemoveType", remove_extension_info, 
402         (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
403         "one or more file extensions"),
404     AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
405         "the MIME types config file"),
406     AP_INIT_FLAG("ModMimeUsePathInfo", ap_set_flag_slot,
407         (void *)APR_OFFSETOF(mime_dir_config, use_path_info), ACCESS_CONF,
408         "Set to 'yes' to allow mod_mime to use path info for type checking"),
409     {NULL}
410 };
411
412 static apr_hash_t *mime_type_extensions;
413
414 static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
415 {
416     ap_configfile_t *f;
417     char l[MAX_STRING_LEN];
418     const char *types_confname = ap_get_module_config(s->module_config,
419                                                       &mime_module);
420     apr_status_t status;
421
422     if (!types_confname) {
423         types_confname = AP_TYPES_CONFIG_FILE;
424     }
425
426     types_confname = ap_server_root_relative(p, types_confname);
427     if (!types_confname) {
428         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
429                      "Invalid mime types config path %s", 
430                      (const char *)ap_get_module_config(s->module_config,
431                                                         &mime_module));
432         return HTTP_INTERNAL_SERVER_ERROR;
433     }
434     if ((status = ap_pcfg_openfile(&f, ptemp, types_confname)) 
435                 != APR_SUCCESS) {
436         ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
437                      "could not open mime types config file %s.", 
438                      types_confname);
439         return HTTP_INTERNAL_SERVER_ERROR;
440     }
441
442     mime_type_extensions = apr_hash_make(p);
443
444     while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
445         const char *ll = l, *ct;
446
447         if (l[0] == '#') {
448             continue;
449         }
450         ct = ap_getword_conf(p, &ll);
451
452         while (ll[0]) {
453             char *ext = ap_getword_conf(p, &ll);
454             ap_str_tolower(ext);
455             apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
456         }
457     }
458     ap_cfg_closefile(f);
459     return OK;
460 }
461
462 static const char *zap_sp(const char *s)
463 {
464     if (s == NULL) {
465         return (NULL);
466     }
467     if (*s == '\0') {
468         return (s);
469     }
470
471     /* skip prefixed white space */
472     for (; *s == ' ' || *s == '\t' || *s == '\n'; s++)
473         ;
474
475     return (s);
476 }
477
478 static char *zap_sp_and_dup(apr_pool_t *p, const char *start,
479                             const char *end, apr_size_t *len)
480 {
481     while ((start < end) && apr_isspace(*start)) {
482         start++;
483     }
484     while ((end > start) && apr_isspace(*(end - 1))) {
485         end--;
486     }
487     if (len) {
488         *len = end - start;
489     }
490     return apr_pstrmemdup(p, start, end - start);
491 }
492
493 static int is_token(char c)
494 {
495     int res;
496
497     res = (apr_isascii(c) && apr_isgraph(c)
498            && (strchr(tspecial, c) == NULL)) ? 1 : -1;
499     return res;
500 }
501
502 static int is_qtext(char c)
503 {
504     int res;
505
506     res = (apr_isascii(c) && (c != '"') && (c != '\\') && (c != '\n'))
507         ? 1 : -1;
508     return res;
509 }
510
511 static int is_quoted_pair(const char *s)
512 {
513     int res = -1;
514     int c;
515
516     if (((s + 1) != NULL) && (*s == '\\')) {
517         c = (int) *(s + 1);
518         if (apr_isascii(c)) {
519             res = 1;
520         }
521     }
522     return (res);
523 }
524
525 static content_type *analyze_ct(request_rec *r, const char *s)
526 {
527     const char *cp, *mp;
528     char *attribute, *value;
529     int quoted = 0;
530     server_rec * ss = r->server;
531     apr_pool_t * p = r->pool;
532
533     content_type *ctp;
534     param *pp, *npp;
535
536     /* initialize ctp */
537     ctp = (content_type *)apr_palloc(p, sizeof(content_type));
538     ctp->type = NULL;
539     ctp->subtype = NULL;
540     ctp->param = NULL;
541
542     mp = s;
543
544     /* getting a type */
545     cp = mp;
546     while (apr_isspace(*cp)) {
547         cp++;
548     }
549     if (!*cp) {
550         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
551                      "mod_mime: analyze_ct: cannot get media type from '%s'",
552                      (const char *) mp);
553         return (NULL);
554     }
555     ctp->type = cp;
556     do {
557         cp++;
558     } while (*cp && (*cp != '/') && !apr_isspace(*cp) && (*cp != ';'));
559     if (!*cp || (*cp == ';')) {
560         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
561                      "Cannot get media type from '%s'",
562                      (const char *) mp);
563         return (NULL);
564     }
565     while (apr_isspace(*cp)) {
566         cp++;
567     }
568     if (*cp != '/') {
569         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
570                      "mod_mime: analyze_ct: cannot get media type from '%s'",
571                      (const char *) mp);
572         return (NULL);
573     }
574     ctp->type_len = cp - ctp->type;
575
576     cp++; /* skip the '/' */
577
578     /* getting a subtype */
579     while (apr_isspace(*cp)) {
580         cp++;
581     }
582     if (!*cp) {
583         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
584                      "Cannot get media subtype.");
585         return (NULL);
586     }
587     ctp->subtype = cp;
588     do {
589         cp++;
590     } while (*cp && !apr_isspace(*cp) && (*cp != ';'));
591     ctp->subtype_len = cp - ctp->subtype;
592     while (apr_isspace(*cp)) {
593         cp++;
594     }
595
596     if (*cp == '\0') {
597         return (ctp);
598     }
599
600     /* getting parameters */
601     cp++; /* skip the ';' */
602     cp = zap_sp(cp);
603     if (cp == NULL || *cp == '\0') {
604         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
605                      "Cannot get media parameter.");
606         return (NULL);
607     }
608     mp = cp;
609     attribute = NULL;
610     value = NULL;
611
612     while (cp != NULL && *cp != '\0') {
613         if (attribute == NULL) {
614             if (is_token(*cp) > 0) {
615                 cp++;
616                 continue;
617             }
618             else if (*cp == ' ' || *cp == '\t' || *cp == '\n') {
619                 cp++;
620                 continue;
621             }
622             else if (*cp == '=') {
623                 attribute = zap_sp_and_dup(p, mp, cp, NULL);
624                 if (attribute == NULL || *attribute == '\0') {
625                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
626                                  "Cannot get media parameter.");
627                     return (NULL);
628                 }
629                 cp++;
630                 cp = zap_sp(cp);
631                 if (cp == NULL || *cp == '\0') {
632                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
633                                  "Cannot get media parameter.");
634                     return (NULL);
635                 }
636                 mp = cp;
637                 continue;
638             }
639             else {
640                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
641                              "Cannot get media parameter.");
642                 return (NULL);
643             }
644         }
645         else {
646             if (mp == cp) {
647                 if (*cp == '"') {
648                     quoted = 1;
649                     cp++;
650                 }
651                 else {
652                     quoted = 0;
653                 }
654             }
655             if (quoted > 0) {
656                 while (quoted && *cp != '\0') {
657                     if (is_qtext(*cp) > 0) {
658                         cp++;
659                     }
660                     else if (is_quoted_pair(cp) > 0) {
661                         cp += 2;
662                     }
663                     else if (*cp == '"') {
664                         cp++;
665                         while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
666                             cp++;
667                         }
668                         if (*cp != ';' && *cp != '\0') {
669                             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
670                                          "Cannot get media parameter.");
671                             return(NULL);
672                         }
673                         quoted = 0;
674                     }
675                     else {
676                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
677                                      "Cannot get media parameter.");
678                         return (NULL);
679                     }
680                 }
681             }
682             else {
683                 while (1) {
684                     if (is_token(*cp) > 0) {
685                         cp++;
686                     }
687                     else if (*cp == '\0' || *cp == ';') {
688                         break;
689                     }
690                     else {
691                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
692                                      "Cannot get media parameter.");
693                         return (NULL);
694                     }
695                 }
696             }
697             value = zap_sp_and_dup(p, mp, cp, NULL);
698             if (value == NULL || *value == '\0') {
699                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
700                              "Cannot get media parameter.");
701                 return (NULL);
702             }
703
704             pp = apr_palloc(p, sizeof(param));
705             pp->attr = attribute;
706             pp->val = value;
707             pp->next = NULL;
708
709             if (ctp->param == NULL) {
710                 ctp->param = pp;
711             }
712             else {
713                 npp = ctp->param;
714                 while (npp->next) {
715                     npp = npp->next;
716                 }
717                 npp->next = pp;
718             }
719             quoted = 0;
720             attribute = NULL;
721             value = NULL;
722             if (*cp == '\0') {
723                 break;
724             }
725             cp++;
726             mp = cp;
727         }
728     }
729     return (ctp);
730 }
731
732 /*
733  * find_ct is the hook routine for determining content-type and other
734  * MIME-related metadata.  It assumes that r->filename has already been
735  * set and stat has been called for r->finfo.  It also assumes that the
736  * non-path base file name is not the empty string unless it is a dir.
737  */
738 static int find_ct(request_rec *r)
739 {
740     mime_dir_config *conf;
741     apr_array_header_t *exception_list;
742     char *ext;
743     const char *fn, *type, *charset = NULL, *resource_name;
744     int found_metadata = 0;
745
746     if (r->finfo.filetype == APR_DIR) {
747         ap_set_content_type(r, DIR_MAGIC_TYPE);
748         return OK;
749     }
750
751     if (!r->filename) {
752         return DECLINED;
753     }
754
755     conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
756                                                    &mime_module);
757     exception_list = apr_array_make(r->pool, 2, sizeof(char *));
758
759     /* If use_path_info is explicitly set to on (value & 1 == 1), append. */
760     if (conf->use_path_info & 1) {
761         resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
762     }
763     else {
764         resource_name = r->filename;
765     }
766
767     /* Always drop the path leading up to the file name.
768      */
769     if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
770         fn = resource_name;
771     }
772     else {
773         ++fn;
774     }
775
776     /* The exception list keeps track of those filename components that
777      * are not associated with extensions indicating metadata.
778      * The base name is always the first exception (i.e., "txt.html" has
779      * a basename of "txt" even though it might look like an extension).
780      */
781     ext = ap_getword(r->pool, &fn, '.');
782     *((const char **)apr_array_push(exception_list)) = ext;
783
784     /* Parse filename extensions which can be in any order 
785      */
786     while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
787         const extension_info *exinfo = NULL;
788         int found;
789
790         if (*ext == '\0') {  /* ignore empty extensions "bad..html" */
791             continue;
792         }
793
794         found = 0;
795
796         ap_str_tolower(ext);
797
798         if (conf->extension_mappings != NULL) {
799             exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
800                                                    ext, APR_HASH_KEY_STRING);
801         }
802
803         if (exinfo == NULL || !exinfo->forced_type) {
804             if ((type = apr_hash_get(mime_type_extensions, ext,
805                                      APR_HASH_KEY_STRING)) != NULL) {
806                 ap_set_content_type(r, (char*) type);
807                 found = 1;
808             }
809         }
810
811         if (exinfo != NULL) {
812
813             if (exinfo->forced_type) {
814                 ap_set_content_type(r, exinfo->forced_type);
815                 found = 1;
816             }
817
818             if (exinfo->charset_type) {
819                 charset = exinfo->charset_type;
820                 found = 1;
821             }
822             if (exinfo->language_type) {
823                 if (!r->content_languages) {
824                     r->content_languages = apr_array_make(r->pool, 2,
825                                                           sizeof(char *));
826                 }
827                 *((const char **)apr_array_push(r->content_languages))
828                     = exinfo->language_type;
829                 found = 1;
830             }
831             if (exinfo->encoding_type) {
832                 if (!r->content_encoding) {
833                     r->content_encoding = exinfo->encoding_type;
834                 }
835                 else {
836                     /* XXX should eliminate duplicate entities */
837                     r->content_encoding = apr_pstrcat(r->pool,
838                                                       r->content_encoding,
839                                                       ", ",
840                                                       exinfo->encoding_type,
841                                                       NULL);
842                 }
843                 found = 1;
844             }
845             /* The following extensions are not 'Found'.  That is, they don't
846              * make any contribution to metadata negotation, so they must have
847              * been explicitly requested by name. 
848              */
849             if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
850                 r->handler = exinfo->handler;
851                 if (conf->multimatch & MULTIMATCH_HANDLERS) {
852                     found = 1;
853                 }
854             }
855             /* XXX Two significant problems; 1, we don't check to see if we are
856              * setting redundant filters.    2, we insert these in the types config
857              * hook, which may be too early (dunno.)
858              */
859             if (exinfo->input_filters && r->proxyreq == PROXYREQ_NONE) {
860                 const char *filter, *filters = exinfo->input_filters;
861                 while (*filters 
862                     && (filter = ap_getword(r->pool, &filters, ';'))) {
863                     ap_add_input_filter(filter, NULL, r, r->connection);
864                 }
865                 if (conf->multimatch & MULTIMATCH_FILTERS) {
866                     found = 1;
867                 }
868             }
869             if (exinfo->output_filters && r->proxyreq == PROXYREQ_NONE) {
870                 const char *filter, *filters = exinfo->output_filters;
871                 while (*filters 
872                     && (filter = ap_getword(r->pool, &filters, ';'))) {
873                     ap_add_output_filter(filter, NULL, r, r->connection);
874                 }
875                 if (conf->multimatch & MULTIMATCH_FILTERS) {
876                     found = 1;
877                 }
878             }
879         }
880
881         if (found || (conf->multimatch & MULTIMATCH_ANY)) {
882             found_metadata = 1;
883         }
884         else {
885             *((const char **) apr_array_push(exception_list)) = ext;
886         }
887     }
888
889     /*
890      * Need to set a notes entry on r for unrecognized elements.
891      * Somebody better claim them!  If we did absolutely nothing,
892      * skip the notes to alert mod_negotiation we are clueless.
893      */
894     if (found_metadata) {
895         apr_table_setn(r->notes, "ap-mime-exceptions-list", 
896                        (void *)exception_list);
897     }
898
899     if (r->content_type) {
900         content_type *ctp;
901         int override = 0;
902
903         if ((ctp = analyze_ct(r, r->content_type))) {
904             param *pp = ctp->param;
905             char *base_content_type = apr_palloc(r->pool, ctp->type_len +
906                                                  ctp->subtype_len +
907                                                  sizeof("/"));
908             char *tmp = base_content_type;
909             memcpy(tmp, ctp->type, ctp->type_len);
910             tmp += ctp->type_len;
911             *tmp++ = '/';
912             memcpy(tmp, ctp->subtype, ctp->subtype_len);
913             tmp += ctp->subtype_len;
914             *tmp = 0;
915             ap_set_content_type(r, base_content_type);
916             while (pp != NULL) {
917                 if (charset && !strcmp(pp->attr, "charset")) {
918                     if (!override) {
919                         ap_set_content_type(r,
920                                             apr_pstrcat(r->pool,
921                                                         r->content_type,
922                                                         "; charset=",
923                                                         charset,
924                                                         NULL));
925                         override = 1;
926                     }
927                 }
928                 else {
929                     ap_set_content_type(r,
930                                         apr_pstrcat(r->pool,
931                                                     r->content_type,
932                                                     "; ", pp->attr,
933                                                     "=", pp->val,
934                                                     NULL));
935                 }
936                 pp = pp->next;
937             }
938             if (charset && !override) {
939                 ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
940                                                    "; charset=", charset,
941                                                    NULL));
942             }
943         }
944     }
945
946     /* Set default language, if none was specified by the extensions
947      * and we have a DefaultLanguage setting in force
948      */
949
950     if (!r->content_languages && conf->default_language) {
951         const char **new;
952
953         if (!r->content_languages) {
954             r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
955         }
956         new = (const char **)apr_array_push(r->content_languages);
957         *new = conf->default_language;
958     }
959
960     if (!r->content_type) {
961         return DECLINED;
962     }
963
964     return OK;
965 }
966
967 static void register_hooks(apr_pool_t *p)
968 {
969     ap_hook_post_config(mime_post_config,NULL,NULL,APR_HOOK_MIDDLE);
970     ap_hook_type_checker(find_ct,NULL,NULL,APR_HOOK_MIDDLE);
971     /* 
972      * this hook seems redundant ... is there any reason a type checker isn't
973      * allowed to do this already?  I'd think that fixups in general would be
974      * the last opportunity to get the filters right.
975      * ap_hook_insert_filter(mime_insert_filters,NULL,NULL,APR_HOOK_MIDDLE);
976      */
977 }
978
979 module AP_MODULE_DECLARE_DATA mime_module = {
980     STANDARD20_MODULE_STUFF,
981     create_mime_dir_config,     /* create per-directory config structure */
982     merge_mime_dir_configs,     /* merge per-directory config structures */
983     NULL,                       /* create per-server config structure */
984     NULL,                       /* merge per-server config structures */
985     mime_cmds,                  /* command apr_table_t */
986     register_hooks              /* register hooks */
987 };