in modules/http/mod_mime.c [811:1083]
static int find_ct(request_rec *r)
{
mime_dir_config *conf;
apr_array_header_t *exception_list;
char *ext;
const char *fn, *fntmp, *type, *charset = NULL, *resource_name, *qm;
int found_metadata = 0;
if (r->finfo.filetype == APR_DIR) {
ap_set_content_type_ex(r, DIR_MAGIC_TYPE, 1);
return OK;
}
if (!r->filename) {
return DECLINED;
}
conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
&mime_module);
if (conf->nomime == NOMIME_ON) {
return DECLINED;
}
exception_list = apr_array_make(r->pool, 2, sizeof(char *));
/* If use_path_info is explicitly set to on (value & 1 == 1), append. */
if (conf->use_path_info & 1) {
resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
}
/*
* In the reverse proxy case r->filename might contain a query string if
* the nocanon option was used with ProxyPass.
* If this is the case cut off the query string as the last parameter in
* this query string might end up on an extension we take care about, but
* we only want to match against path components not against query
* parameters.
*/
else if ((r->proxyreq == PROXYREQ_REVERSE)
&& (apr_table_get(r->notes, "proxy-nocanon"))
&& ((qm = ap_strchr_c(r->filename, '?')) != NULL)) {
resource_name = apr_pstrmemdup(r->pool, r->filename, qm - r->filename);
}
else {
resource_name = r->filename;
}
/* Always drop the path leading up to the file name.
*/
if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
fn = resource_name;
}
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).
* Leading dots are considered to be part of the base name (a file named
* ".png" is likely not a png file but just a hidden file called png).
*/
fntmp = fn;
while (*fntmp == '.')
fntmp++;
fntmp = ap_strchr_c(fntmp, '.');
if (fntmp) {
ext = apr_pstrmemdup(r->pool, fn, fntmp - fn);
fn = fntmp + 1;
}
else {
ext = apr_pstrdup(r->pool, fn);
fn += strlen(fn);
}
*((const char **)apr_array_push(exception_list)) = ext;
/* Parse filename extensions which can be in any order
*/
while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
const extension_info *exinfo = NULL;
int found;
char *extcase;
int skipct = (conf->ct_last_ext == CT_LAST_ON) && (*fn);
int skipall = (conf->all_last_ext == ALL_LAST_ON) && (*fn);
if (*ext == '\0') { /* ignore empty extensions "bad..html" */
continue;
}
if (skipall) {
continue;
}
found = 0;
/* Save the ext in extcase before converting it to lower case.
*/
extcase = apr_pstrdup(r->pool, ext);
ap_str_tolower(ext);
if (conf->extension_mappings != NULL) {
exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
ext, APR_HASH_KEY_STRING);
}
if ((exinfo == NULL || !exinfo->forced_type) && !skipct) {
if ((type = apr_hash_get(mime_type_extensions, ext,
APR_HASH_KEY_STRING)) != NULL) {
ap_set_content_type_ex(r, (char*) type, 1);
found = 1;
}
}
if (exinfo != NULL) {
/* empty string is treated as special case for RemoveType */
if ((exinfo->forced_type && *exinfo->forced_type) && !skipct) {
ap_set_content_type_ex(r, exinfo->forced_type, 1);
found = 1;
}
if (exinfo->charset_type) {
charset = exinfo->charset_type;
found = 1;
}
if (exinfo->language_type) {
if (!r->content_languages) {
r->content_languages = apr_array_make(r->pool, 2,
sizeof(char *));
}
*((const char **)apr_array_push(r->content_languages))
= exinfo->language_type;
found = 1;
}
if (exinfo->encoding_type) {
if (!r->content_encoding) {
r->content_encoding = exinfo->encoding_type;
}
else {
/* XXX should eliminate duplicate entities
*
* ah no. Order is important and double encoding is neither
* forbidden nor impossible. -- nd
*/
r->content_encoding = apr_pstrcat(r->pool,
r->content_encoding,
", ",
exinfo->encoding_type,
NULL);
}
found = 1;
}
/* The following extensions are not 'Found'. That is, they don't
* make any contribution to metadata negotiation, so they must have
* been explicitly requested by name.
*/
if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
r->handler = exinfo->handler;
if (conf->multimatch & MULTIMATCH_HANDLERS) {
found = 1;
}
}
/* XXX Two significant problems; 1, we don't check to see if we are
* setting redundant filters. 2, we insert these in the types
* config hook, which may be too early (dunno.)
*/
if (exinfo->input_filters) {
const char *filter, *filters = exinfo->input_filters;
while (*filters
&& (filter = ap_getword(r->pool, &filters, ';'))) {
ap_add_input_filter(filter, NULL, r, r->connection);
}
if (conf->multimatch & MULTIMATCH_FILTERS) {
found = 1;
}
}
if (exinfo->output_filters) {
const char *filter, *filters = exinfo->output_filters;
while (*filters
&& (filter = ap_getword(r->pool, &filters, ';'))) {
ap_add_output_filter(filter, NULL, r, r->connection);
}
if (conf->multimatch & MULTIMATCH_FILTERS) {
found = 1;
}
}
}
if (found || (conf->multimatch & MULTIMATCH_ANY)) {
found_metadata = 1;
}
else {
*((const char **) apr_array_push(exception_list)) = extcase;
}
}
/*
* Need to set a notes entry on r for unrecognized elements.
* Somebody better claim them! If we did absolutely nothing,
* skip the notes to alert mod_negotiation we are clueless.
*/
if (found_metadata) {
apr_table_setn(r->notes, "ap-mime-exceptions-list",
(void *)exception_list);
}
if (r->content_type) {
content_type *ctp;
int override = 0;
if ((ctp = analyze_ct(r, r->content_type))) {
param *pp = ctp->param;
char *base_content_type = apr_palloc(r->pool, ctp->type_len +
ctp->subtype_len +
sizeof("/"));
char *tmp = base_content_type;
memcpy(tmp, ctp->type, ctp->type_len);
tmp += ctp->type_len;
*tmp++ = '/';
memcpy(tmp, ctp->subtype, ctp->subtype_len);
tmp += ctp->subtype_len;
*tmp = 0;
ap_set_content_type_ex(r, base_content_type, AP_REQUEST_IS_TRUSTED_CT(r));
while (pp != NULL) {
if (charset && !strcmp(pp->attr, "charset")) {
if (!override) {
ap_set_content_type_ex(r,
apr_pstrcat(r->pool,
r->content_type,
"; charset=",
charset,
NULL), AP_REQUEST_IS_TRUSTED_CT(r));
override = 1;
}
}
else {
ap_set_content_type_ex(r,
apr_pstrcat(r->pool,
r->content_type,
"; ", pp->attr,
"=", pp->val,
NULL), AP_REQUEST_IS_TRUSTED_CT(r));
}
pp = pp->next;
}
if (charset && !override) {
ap_set_content_type_ex(r, apr_pstrcat(r->pool, r->content_type,
"; charset=", charset,
NULL), AP_REQUEST_IS_TRUSTED_CT(r));
}
}
}
/* 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_languages = apr_array_make(r->pool, 2, sizeof(char *));
new = (const char **)apr_array_push(r->content_languages);
*new = conf->default_language;
}
if (!r->content_type) {
return DECLINED;
}
return OK;
}