diff options
author | Colm MacCarthaigh <colm@apache.org> | 2010-01-08 11:45:43 +0000 |
---|---|---|
committer | Colm MacCarthaigh <colm@apache.org> | 2010-01-08 11:45:43 +0000 |
commit | 396931c93e46f5d130f0df7044a9c0e63fd12ebd (patch) | |
tree | 9ff5247f604985caa9581ebe4bfa6aa5164db5ce /APACHE_1_3_42/src/modules/standard/mod_mime.c | |
parent | 7d344b579813528064a6711a91f675b7f47e4926 (diff) | |
download | httpd-1.3.tar.gz |
Tag 1.3.421.3
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/tags/1.3@897175 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'APACHE_1_3_42/src/modules/standard/mod_mime.c')
-rw-r--r-- | APACHE_1_3_42/src/modules/standard/mod_mime.c | 756 |
1 files changed, 756 insertions, 0 deletions
diff --git a/APACHE_1_3_42/src/modules/standard/mod_mime.c b/APACHE_1_3_42/src/modules/standard/mod_mime.c new file mode 100644 index 0000000000..b096466417 --- /dev/null +++ b/APACHE_1_3_42/src/modules/standard/mod_mime.c @@ -0,0 +1,756 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * http_mime.c: Sends/gets MIME headers for requests + * + * Rob McCool + * + */ + +#define MIME_PRIVATE + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" + +/* + * isascii(c) isn't universal, and even those places where it is + * defined it's not always right for our needs. Roll our own that + * we can rely on. + */ +#define ap_isascii(c) ((OS_ASC(c) & 0x80) == 0) + +typedef struct attrib_info { + char *name; +} attrib_info; + +typedef struct { + table *forced_types; /* Additional AddTyped stuff */ + table *encoding_types; /* Added with AddEncoding... */ + table *charset_types; /* Added with AddCharset... */ + table *language_types; /* Added with AddLanguage... */ + table *handlers; /* Added with AddHandler... */ + array_header *handlers_remove; /* List of handlers to remove */ + array_header *types_remove; /* List of MIME types to remove */ + array_header *encodings_remove; /* List of encodings to remove */ + + char *type; /* Type forced with ForceType */ + char *handler; /* Handler forced with SetHandler */ + char *default_language; /* Language if no AddLanguage ext found */ +} mime_dir_config; + +typedef struct param_s { + char *attr; + char *val; + struct param_s *next; +} param; + +typedef struct { + char *type; + char *subtype; + param *param; +} content_type; + +static char tspecial[] = { + '(', ')', '<', '>', '@', ',', ';', ':', + '\\', '"', '/', '[', ']', '?', '=', + '\0' +}; + +module MODULE_VAR_EXPORT mime_module; + +static void *create_mime_dir_config(pool *p, char *dummy) +{ + mime_dir_config *new = + (mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config)); + + new->forced_types = ap_make_table(p, 4); + new->encoding_types = ap_make_table(p, 4); + new->charset_types = ap_make_table(p, 4); + new->language_types = ap_make_table(p, 4); + new->handlers = ap_make_table(p, 4); + new->handlers_remove = ap_make_array(p, 4, sizeof(attrib_info)); + new->types_remove = ap_make_array(p, 4, sizeof(attrib_info)); + new->encodings_remove = ap_make_array(p, 4, sizeof(attrib_info)); + + new->type = NULL; + new->handler = NULL; + new->default_language = NULL; + + return new; +} + +static void *merge_mime_dir_configs(pool *p, void *basev, void *addv) +{ + mime_dir_config *base = (mime_dir_config *) basev; + mime_dir_config *add = (mime_dir_config *) addv; + mime_dir_config *new = + (mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config)); + int i; + attrib_info *suffix; + + new->forced_types = ap_overlay_tables(p, add->forced_types, + base->forced_types); + new->encoding_types = ap_overlay_tables(p, add->encoding_types, + base->encoding_types); + new->charset_types = ap_overlay_tables(p, add->charset_types, + base->charset_types); + new->language_types = ap_overlay_tables(p, add->language_types, + base->language_types); + new->handlers = ap_overlay_tables(p, add->handlers, + base->handlers); + + suffix = (attrib_info *) add->handlers_remove->elts; + for (i = 0; i < add->handlers_remove->nelts; i++) { + ap_table_unset(new->handlers, suffix[i].name); + } + suffix = (attrib_info *) add->types_remove->elts; + for (i = 0; i < add->types_remove->nelts; i++) { + ap_table_unset(new->forced_types, suffix[i].name); + } + suffix = (attrib_info *) add->encodings_remove->elts; + for (i = 0; i < add->encodings_remove->nelts; i++) { + ap_table_unset(new->encoding_types, suffix[i].name); + } + + new->type = add->type ? add->type : base->type; + new->handler = add->handler ? add->handler : base->handler; + new->default_language = add->default_language ? + add->default_language : base->default_language; + + return new; +} + +static const char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, + char *ext) +{ + if (*ext == '.') + ++ext; + + ap_str_tolower(ct); + ap_table_setn(m->forced_types, ext, ct); + return NULL; +} + +static const char *add_encoding(cmd_parms *cmd, mime_dir_config *m, char *enc, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(enc); + ap_table_setn(m->encoding_types, ext, enc); + return NULL; +} + +static const char *add_charset(cmd_parms *cmd, mime_dir_config *m, + char *charset, char *ext) +{ + if (*ext == '.') { + ++ext; + } + ap_str_tolower(charset); + ap_table_setn(m->charset_types, ext, charset); + return NULL; +} + +static const char *add_language(cmd_parms *cmd, mime_dir_config *m, char *lang, + char *ext) +{ + if (*ext == '.') { + ++ext; + } + ap_str_tolower(lang); + ap_table_setn(m->language_types, ext, lang); + return NULL; +} + +static const char *add_handler(cmd_parms *cmd, mime_dir_config *m, char *hdlr, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(hdlr); + ap_table_setn(m->handlers, ext, hdlr); + return NULL; +} + +/* + * Note handler names that should be un-added for this location. This + * will keep the association from being inherited, as well, but not + * from being re-added at a subordinate level. + */ +static const char *remove_handler(cmd_parms *cmd, void *m, char *ext) +{ + mime_dir_config *mcfg = (mime_dir_config *) m; + attrib_info *suffix; + + if (*ext == '.') { + ++ext; + } + suffix = (attrib_info *) ap_push_array(mcfg->handlers_remove); + suffix->name = ap_pstrdup(cmd->pool, ext); + return NULL; +} + +/* + * Just like the previous function, except that it records encoding + * associations to be undone. + */ +static const char *remove_encoding(cmd_parms *cmd, void *m, char *ext) +{ + mime_dir_config *mcfg = (mime_dir_config *) m; + attrib_info *suffix; + + if (*ext == '.') { + ++ext; + } + suffix = (attrib_info *) ap_push_array(mcfg->encodings_remove); + suffix->name = ap_pstrdup(cmd->pool, ext); + return NULL; +} + +/* + * Similar to the previous functions, except that it deals with filename + * suffix/MIME-type associations. + */ +static const char *remove_type(cmd_parms *cmd, void *m, char *ext) +{ + mime_dir_config *mcfg = (mime_dir_config *) m; + attrib_info *suffix; + + if (*ext == '.') { + ++ext; + } + suffix = (attrib_info *) ap_push_array(mcfg->types_remove); + suffix->name = ap_pstrdup(cmd->pool, ext); + return NULL; +} + +/* The sole bit of server configuration that the MIME module has is + * the name of its config file, so... + */ + +static const char *set_types_config(cmd_parms *cmd, void *dummy, char *arg) +{ + ap_set_module_config(cmd->server->module_config, &mime_module, arg); + return NULL; +} + +static const command_rec mime_cmds[] = +{ + {"AddType", add_type, NULL, OR_FILEINFO, ITERATE2, + "a mime type followed by one or more file extensions"}, + {"AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2, + "an encoding (e.g., gzip), followed by one or more file extensions"}, + {"AddCharset", add_charset, NULL, OR_FILEINFO, ITERATE2, + "a charset (e.g., iso-2022-jp), followed by one or more file extensions"}, + {"AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2, + "a language (e.g., fr), followed by one or more file extensions"}, + {"AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2, + "a handler name followed by one or more file extensions"}, + {"ForceType", ap_set_string_slot_lower, + (void *)XtOffsetOf(mime_dir_config, type), OR_FILEINFO, TAKE1, + "a media type"}, + {"RemoveHandler", remove_handler, NULL, OR_FILEINFO, ITERATE, + "one or more file extensions"}, + {"RemoveEncoding", remove_encoding, NULL, OR_FILEINFO, ITERATE, + "one or more file extensions"}, + {"RemoveType", remove_type, NULL, OR_FILEINFO, ITERATE, + "one or more file extensions"}, + {"SetHandler", ap_set_string_slot_lower, + (void *)XtOffsetOf(mime_dir_config, handler), OR_FILEINFO, TAKE1, + "a handler name"}, + {"TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1, + "the MIME types config file"}, + {"DefaultLanguage", ap_set_string_slot, + (void*)XtOffsetOf(mime_dir_config, default_language), OR_FILEINFO, TAKE1, + "language to use for documents with no other language file extension" }, + {NULL} +}; + +/* Hash table --- only one of these per daemon; virtual hosts can + * get private versions through AddType... + */ + +#define MIME_HASHSIZE (32) +#define hash(i) (ap_tolower(i) % MIME_HASHSIZE) + +static table *hash_buckets[MIME_HASHSIZE]; + +static void init_mime(server_rec *s, pool *p) +{ + configfile_t *f; + char l[MAX_STRING_LEN]; + int x; + char *types_confname = ap_get_module_config(s->module_config, &mime_module); + + if (!types_confname) + types_confname = TYPES_CONFIG_FILE; + + types_confname = ap_server_root_relative(p, types_confname); + + if (!(f = ap_pcfg_openfile(p, types_confname))) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "could not open mime types config file %s.", types_confname); + exit(1); + } + + for (x = 0; x < MIME_HASHSIZE; x++) + hash_buckets[x] = ap_make_table(p, 10); + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { + const char *ll = l, *ct; + + if (l[0] == '#') + continue; + ct = ap_getword_conf(p, &ll); + + while (ll[0]) { + char *ext = ap_getword_conf(p, &ll); + ap_str_tolower(ext); /* ??? */ + ap_table_setn(hash_buckets[hash(ext[0])], ext, ct); + } + } + ap_cfg_closefile(f); +} + +static char *zap_sp(char *s) +{ + char *tp; + + if (s == NULL) { + return (NULL); + } + if (*s == '\0') { + return (s); + } + + /* delete prefixed white space */ + for (; *s == ' ' || *s == '\t' || *s == '\n'; s++); + + /* delete postfixed white space */ + for (tp = s; *tp != '\0'; tp++); + for (tp--; tp != s && (*tp == ' ' || *tp == '\t' || *tp == '\n'); tp--) { + *tp = '\0'; + } + return (s); +} + +static int is_token(int c) +{ + int res; + + res = (ap_isascii(c) && ap_isgraph(c) + && (strchr(tspecial, c) == NULL)) ? 1 : -1; + return res; +} + +static int is_qtext(int c) +{ + int res; + + res = (ap_isascii(c) && (c != '"') && (c != '\\') && (c != '\n')) + ? 1 : -1; + return res; +} + +static int is_quoted_pair(char *s) +{ + int res = -1; + int c; + + if (((s + 1) != NULL) && (*s == '\\')) { + c = (int) *(s + 1); + if (ap_isascii(c)) { + res = 1; + } + } + return (res); +} + +static content_type *analyze_ct(pool *p, char *s) +{ + char *tp, *mp, *cp; + char *attribute, *value; + int quoted = 0; + + content_type *ctp; + param *pp, *npp; + + /* initialize ctp */ + ctp = (content_type *) ap_palloc(p, sizeof(content_type)); + ctp->type = NULL; + ctp->subtype = NULL; + ctp->param = NULL; + + tp = ap_pstrdup(p, s); + + mp = tp; + cp = mp; + + /* getting a type */ + if (!(cp = strchr(mp, '/'))) { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "mod_mime: analyze_ct: cannot get media type from '%s'", + mp); + return (NULL); + } + ctp->type = ap_pstrndup(p, mp, cp - mp); + ctp->type = zap_sp(ctp->type); + if (ctp->type == NULL || *(ctp->type) == '\0' || + strchr(ctp->type, ';') || strchr(ctp->type, ' ') || + strchr(ctp->type, '\t')) { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media subtype."); + return (NULL); + } + + /* getting a subtype */ + cp++; + mp = cp; + + for (; *cp != ';' && *cp != '\0'; cp++); + ctp->subtype = ap_pstrndup(p, mp, cp - mp); + ctp->subtype = zap_sp(ctp->subtype); + if ((ctp->subtype == NULL) || (*(ctp->subtype) == '\0') || + strchr(ctp->subtype, ' ') || strchr(ctp->subtype, '\t')) { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media subtype."); + return (NULL); + } + cp = zap_sp(cp); + if (cp == NULL || *cp == '\0') { + return (ctp); + } + + /* getting parameters */ + cp++; + cp = zap_sp(cp); + if (cp == NULL || *cp == '\0') { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + mp = cp; + attribute = NULL; + value = NULL; + + while (cp != NULL && *cp != '\0') { + if (attribute == NULL) { + if (is_token((int) *cp) > 0) { + cp++; + continue; + } + else if (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + continue; + } + else if (*cp == '=') { + attribute = ap_pstrndup(p, mp, cp - mp); + attribute = zap_sp(attribute); + if (attribute == NULL || *attribute == '\0') { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + cp++; + cp = zap_sp(cp); + if (cp == NULL || *cp == '\0') { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + mp = cp; + continue; + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + } + else { + if (mp == cp) { + if (*cp == '"') { + quoted = 1; + cp++; + } + else { + quoted = 0; + } + } + if (quoted > 0) { + while (quoted && *cp != '\0') { + if (is_qtext((int) *cp) > 0) { + cp++; + } + else if (is_quoted_pair(cp) > 0) { + cp += 2; + } + else if (*cp == '"') { + cp++; + while (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + } + if (*cp != ';' && *cp != '\0') { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return(NULL); + } + quoted = 0; + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + } + } + else { + while (1) { + if (is_token((int) *cp) > 0) { + cp++; + } + else if (*cp == '\0' || *cp == ';') { + break; + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + } + } + value = ap_pstrndup(p, mp, cp - mp); + value = zap_sp(value); + if (value == NULL || *value == '\0') { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "Cannot get media parameter."); + return (NULL); + } + + pp = ap_palloc(p, sizeof(param)); + pp->attr = attribute; + pp->val = value; + pp->next = NULL; + + if (ctp->param == NULL) { + ctp->param = pp; + } + else { + npp = ctp->param; + while (npp->next) { + npp = npp->next; + } + npp->next = pp; + } + quoted = 0; + attribute = NULL; + value = NULL; + if (*cp == '\0') { + break; + } + cp++; + mp = cp; + } + } + return (ctp); +} + +static int find_ct(request_rec *r) +{ + mime_dir_config *conf; + array_header *exception_list; + const char *fn; + char *ext; + const char *type; + const char *charset = NULL; + int found_metadata = 0; + + if (S_ISDIR(r->finfo.st_mode)) { + r->content_type = DIR_MAGIC_TYPE; + return OK; + } + + conf = (mime_dir_config *) ap_get_module_config(r->per_dir_config, + &mime_module); + + exception_list = ap_make_array(r->pool, 2, sizeof(char *)); + + /* Always drop the leading element */ + fn = strrchr(r->filename, '/'); + if (fn == NULL) + fn = r->filename; + else + ++fn; + + /* The exception list keeps track of those filename components that + * are not associated with extensions indicating metadata. + * The base name is always the first exception (i.e., "txt.html" has + * a basename of "txt" even though it might look like an extension). + */ + ext = ap_getword(r->pool, &fn, '.'); + *((const char **) ap_push_array(exception_list)) = ext; + + /* Parse filename extensions, which can be in any order */ + while ((ext = ap_getword(r->pool, &fn, '.')) && *ext) { + int found = 0; + + /* Check for Content-Type */ + if ((type = ap_table_get(conf->forced_types, ext)) + || (type = ap_table_get(hash_buckets[hash(*ext)], ext))) { + r->content_type = type; + found = 1; + } + + /* Add charset to Content-Type */ + if ((type = ap_table_get(conf->charset_types, ext))) { + charset = type; + found = 1; + } + + /* Check for Content-Language */ + if ((type = ap_table_get(conf->language_types, ext))) { + const char **new; + + r->content_language = type; /* back compat. only */ + if (!r->content_languages) + r->content_languages = ap_make_array(r->pool, 2, sizeof(char *)); + new = (const char **) ap_push_array(r->content_languages); + *new = type; + found = 1; + } + + /* Check for Content-Encoding */ + if ((type = ap_table_get(conf->encoding_types, ext))) { + if (!r->content_encoding) + r->content_encoding = type; + else + r->content_encoding = ap_pstrcat(r->pool, r->content_encoding, + ", ", type, NULL); + found = 1; + } + + /* Check for a special handler, but not for proxy request */ + if ((type = ap_table_get(conf->handlers, ext)) + && r->proxyreq == NOT_PROXY) { + r->handler = type; + found = 1; + } + + if (found) + found_metadata = 1; + else + *((const char **) ap_push_array(exception_list)) = ext; + } + + /* Need to see a notes entry on r for unrecognized elements. + * Somebody better claim them! If we did absolutly nothing, + * skip the notes to alert mod_negotiation we are clueless. + */ + if (found_metadata) { + ap_table_setn(r->notes, "ap-mime-exceptions-list", + (void *) exception_list); + } + + if (r->content_type) { + content_type *ctp; + char *ct; + int override = 0; + + ct = (char *) ap_palloc(r->pool, + sizeof(char) * (strlen(r->content_type) + 1)); + strcpy(ct, r->content_type); + + if ((ctp = analyze_ct(r->pool, ct))) { + param *pp = ctp->param; + r->content_type = ap_pstrcat(r->pool, ctp->type, "/", + ctp->subtype, NULL); + while (pp != NULL) { + if (charset && !strcmp(pp->attr, "charset")) { + if (!override) { + r->content_type = ap_pstrcat(r->pool, r->content_type, + "; charset=", charset, + NULL); + override = 1; + } + } + else { + r->content_type = ap_pstrcat(r->pool, r->content_type, + "; ", pp->attr, + "=", pp->val, + NULL); + } + pp = pp->next; + } + if (charset && !override) { + r->content_type = ap_pstrcat(r->pool, r->content_type, + "; charset=", charset, + NULL); + } + } + } + + /* Set default language, if none was specified by the extensions + * and we have a DefaultLanguage setting in force + */ + + if (!r->content_languages && conf->default_language) { + const char **new; + + r->content_language = conf->default_language; /* back compat. only */ + if (!r->content_languages) + r->content_languages = ap_make_array(r->pool, 2, sizeof(char *)); + new = (const char **) ap_push_array(r->content_languages); + *new = conf->default_language; + } + + /* Check for overrides with ForceType/SetHandler */ + + if (conf->type && strcmp(conf->type, "none")) + r->content_type = conf->type; + if (conf->handler && strcmp(conf->handler, "none")) + r->handler = conf->handler; + + if (!r->content_type) + return DECLINED; + + return OK; +} + +module MODULE_VAR_EXPORT mime_module = +{ + STANDARD_MODULE_STUFF, + init_mime, /* initializer */ + create_mime_dir_config, /* dir config creator */ + merge_mime_dir_configs, /* dir config merger */ + NULL, /* server config */ + NULL, /* merge server config */ + mime_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + find_ct, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; |