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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * http_mime.c: Sends/gets MIME headers for requests
25 #include "apr_strings.h"
29 #define APR_WANT_STRFUNC
32 #include "ap_config.h"
34 #include "http_config.h"
36 #include "http_request.h"
37 #include "http_protocol.h"
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.
44 * #define OS_ASC(c) (c) -- for mere mortals
46 * #define OS_ASC(c) (ebcdic2ascii[c]) -- for dino's
48 * #define apr_isascii(c) ((OS_ASC(c) & 0x80) == 0)
51 /* XXXXX - fix me - See note with NOT_PROXY
54 typedef struct attrib_info {
59 /* Information to which an extension can be mapped
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... */
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
78 apr_hash_t *extension_mappings; /* Map from extension name to
79 * extension_info structure */
81 apr_array_header_t *remove_mappings; /* A simple list, walked once */
83 char *default_language; /* Language if no AddLanguage ext found */
85 int multimatch; /* Extensions to include in multiview matching
86 * for filenames, e.g. Filters and Handlers
88 int use_path_info; /* If set to 0, only use filename.
89 * If set to 1, append PATH_INFO to filename for
91 * If set to 2, this value is unset and is
96 typedef struct param_s {
106 apr_size_t subtype_len;
110 static char tspecial[] = {
111 '(', ')', '<', '>', '@', ',', ';', ':',
112 '\\', '"', '/', '[', ']', '?', '=',
116 module AP_MODULE_DECLARE_DATA mime_module;
118 static void *create_mime_dir_config(apr_pool_t *p, char *dummy)
120 mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
122 new->extension_mappings = NULL;
123 new->remove_mappings = NULL;
125 new->default_language = NULL;
127 new->multimatch = MULTIMATCH_UNSET;
129 new->use_path_info = 2;
134 * Overlay one hash table of extension_mappings onto another
136 static void *overlay_extension_mappings(apr_pool_t *p,
139 const void *overlay_val,
140 const void *base_val,
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;
147 memcpy(new_info, base_info, sizeof(extension_info));
148 if (overlay_info->forced_type) {
149 new_info->forced_type = overlay_info->forced_type;
151 if (overlay_info->encoding_type) {
152 new_info->encoding_type = overlay_info->encoding_type;
154 if (overlay_info->language_type) {
155 new_info->language_type = overlay_info->language_type;
157 if (overlay_info->handler) {
158 new_info->handler = overlay_info->handler;
160 if (overlay_info->charset_type) {
161 new_info->charset_type = overlay_info->charset_type;
163 if (overlay_info->input_filters) {
164 new_info->input_filters = overlay_info->input_filters;
166 if (overlay_info->output_filters) {
167 new_info->output_filters = overlay_info->output_filters;
173 /* Member is the offset within an extension_info of the pointer to reset
175 static void remove_items(apr_pool_t *p, apr_array_header_t *remove,
176 apr_hash_t *mappings)
178 attrib_info *suffix = (attrib_info *) remove->elts;
180 for (i = 0; i < remove->nelts; i++) {
181 extension_info *exinfo = apr_hash_get(mappings,
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;
195 static void *merge_mime_dir_configs(apr_pool_t *p, void *basev, void *addv)
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));
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,
208 if (base->extension_mappings == NULL) {
209 new->extension_mappings = add->extension_mappings;
212 new->extension_mappings = base->extension_mappings;
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.
218 if (new->extension_mappings && add->remove_mappings) {
219 new->extension_mappings =
220 apr_hash_copy(p, new->extension_mappings);
224 if (new->extension_mappings) {
225 if (add->remove_mappings)
226 remove_items(p, add->remove_mappings, new->extension_mappings);
228 new->remove_mappings = NULL;
230 new->default_language = add->default_language ?
231 add->default_language : base->default_language;
233 new->multimatch = (add->multimatch != MULTIMATCH_UNSET) ?
234 add->multimatch : base->multimatch;
236 if ((add->use_path_info & 2) == 0) {
237 new->use_path_info = add->use_path_info;
240 new->use_path_info = base->use_path_info;
246 static const char *add_extension_info(cmd_parms *cmd, void *m_,
247 const char *value_, const char* ext)
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);
260 if (!m->extension_mappings) {
261 m->extension_mappings = apr_hash_make(cmd->pool);
265 exinfo = (extension_info*)apr_hash_get(m->extension_mappings, key,
266 APR_HASH_KEY_STRING);
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);
273 *(const char**)((char *)exinfo + offset) = value;
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.
282 static const char *remove_extension_info(cmd_parms *cmd, void *m_,
285 mime_dir_config *m = (mime_dir_config *) m_;
290 if (!m->remove_mappings) {
291 m->remove_mappings = apr_array_make(cmd->pool, 4, sizeof(*suffix));
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;
300 /* The sole bit of server configuration that the MIME module has is
301 * the name of its config file, so...
304 static const char *set_types_config(cmd_parms *cmd, void *dummy,
307 ap_set_module_config(cmd->server->module_config, &mime_module,
312 static const char *multiviews_match(cmd_parms *cmd, void *m_,
315 mime_dir_config *m = (mime_dir_config *) m_;
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";
322 m->multimatch |= MULTIMATCH_ANY;
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";
329 m->multimatch |= MULTIMATCH_NEGOTIATED;
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";
336 m->multimatch |= MULTIMATCH_FILTERS;
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";
343 m->multimatch |= MULTIMATCH_HANDLERS;
346 return "Unrecognized option";
352 static const command_rec mime_cmds[] =
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 "
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"),
412 static apr_hash_t *mime_type_extensions;
414 static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
417 char l[MAX_STRING_LEN];
418 const char *types_confname = ap_get_module_config(s->module_config,
422 if (!types_confname) {
423 types_confname = AP_TYPES_CONFIG_FILE;
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,
432 return HTTP_INTERNAL_SERVER_ERROR;
434 if ((status = ap_pcfg_openfile(&f, ptemp, types_confname))
436 ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
437 "could not open mime types config file %s.",
439 return HTTP_INTERNAL_SERVER_ERROR;
442 mime_type_extensions = apr_hash_make(p);
444 while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
445 const char *ll = l, *ct;
450 ct = ap_getword_conf(p, &ll);
453 char *ext = ap_getword_conf(p, &ll);
455 apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
462 static const char *zap_sp(const char *s)
471 /* skip prefixed white space */
472 for (; *s == ' ' || *s == '\t' || *s == '\n'; s++)
478 static char *zap_sp_and_dup(apr_pool_t *p, const char *start,
479 const char *end, apr_size_t *len)
481 while ((start < end) && apr_isspace(*start)) {
484 while ((end > start) && apr_isspace(*(end - 1))) {
490 return apr_pstrmemdup(p, start, end - start);
493 static int is_token(char c)
497 res = (apr_isascii(c) && apr_isgraph(c)
498 && (strchr(tspecial, c) == NULL)) ? 1 : -1;
502 static int is_qtext(char c)
506 res = (apr_isascii(c) && (c != '"') && (c != '\\') && (c != '\n'))
511 static int is_quoted_pair(const char *s)
516 if (((s + 1) != NULL) && (*s == '\\')) {
518 if (apr_isascii(c)) {
525 static content_type *analyze_ct(request_rec *r, const char *s)
528 char *attribute, *value;
530 server_rec * ss = r->server;
531 apr_pool_t * p = r->pool;
537 ctp = (content_type *)apr_palloc(p, sizeof(content_type));
546 while (apr_isspace(*cp)) {
550 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
551 "mod_mime: analyze_ct: cannot get media type from '%s'",
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'",
565 while (apr_isspace(*cp)) {
569 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
570 "mod_mime: analyze_ct: cannot get media type from '%s'",
574 ctp->type_len = cp - ctp->type;
576 cp++; /* skip the '/' */
578 /* getting a subtype */
579 while (apr_isspace(*cp)) {
583 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
584 "Cannot get media subtype.");
590 } while (*cp && !apr_isspace(*cp) && (*cp != ';'));
591 ctp->subtype_len = cp - ctp->subtype;
592 while (apr_isspace(*cp)) {
600 /* getting parameters */
601 cp++; /* skip the ';' */
603 if (cp == NULL || *cp == '\0') {
604 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
605 "Cannot get media parameter.");
612 while (cp != NULL && *cp != '\0') {
613 if (attribute == NULL) {
614 if (is_token(*cp) > 0) {
618 else if (*cp == ' ' || *cp == '\t' || *cp == '\n') {
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.");
631 if (cp == NULL || *cp == '\0') {
632 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
633 "Cannot get media parameter.");
640 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
641 "Cannot get media parameter.");
656 while (quoted && *cp != '\0') {
657 if (is_qtext(*cp) > 0) {
660 else if (is_quoted_pair(cp) > 0) {
663 else if (*cp == '"') {
665 while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
668 if (*cp != ';' && *cp != '\0') {
669 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
670 "Cannot get media parameter.");
676 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
677 "Cannot get media parameter.");
684 if (is_token(*cp) > 0) {
687 else if (*cp == '\0' || *cp == ';') {
691 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
692 "Cannot get media parameter.");
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.");
704 pp = apr_palloc(p, sizeof(param));
705 pp->attr = attribute;
709 if (ctp->param == NULL) {
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.
738 static int find_ct(request_rec *r)
740 mime_dir_config *conf;
741 apr_array_header_t *exception_list;
743 const char *fn, *type, *charset = NULL, *resource_name;
744 int found_metadata = 0;
746 if (r->finfo.filetype == APR_DIR) {
747 ap_set_content_type(r, DIR_MAGIC_TYPE);
755 conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
757 exception_list = apr_array_make(r->pool, 2, sizeof(char *));
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);
764 resource_name = r->filename;
767 /* Always drop the path leading up to the file name.
769 if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
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).
781 ext = ap_getword(r->pool, &fn, '.');
782 *((const char **)apr_array_push(exception_list)) = ext;
784 /* Parse filename extensions which can be in any order
786 while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
787 const extension_info *exinfo = NULL;
790 if (*ext == '\0') { /* ignore empty extensions "bad..html" */
798 if (conf->extension_mappings != NULL) {
799 exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
800 ext, APR_HASH_KEY_STRING);
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);
811 if (exinfo != NULL) {
813 if (exinfo->forced_type) {
814 ap_set_content_type(r, exinfo->forced_type);
818 if (exinfo->charset_type) {
819 charset = exinfo->charset_type;
822 if (exinfo->language_type) {
823 if (!r->content_languages) {
824 r->content_languages = apr_array_make(r->pool, 2,
827 *((const char **)apr_array_push(r->content_languages))
828 = exinfo->language_type;
831 if (exinfo->encoding_type) {
832 if (!r->content_encoding) {
833 r->content_encoding = exinfo->encoding_type;
836 /* XXX should eliminate duplicate entities */
837 r->content_encoding = apr_pstrcat(r->pool,
840 exinfo->encoding_type,
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.
849 if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
850 r->handler = exinfo->handler;
851 if (conf->multimatch & MULTIMATCH_HANDLERS) {
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.)
859 if (exinfo->input_filters && r->proxyreq == PROXYREQ_NONE) {
860 const char *filter, *filters = exinfo->input_filters;
862 && (filter = ap_getword(r->pool, &filters, ';'))) {
863 ap_add_input_filter(filter, NULL, r, r->connection);
865 if (conf->multimatch & MULTIMATCH_FILTERS) {
869 if (exinfo->output_filters && r->proxyreq == PROXYREQ_NONE) {
870 const char *filter, *filters = exinfo->output_filters;
872 && (filter = ap_getword(r->pool, &filters, ';'))) {
873 ap_add_output_filter(filter, NULL, r, r->connection);
875 if (conf->multimatch & MULTIMATCH_FILTERS) {
881 if (found || (conf->multimatch & MULTIMATCH_ANY)) {
885 *((const char **) apr_array_push(exception_list)) = ext;
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.
894 if (found_metadata) {
895 apr_table_setn(r->notes, "ap-mime-exceptions-list",
896 (void *)exception_list);
899 if (r->content_type) {
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 +
908 char *tmp = base_content_type;
909 memcpy(tmp, ctp->type, ctp->type_len);
910 tmp += ctp->type_len;
912 memcpy(tmp, ctp->subtype, ctp->subtype_len);
913 tmp += ctp->subtype_len;
915 ap_set_content_type(r, base_content_type);
917 if (charset && !strcmp(pp->attr, "charset")) {
919 ap_set_content_type(r,
929 ap_set_content_type(r,
938 if (charset && !override) {
939 ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
940 "; charset=", charset,
946 /* Set default language, if none was specified by the extensions
947 * and we have a DefaultLanguage setting in force
950 if (!r->content_languages && conf->default_language) {
953 if (!r->content_languages) {
954 r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
956 new = (const char **)apr_array_push(r->content_languages);
957 *new = conf->default_language;
960 if (!r->content_type) {
967 static void register_hooks(apr_pool_t *p)
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);
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);
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 */