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
create_mime_dir_config(apr_pool_t * p,char * dummy)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 */
overlay_extension_mappings(apr_pool_t * p,const void * key,apr_ssize_t klen,const void * overlay_val,const void * base_val,const void * data)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 const extension_info *overlay_info = (const extension_info *)overlay_val;
144 const extension_info *base_info = (const extension_info *)base_val;
145 extension_info *new_info = apr_pmemdup(p, base_info, sizeof(extension_info));
146
147 if (overlay_info->forced_type) {
148 new_info->forced_type = overlay_info->forced_type;
149 }
150 if (overlay_info->encoding_type) {
151 new_info->encoding_type = overlay_info->encoding_type;
152 }
153 if (overlay_info->language_type) {
154 new_info->language_type = overlay_info->language_type;
155 }
156 if (overlay_info->handler) {
157 new_info->handler = overlay_info->handler;
158 }
159 if (overlay_info->charset_type) {
160 new_info->charset_type = overlay_info->charset_type;
161 }
162 if (overlay_info->input_filters) {
163 new_info->input_filters = overlay_info->input_filters;
164 }
165 if (overlay_info->output_filters) {
166 new_info->output_filters = overlay_info->output_filters;
167 }
168
169 return new_info;
170 }
171
172 /* Member is the offset within an extension_info of the pointer to reset
173 */
remove_items(apr_pool_t * p,apr_array_header_t * remove,apr_hash_t * mappings)174 static void remove_items(apr_pool_t *p, apr_array_header_t *remove,
175 apr_hash_t *mappings)
176 {
177 attrib_info *suffix = (attrib_info *) remove->elts;
178 int i;
179 for (i = 0; i < remove->nelts; i++) {
180 extension_info *exinfo = apr_hash_get(mappings,
181 suffix[i].name,
182 APR_HASH_KEY_STRING);
183 if (exinfo && *(const char**)((char *)exinfo + suffix[i].offset)) {
184 extension_info *copyinfo = exinfo;
185 exinfo = apr_pmemdup(p, copyinfo, sizeof(*exinfo));
186 apr_hash_set(mappings, suffix[i].name,
187 APR_HASH_KEY_STRING, exinfo);
188
189 *(const char**)((char *)exinfo + suffix[i].offset) = NULL;
190 }
191 }
192 }
193
merge_mime_dir_configs(apr_pool_t * p,void * basev,void * addv)194 static void *merge_mime_dir_configs(apr_pool_t *p, void *basev, void *addv)
195 {
196 mime_dir_config *base = (mime_dir_config *)basev;
197 mime_dir_config *add = (mime_dir_config *)addv;
198 mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
199
200 if (base->extension_mappings && add->extension_mappings) {
201 new->extension_mappings = apr_hash_merge(p, add->extension_mappings,
202 base->extension_mappings,
203 overlay_extension_mappings,
204 NULL);
205 }
206 else {
207 if (base->extension_mappings == NULL) {
208 new->extension_mappings = add->extension_mappings;
209 }
210 else {
211 new->extension_mappings = base->extension_mappings;
212 }
213 /* We may not be merging the tables, but if we potentially will change
214 * an exinfo member, then we are about to trounce it anyways.
215 * We must have a copy for safety.
216 */
217 if (new->extension_mappings && add->remove_mappings) {
218 new->extension_mappings =
219 apr_hash_copy(p, new->extension_mappings);
220 }
221 }
222
223 if (new->extension_mappings) {
224 if (add->remove_mappings)
225 remove_items(p, add->remove_mappings, new->extension_mappings);
226 }
227 new->remove_mappings = NULL;
228
229 new->default_language = add->default_language ?
230 add->default_language : base->default_language;
231
232 new->multimatch = (add->multimatch != MULTIMATCH_UNSET) ?
233 add->multimatch : base->multimatch;
234
235 if ((add->use_path_info & 2) == 0) {
236 new->use_path_info = add->use_path_info;
237 }
238 else {
239 new->use_path_info = base->use_path_info;
240 }
241
242 return new;
243 }
244
add_extension_info(cmd_parms * cmd,void * m_,const char * value_,const char * ext)245 static const char *add_extension_info(cmd_parms *cmd, void *m_,
246 const char *value_, const char* ext)
247 {
248 mime_dir_config *m=m_;
249 extension_info *exinfo;
250 int offset = (int) (long) cmd->info;
251 char *key = apr_pstrdup(cmd->temp_pool, ext);
252 char *value = apr_pstrdup(cmd->pool, value_);
253 ap_str_tolower(value);
254 ap_str_tolower(key);
255
256 if (*key == '.') {
257 ++key;
258 }
259 if (!m->extension_mappings) {
260 m->extension_mappings = apr_hash_make(cmd->pool);
261 exinfo = NULL;
262 }
263 else {
264 exinfo = (extension_info*)apr_hash_get(m->extension_mappings, key,
265 APR_HASH_KEY_STRING);
266 }
267 if (!exinfo) {
268 exinfo = apr_pcalloc(cmd->pool, sizeof(extension_info));
269 key = apr_pstrdup(cmd->pool, key);
270 apr_hash_set(m->extension_mappings, key, APR_HASH_KEY_STRING, exinfo);
271 }
272 *(const char**)((char *)exinfo + offset) = value;
273 return NULL;
274 }
275
276 /*
277 * As RemoveType should also override the info from TypesConfig, we add an
278 * empty string as type instead of actually removing the type.
279 */
remove_extension_type(cmd_parms * cmd,void * m_,const char * ext)280 static const char *remove_extension_type(cmd_parms *cmd, void *m_,
281 const char *ext)
282 {
283 return add_extension_info(cmd, m_, "", ext);
284 }
285
286 /*
287 * Note handler names are un-added with each per_dir_config merge.
288 * This keeps the association from being inherited, but not
289 * from being re-added at a subordinate level.
290 */
remove_extension_info(cmd_parms * cmd,void * m_,const char * ext)291 static const char *remove_extension_info(cmd_parms *cmd, void *m_,
292 const char *ext)
293 {
294 mime_dir_config *m = (mime_dir_config *) m_;
295 attrib_info *suffix;
296 if (*ext == '.') {
297 ++ext;
298 }
299 if (!m->remove_mappings) {
300 m->remove_mappings = apr_array_make(cmd->pool, 4, sizeof(*suffix));
301 }
302 suffix = (attrib_info *)apr_array_push(m->remove_mappings);
303 suffix->name = apr_pstrdup(cmd->pool, ext);
304 ap_str_tolower(suffix->name);
305 suffix->offset = (int) (long) cmd->info;
306 return NULL;
307 }
308
309 /* The sole bit of server configuration that the MIME module has is
310 * the name of its config file, so...
311 */
312
set_types_config(cmd_parms * cmd,void * dummy,const char * arg)313 static const char *set_types_config(cmd_parms *cmd, void *dummy,
314 const char *arg)
315 {
316 ap_set_module_config(cmd->server->module_config, &mime_module,
317 (void *)arg);
318 return NULL;
319 }
320
multiviews_match(cmd_parms * cmd,void * m_,const char * include)321 static const char *multiviews_match(cmd_parms *cmd, void *m_,
322 const char *include)
323 {
324 mime_dir_config *m = (mime_dir_config *) m_;
325 const char *errmsg;
326
327 errmsg = ap_check_cmd_context(cmd, NOT_IN_LOCATION);
328 if (errmsg != NULL) {
329 return errmsg;
330 }
331
332 if (strcasecmp(include, "Any") == 0) {
333 if (m->multimatch && (m->multimatch & ~MULTIMATCH_ANY)) {
334 return "Any is incompatible with NegotiatedOnly, "
335 "Filters and Handlers";
336 }
337 m->multimatch |= MULTIMATCH_ANY;
338 }
339 else if (strcasecmp(include, "NegotiatedOnly") == 0) {
340 if (m->multimatch && (m->multimatch & ~MULTIMATCH_NEGOTIATED)) {
341 return "NegotiatedOnly is incompatible with Any, "
342 "Filters and Handlers";
343 }
344 m->multimatch |= MULTIMATCH_NEGOTIATED;
345 }
346 else if (strcasecmp(include, "Filters") == 0) {
347 if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
348 | MULTIMATCH_ANY))) {
349 return "Filters is incompatible with Any and NegotiatedOnly";
350 }
351 m->multimatch |= MULTIMATCH_FILTERS;
352 }
353 else if (strcasecmp(include, "Handlers") == 0) {
354 if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
355 | MULTIMATCH_ANY))) {
356 return "Handlers is incompatible with Any and NegotiatedOnly";
357 }
358 m->multimatch |= MULTIMATCH_HANDLERS;
359 }
360 else {
361 return apr_psprintf(cmd->pool, "Unrecognized option '%s'", include);
362 }
363
364 return NULL;
365 }
366
367 static const command_rec mime_cmds[] =
368 {
369 AP_INIT_ITERATE2("AddCharset", add_extension_info,
370 (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
371 "a charset (e.g., iso-2022-jp), followed by one or more "
372 "file extensions"),
373 AP_INIT_ITERATE2("AddEncoding", add_extension_info,
374 (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
375 "an encoding (e.g., gzip), followed by one or more file extensions"),
376 AP_INIT_ITERATE2("AddHandler", add_extension_info,
377 (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
378 "a handler name followed by one or more file extensions"),
379 AP_INIT_ITERATE2("AddInputFilter", add_extension_info,
380 (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
381 "input filter name (or ; delimited names) followed by one or "
382 "more file extensions"),
383 AP_INIT_ITERATE2("AddLanguage", add_extension_info,
384 (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
385 "a language (e.g., fr), followed by one or more file extensions"),
386 AP_INIT_ITERATE2("AddOutputFilter", add_extension_info,
387 (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
388 "output filter name (or ; delimited names) followed by one or "
389 "more file extensions"),
390 AP_INIT_ITERATE2("AddType", add_extension_info,
391 (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
392 "a mime type followed by one or more file extensions"),
393 AP_INIT_TAKE1("DefaultLanguage", ap_set_string_slot,
394 (void*)APR_OFFSETOF(mime_dir_config, default_language), OR_FILEINFO,
395 "language to use for documents with no other language file extension"),
396 AP_INIT_ITERATE("MultiviewsMatch", multiviews_match, NULL, OR_FILEINFO,
397 "NegotiatedOnly (default), Handlers and/or Filters, or Any"),
398 AP_INIT_ITERATE("RemoveCharset", remove_extension_info,
399 (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
400 "one or more file extensions"),
401 AP_INIT_ITERATE("RemoveEncoding", remove_extension_info,
402 (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
403 "one or more file extensions"),
404 AP_INIT_ITERATE("RemoveHandler", remove_extension_info,
405 (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
406 "one or more file extensions"),
407 AP_INIT_ITERATE("RemoveInputFilter", remove_extension_info,
408 (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
409 "one or more file extensions"),
410 AP_INIT_ITERATE("RemoveLanguage", remove_extension_info,
411 (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
412 "one or more file extensions"),
413 AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info,
414 (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
415 "one or more file extensions"),
416 AP_INIT_ITERATE("RemoveType", remove_extension_type,
417 (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
418 "one or more file extensions"),
419 AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
420 "the MIME types config file"),
421 AP_INIT_FLAG("ModMimeUsePathInfo", ap_set_flag_slot,
422 (void *)APR_OFFSETOF(mime_dir_config, use_path_info), ACCESS_CONF,
423 "Set to 'yes' to allow mod_mime to use path info for type checking"),
424 {NULL}
425 };
426
427 static apr_hash_t *mime_type_extensions;
428
mime_post_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)429 static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
430 {
431 ap_configfile_t *f;
432 char l[MAX_STRING_LEN];
433 const char *types_confname = ap_get_module_config(s->module_config,
434 &mime_module);
435 apr_status_t status;
436
437 if (!types_confname) {
438 types_confname = AP_TYPES_CONFIG_FILE;
439 }
440
441 types_confname = ap_server_root_relative(p, types_confname);
442 if (!types_confname) {
443 ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s, APLOGNO(01596)
444 "Invalid mime types config path %s",
445 (const char *)ap_get_module_config(s->module_config,
446 &mime_module));
447 return HTTP_INTERNAL_SERVER_ERROR;
448 }
449 if ((status = ap_pcfg_openfile(&f, ptemp, types_confname))
450 != APR_SUCCESS) {
451 ap_log_error(APLOG_MARK, APLOG_ERR, status, s, APLOGNO(01597)
452 "could not open mime types config file %s.",
453 types_confname);
454 return HTTP_INTERNAL_SERVER_ERROR;
455 }
456
457 mime_type_extensions = apr_hash_make(p);
458
459 while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
460 const char *ll = l, *ct;
461
462 if (l[0] == '#') {
463 continue;
464 }
465 ct = ap_getword_conf(p, &ll);
466
467 while (ll[0]) {
468 char *ext = ap_getword_conf(p, &ll);
469 ap_str_tolower(ext);
470 apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
471 }
472 }
473 ap_cfg_closefile(f);
474 return OK;
475 }
476
zap_sp(const char * s)477 static const char *zap_sp(const char *s)
478 {
479 if (s == NULL) {
480 return (NULL);
481 }
482 if (*s == '\0') {
483 return (s);
484 }
485
486 /* skip prefixed white space */
487 for (; *s == ' ' || *s == '\t' || *s == '\n'; s++)
488 ;
489
490 return (s);
491 }
492
zap_sp_and_dup(apr_pool_t * p,const char * start,const char * end,apr_size_t * len)493 static char *zap_sp_and_dup(apr_pool_t *p, const char *start,
494 const char *end, apr_size_t *len)
495 {
496 while ((start < end) && apr_isspace(*start)) {
497 start++;
498 }
499 while ((end > start) && apr_isspace(*(end - 1))) {
500 end--;
501 }
502 if (len) {
503 *len = end - start;
504 }
505 return apr_pstrmemdup(p, start, end - start);
506 }
507
is_token(char c)508 static int is_token(char c)
509 {
510 int res;
511
512 res = (apr_isascii(c) && apr_isgraph(c)
513 && (strchr(tspecial, c) == NULL)) ? 1 : -1;
514 return res;
515 }
516
is_qtext(char c)517 static int is_qtext(char c)
518 {
519 int res;
520
521 res = (apr_isascii(c) && (c != '"') && (c != '\\') && (c != '\n'))
522 ? 1 : -1;
523 return res;
524 }
525
is_quoted_pair(const char * s)526 static int is_quoted_pair(const char *s)
527 {
528 int res = -1;
529 int c;
530
531 if (((s + 1) != NULL) && (*s == '\\')) {
532 c = (int) *(s + 1);
533 if (apr_isascii(c)) {
534 res = 1;
535 }
536 }
537 return (res);
538 }
539
analyze_ct(request_rec * r,const char * s)540 static content_type *analyze_ct(request_rec *r, const char *s)
541 {
542 const char *cp, *mp;
543 char *attribute, *value;
544 int quoted = 0;
545 server_rec * ss = r->server;
546 apr_pool_t * p = r->pool;
547
548 content_type *ctp;
549 param *pp, *npp;
550
551 /* initialize ctp */
552 ctp = (content_type *)apr_palloc(p, sizeof(content_type));
553 ctp->type = NULL;
554 ctp->subtype = NULL;
555 ctp->param = NULL;
556
557 mp = s;
558
559 /* getting a type */
560 cp = mp;
561 while (apr_isspace(*cp)) {
562 cp++;
563 }
564 if (!*cp) {
565 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01598)
566 "mod_mime: analyze_ct: cannot get media type from '%s'",
567 (const char *) mp);
568 return (NULL);
569 }
570 ctp->type = cp;
571 do {
572 cp++;
573 } while (*cp && (*cp != '/') && !apr_isspace(*cp) && (*cp != ';'));
574 if (!*cp || (*cp == ';')) {
575 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01599)
576 "Cannot get media type from '%s'",
577 (const char *) mp);
578 return (NULL);
579 }
580 while (apr_isspace(*cp)) {
581 cp++;
582 }
583 if (*cp != '/') {
584 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01600)
585 "mod_mime: analyze_ct: cannot get media type from '%s'",
586 (const char *) mp);
587 return (NULL);
588 }
589 ctp->type_len = cp - ctp->type;
590
591 cp++; /* skip the '/' */
592
593 /* getting a subtype */
594 while (apr_isspace(*cp)) {
595 cp++;
596 }
597 if (!*cp) {
598 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01601)
599 "Cannot get media subtype.");
600 return (NULL);
601 }
602 ctp->subtype = cp;
603 do {
604 cp++;
605 } while (*cp && !apr_isspace(*cp) && (*cp != ';'));
606 ctp->subtype_len = cp - ctp->subtype;
607 while (apr_isspace(*cp)) {
608 cp++;
609 }
610
611 if (*cp == '\0') {
612 return (ctp);
613 }
614
615 /* getting parameters */
616 cp++; /* skip the ';' */
617 cp = zap_sp(cp);
618 if (cp == NULL || *cp == '\0') {
619 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01602)
620 "Cannot get media parameter.");
621 return (NULL);
622 }
623 mp = cp;
624 attribute = NULL;
625 value = NULL;
626
627 while (cp != NULL && *cp != '\0') {
628 if (attribute == NULL) {
629 if (is_token(*cp) > 0) {
630 cp++;
631 continue;
632 }
633 else if (*cp == ' ' || *cp == '\t' || *cp == '\n') {
634 cp++;
635 continue;
636 }
637 else if (*cp == '=') {
638 attribute = zap_sp_and_dup(p, mp, cp, NULL);
639 if (attribute == NULL || *attribute == '\0') {
640 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01603)
641 "Cannot get media parameter.");
642 return (NULL);
643 }
644 cp++;
645 cp = zap_sp(cp);
646 if (cp == NULL || *cp == '\0') {
647 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01604)
648 "Cannot get media parameter.");
649 return (NULL);
650 }
651 mp = cp;
652 continue;
653 }
654 else {
655 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01605)
656 "Cannot get media parameter.");
657 return (NULL);
658 }
659 }
660 else {
661 if (mp == cp) {
662 if (*cp == '"') {
663 quoted = 1;
664 cp++;
665 }
666 else {
667 quoted = 0;
668 }
669 }
670 if (quoted > 0) {
671 while (quoted && *cp != '\0') {
672 if (is_qtext(*cp) > 0) {
673 cp++;
674 }
675 else if (is_quoted_pair(cp) > 0) {
676 cp += 2;
677 }
678 else if (*cp == '"') {
679 cp++;
680 while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
681 cp++;
682 }
683 if (*cp != ';' && *cp != '\0') {
684 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01606)
685 "Cannot get media parameter.");
686 return(NULL);
687 }
688 quoted = 0;
689 }
690 else {
691 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01607)
692 "Cannot get media parameter.");
693 return (NULL);
694 }
695 }
696 }
697 else {
698 while (1) {
699 if (is_token(*cp) > 0) {
700 cp++;
701 }
702 else if (*cp == '\0' || *cp == ';') {
703 break;
704 }
705 else {
706 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01608)
707 "Cannot get media parameter.");
708 return (NULL);
709 }
710 }
711 }
712 value = zap_sp_and_dup(p, mp, cp, NULL);
713 if (value == NULL || *value == '\0') {
714 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss, APLOGNO(01609)
715 "Cannot get media parameter.");
716 return (NULL);
717 }
718
719 pp = apr_palloc(p, sizeof(param));
720 pp->attr = attribute;
721 pp->val = value;
722 pp->next = NULL;
723
724 if (ctp->param == NULL) {
725 ctp->param = pp;
726 }
727 else {
728 npp = ctp->param;
729 while (npp->next) {
730 npp = npp->next;
731 }
732 npp->next = pp;
733 }
734 quoted = 0;
735 attribute = NULL;
736 value = NULL;
737 if (*cp == '\0') {
738 break;
739 }
740 cp++;
741 mp = cp;
742 }
743 }
744 return (ctp);
745 }
746
747 /*
748 * find_ct is the hook routine for determining content-type and other
749 * MIME-related metadata. It assumes that r->filename has already been
750 * set and stat has been called for r->finfo. It also assumes that the
751 * non-path base file name is not the empty string unless it is a dir.
752 */
find_ct(request_rec * r)753 static int find_ct(request_rec *r)
754 {
755 mime_dir_config *conf;
756 apr_array_header_t *exception_list;
757 char *ext;
758 const char *fn, *fntmp, *type, *charset = NULL, *resource_name;
759 int found_metadata = 0;
760
761 if (r->finfo.filetype == APR_DIR) {
762 ap_set_content_type(r, DIR_MAGIC_TYPE);
763 return OK;
764 }
765
766 if (!r->filename) {
767 return DECLINED;
768 }
769
770 conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
771 &mime_module);
772 exception_list = apr_array_make(r->pool, 2, sizeof(char *));
773
774 /* If use_path_info is explicitly set to on (value & 1 == 1), append. */
775 if (conf->use_path_info & 1) {
776 resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
777 }
778 else {
779 resource_name = r->filename;
780 }
781
782 /* Always drop the path leading up to the file name.
783 */
784 if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
785 fn = resource_name;
786 }
787 else {
788 ++fn;
789 }
790
791
792 /* The exception list keeps track of those filename components that
793 * are not associated with extensions indicating metadata.
794 * The base name is always the first exception (i.e., "txt.html" has
795 * a basename of "txt" even though it might look like an extension).
796 * Leading dots are considered to be part of the base name (a file named
797 * ".png" is likely not a png file but just a hidden file called png).
798 */
799 fntmp = fn;
800 while (*fntmp == '.')
801 fntmp++;
802 fntmp = ap_strchr_c(fntmp, '.');
803 if (fntmp) {
804 ext = apr_pstrmemdup(r->pool, fn, fntmp - fn);
805 fn = fntmp + 1;
806 }
807 else {
808 ext = apr_pstrdup(r->pool, fn);
809 fn += strlen(fn);
810 }
811
812 *((const char **)apr_array_push(exception_list)) = ext;
813
814 /* Parse filename extensions which can be in any order
815 */
816 while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
817 const extension_info *exinfo = NULL;
818 int found;
819 char *extcase;
820
821 if (*ext == '\0') { /* ignore empty extensions "bad..html" */
822 continue;
823 }
824
825 found = 0;
826
827 /* Save the ext in extcase before converting it to lower case.
828 */
829 extcase = apr_pstrdup(r->pool, ext);
830 ap_str_tolower(ext);
831
832 if (conf->extension_mappings != NULL) {
833 exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
834 ext, APR_HASH_KEY_STRING);
835 }
836
837 if (exinfo == NULL || !exinfo->forced_type) {
838 if ((type = apr_hash_get(mime_type_extensions, ext,
839 APR_HASH_KEY_STRING)) != NULL) {
840 ap_set_content_type(r, (char*) type);
841 found = 1;
842 }
843 }
844
845 if (exinfo != NULL) {
846
847 /* empty string is treated as special case for RemoveType */
848 if (exinfo->forced_type && *exinfo->forced_type) {
849 ap_set_content_type(r, exinfo->forced_type);
850 found = 1;
851 }
852
853 if (exinfo->charset_type) {
854 charset = exinfo->charset_type;
855 found = 1;
856 }
857 if (exinfo->language_type) {
858 if (!r->content_languages) {
859 r->content_languages = apr_array_make(r->pool, 2,
860 sizeof(char *));
861 }
862 *((const char **)apr_array_push(r->content_languages))
863 = exinfo->language_type;
864 found = 1;
865 }
866 if (exinfo->encoding_type) {
867 if (!r->content_encoding) {
868 r->content_encoding = exinfo->encoding_type;
869 }
870 else {
871 /* XXX should eliminate duplicate entities
872 *
873 * ah no. Order is important and double encoding is neither
874 * forbidden nor impossible. -- nd
875 */
876 r->content_encoding = apr_pstrcat(r->pool,
877 r->content_encoding,
878 ", ",
879 exinfo->encoding_type,
880 NULL);
881 }
882 found = 1;
883 }
884 /* The following extensions are not 'Found'. That is, they don't
885 * make any contribution to metadata negotiation, so they must have
886 * been explicitly requested by name.
887 */
888 if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
889 r->handler = exinfo->handler;
890 if (conf->multimatch & MULTIMATCH_HANDLERS) {
891 found = 1;
892 }
893 }
894 /* XXX Two significant problems; 1, we don't check to see if we are
895 * setting redundant filters. 2, we insert these in the types
896 * config hook, which may be too early (dunno.)
897 */
898 if (exinfo->input_filters) {
899 const char *filter, *filters = exinfo->input_filters;
900 while (*filters
901 && (filter = ap_getword(r->pool, &filters, ';'))) {
902 ap_add_input_filter(filter, NULL, r, r->connection);
903 }
904 if (conf->multimatch & MULTIMATCH_FILTERS) {
905 found = 1;
906 }
907 }
908 if (exinfo->output_filters) {
909 const char *filter, *filters = exinfo->output_filters;
910 while (*filters
911 && (filter = ap_getword(r->pool, &filters, ';'))) {
912 ap_add_output_filter(filter, NULL, r, r->connection);
913 }
914 if (conf->multimatch & MULTIMATCH_FILTERS) {
915 found = 1;
916 }
917 }
918 }
919
920 if (found || (conf->multimatch & MULTIMATCH_ANY)) {
921 found_metadata = 1;
922 }
923 else {
924 *((const char **) apr_array_push(exception_list)) = extcase;
925 }
926 }
927
928 /*
929 * Need to set a notes entry on r for unrecognized elements.
930 * Somebody better claim them! If we did absolutely nothing,
931 * skip the notes to alert mod_negotiation we are clueless.
932 */
933 if (found_metadata) {
934 apr_table_setn(r->notes, "ap-mime-exceptions-list",
935 (void *)exception_list);
936 }
937
938 if (r->content_type) {
939 content_type *ctp;
940 int override = 0;
941
942 if ((ctp = analyze_ct(r, r->content_type))) {
943 param *pp = ctp->param;
944 char *base_content_type = apr_palloc(r->pool, ctp->type_len +
945 ctp->subtype_len +
946 sizeof("/"));
947 char *tmp = base_content_type;
948 memcpy(tmp, ctp->type, ctp->type_len);
949 tmp += ctp->type_len;
950 *tmp++ = '/';
951 memcpy(tmp, ctp->subtype, ctp->subtype_len);
952 tmp += ctp->subtype_len;
953 *tmp = 0;
954 ap_set_content_type(r, base_content_type);
955 while (pp != NULL) {
956 if (charset && !strcmp(pp->attr, "charset")) {
957 if (!override) {
958 ap_set_content_type(r,
959 apr_pstrcat(r->pool,
960 r->content_type,
961 "; charset=",
962 charset,
963 NULL));
964 override = 1;
965 }
966 }
967 else {
968 ap_set_content_type(r,
969 apr_pstrcat(r->pool,
970 r->content_type,
971 "; ", pp->attr,
972 "=", pp->val,
973 NULL));
974 }
975 pp = pp->next;
976 }
977 if (charset && !override) {
978 ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
979 "; charset=", charset,
980 NULL));
981 }
982 }
983 }
984
985 /* Set default language, if none was specified by the extensions
986 * and we have a DefaultLanguage setting in force
987 */
988
989 if (!r->content_languages && conf->default_language) {
990 const char **new;
991
992 if (!r->content_languages) {
993 r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
994 }
995 new = (const char **)apr_array_push(r->content_languages);
996 *new = conf->default_language;
997 }
998
999 if (!r->content_type) {
1000 return DECLINED;
1001 }
1002
1003 return OK;
1004 }
1005
register_hooks(apr_pool_t * p)1006 static void register_hooks(apr_pool_t *p)
1007 {
1008 ap_hook_post_config(mime_post_config,NULL,NULL,APR_HOOK_MIDDLE);
1009 ap_hook_type_checker(find_ct,NULL,NULL,APR_HOOK_MIDDLE);
1010 /*
1011 * this hook seems redundant ... is there any reason a type checker isn't
1012 * allowed to do this already? I'd think that fixups in general would be
1013 * the last opportunity to get the filters right.
1014 * ap_hook_insert_filter(mime_insert_filters,NULL,NULL,APR_HOOK_MIDDLE);
1015 */
1016 }
1017
1018 AP_DECLARE_MODULE(mime) = {
1019 STANDARD20_MODULE_STUFF,
1020 create_mime_dir_config, /* create per-directory config structure */
1021 merge_mime_dir_configs, /* merge per-directory config structures */
1022 NULL, /* create per-server config structure */
1023 NULL, /* merge per-server config structures */
1024 mime_cmds, /* command apr_table_t */
1025 register_hooks /* register hooks */
1026 };
1027