1
0
mirror of https://github.com/rkd77/elinks.git synced 2025-01-03 14:57:44 -05:00
elinks/src/mime/mime.c
Jonas Fonseca a236608554 Do not set the content type for FSP files
Simplify commit 8d4f44f2f1, in particular
detecting MIME types for files. It is more consistent to do it the way
it was already done by the session/download code.

Instead, write a NUL byte to stderr when getting FSP files and only set
cache->content_type when the header string is non-empty.

Additionally it also moves close(stderr) after the fsp_error() in the
file handling part of do_fsp() so the error message is shown with the
correct type.
2006-01-29 14:27:14 +01:00

329 lines
7.5 KiB
C

/* Functionality for handling mime types */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "elinks.h"
#include "cache/cache.h"
#include "config/options.h"
#include "encoding/encoding.h"
#include "intl/gettext/libintl.h"
#include "main/module.h"
#include "mime/backend/common.h"
#include "mime/mime.h"
#include "protocol/header.h" /* For parse_header() */
#include "protocol/uri.h"
#include "util/conv.h"
#include "util/file.h"
#include "util/memory.h"
#include "util/string.h"
enum mime_options {
MIME_TREE,
MIME_DEFAULT_TYPE,
MIME_OPTIONS,
};
static struct option_info mime_options[] = {
INIT_OPT_TREE("", N_("MIME"),
"mime", OPT_SORT,
N_("MIME-related options (handlers of various MIME types).")),
INIT_OPT_STRING("mime", N_("Default MIME-type"),
"default_type", 0, DEFAULT_MIME_TYPE,
N_("Document MIME-type to assume by default (when we are unable to\n"
"guess it properly from known information about the document).")),
NULL_OPTION_INFO,
};
#define get_opt_mime(which) mime_options[(which)].option
#define get_default_mime_type() get_opt_mime(MIME_DEFAULT_TYPE).value.string
/* Checks protocols headers for a suitable filename */
static unsigned char *
get_content_filename(struct uri *uri, struct cache_entry *cached)
{
unsigned char *filename, *pos;
if (!cached) cached = find_in_cache(uri);
if (!cached || !cached->head)
return NULL;
pos = parse_header(cached->head, "Content-Disposition", NULL);
if (!pos) return NULL;
filename = parse_header_param(pos, "filename");
mem_free(pos);
if (!filename) return NULL;
/* Remove start and ending quotes. */
if (filename[0] == '"') {
int len = strlen(filename);
if (len > 1 && filename[len - 1] == '"') {
filename[len - 1] = 0;
memmove(filename, filename + 1, len);
}
/* It was an empty quotation: "" */
if (!filename[1]) {
mem_free(filename);
return NULL;
}
}
/* We don't want to add any directories from the path so make sure we
* only add the filename. */
pos = get_filename_position(filename);
if (!*pos) {
mem_free(filename);
return NULL;
}
if (pos > filename)
memmove(filename, pos, strlen(pos) + 1);
return filename;
}
/* Checks if application/x-<extension> has any handlers. */
static inline unsigned char *
check_extension_type(unsigned char *extension)
{
/* Trim the extension so only last .<extension> is used. */
unsigned char *trimmed = strrchr(extension, '.');
struct mime_handler *handler;
unsigned char *content_type;
if (!trimmed)
return NULL;
content_type = straconcat("application/x-", trimmed + 1, NULL);
if (!content_type)
return NULL;
handler = get_mime_type_handler(content_type, 1);
if (handler) {
mem_free(handler);
return content_type;
}
mem_free(content_type);
return NULL;
}
/* Check if part of the extension coresponds to a supported encoding and if it
* has any handlers. */
static inline unsigned char *
check_encoding_type(unsigned char *extension)
{
enum stream_encoding encoding = guess_encoding(extension);
unsigned char **extension_list;
unsigned char *last_extension = strrchr(extension, '.');
if (encoding == ENCODING_NONE || !last_extension)
return NULL;
for (extension_list = listext_encoded(encoding);
extension_list && *extension_list;
extension_list++) {
unsigned char *content_type;
if (strcmp(*extension_list, last_extension))
continue;
*last_extension = '\0';
content_type = get_content_type_backends(extension);
*last_extension = '.';
return content_type;
}
return NULL;
}
#if 0
#define DEBUG_CONTENT_TYPE
#endif
#ifdef DEBUG_CONTENT_TYPE
#define debug_get_content_type_params(cached) \
DBG("get_content_type(head, url)\n=== head ===\n%s\n=== url ===\n%s\n", (cached)->head, struri((cached)->uri))
#define debug_ctype(ctype__) DBG("ctype= %s", (ctype__))
#define debug_extension(extension__) DBG("extension= %s", (extension__))
#else
#define debug_get_content_type_params(cached)
#define debug_ctype(ctype__)
#define debug_extension(extension__)
#endif
unsigned char *
get_extension_content_type(unsigned char *extension)
{
unsigned char *ctype;
assert(extension && *extension);
ctype = get_content_type_backends(extension);
debug_ctype(ctype);
if (ctype) return ctype;
ctype = check_encoding_type(extension);
debug_ctype(ctype);
if (ctype) return ctype;
ctype = check_extension_type(extension);
debug_ctype(ctype);
return ctype;
}
unsigned char *
get_cache_header_content_type(struct cache_entry *cached)
{
unsigned char *extension, *ctype;
ctype = parse_header(cached->head, "Content-Type", NULL);
if (ctype) {
unsigned char *end = strchr(ctype, ';');
int ctypelen;
if (end) *end = '\0';
ctypelen = strlen(ctype);
while (ctypelen && ctype[--ctypelen] <= ' ')
ctype[ctypelen] = '\0';
debug_ctype(ctype);
if (*ctype) {
return ctype;
}
mem_free(ctype);
}
/* This searches cached->head for filename so put here */
extension = get_content_filename(cached->uri, cached);
debug_extension(extension);
if (extension) {
ctype = get_extension_content_type(extension);
mem_free(extension);
if (ctype) {
return ctype;
}
}
return NULL;
}
unsigned char *
get_content_type(struct cache_entry *cached)
{
unsigned char *extension, *ctype;
debug_get_content_type_params(cached);
if (cached->content_type)
return cached->content_type;
/* If there's one in header, it's simple.. */
if (cached->head) {
ctype = get_cache_header_content_type(cached);
if (ctype && *ctype) {
cached->content_type = ctype;
return ctype;
}
mem_free_if(ctype);
}
/* We can't use the extension string we are getting below, because we
* want to support also things like "ps.gz" - that'd never work, as we
* would always compare only to "gz". */
/* Guess type accordingly to the extension */
extension = get_extension_from_uri(cached->uri);
debug_extension(extension);
if (extension) {
/* XXX: A little hack for making extension handling case
* insensitive. We could probably do it better by making
* guess_encoding() case independent the real problem however
* is with default (via option system) and mimetypes resolving
* doing that option and hash lookup will not be easy to
* convert. --jonas */
convert_to_lowercase(extension, strlen(extension));
ctype = get_extension_content_type(extension);
mem_free(extension);
if (ctype && *ctype) {
cached->content_type = ctype;
return ctype;
}
mem_free_if(ctype);
}
debug_ctype(get_default_mime_type());
/* Fallback.. use some hardwired default */
cached->content_type = stracpy(get_default_mime_type());
return cached->content_type;
}
struct mime_handler *
get_mime_type_handler(unsigned char *content_type, int xwin)
{
return get_mime_handler_backends(content_type, xwin);
}
struct string *
add_mime_filename_to_string(struct string *string, struct uri *uri)
{
unsigned char *filename = get_content_filename(uri, NULL);
assert(uri->data);
if (filename) {
add_shell_safe_to_string(string, filename, strlen(filename));
mem_free(filename);
return string;
}
return add_uri_to_string(string, uri, URI_FILENAME);
}
/* Backends dynamic area: */
#include "mime/backend/default.h"
#include "mime/backend/mailcap.h"
#include "mime/backend/mimetypes.h"
static struct module *mime_submodules[] = {
&default_mime_module,
#ifdef CONFIG_MAILCAP
&mailcap_mime_module,
#endif
#ifdef CONFIG_MIMETYPES
&mimetypes_mime_module,
#endif
NULL,
};
struct module mime_module = struct_module(
/* name: */ N_("MIME"),
/* options: */ mime_options,
/* hooks: */ NULL,
/* submodules: */ mime_submodules,
/* data: */ NULL,
/* init: */ NULL,
/* done: */ NULL
);