Initial commit of the HEAD branch of the ELinks CVS repository, as of
[elinks/images.git] / src / mime / mime.c
blobcb56c93f6c362730cae8d0db158a1cc3de33315c
1 /* Functionality for handling mime types */
2 /* $Id: mime.c,v 1.72 2005/06/28 19:38:51 jonas Exp $ */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <string.h>
10 #include "elinks.h"
12 #include "cache/cache.h"
13 #include "config/options.h"
14 #include "encoding/encoding.h"
15 #include "intl/gettext/libintl.h"
16 #include "main/module.h"
17 #include "mime/backend/common.h"
18 #include "mime/mime.h"
19 #include "protocol/header.h" /* For parse_header() */
20 #include "protocol/uri.h"
21 #include "util/conv.h"
22 #include "util/file.h"
23 #include "util/memory.h"
24 #include "util/string.h"
27 enum mime_options {
28 MIME_TREE,
29 MIME_DEFAULT_TYPE,
31 MIME_OPTIONS,
34 static struct option_info mime_options[] = {
35 INIT_OPT_TREE("", N_("MIME"),
36 "mime", OPT_SORT,
37 N_("MIME-related options (handlers of various MIME types).")),
39 INIT_OPT_STRING("mime", N_("Default MIME-type"),
40 "default_type", 0, DEFAULT_MIME_TYPE,
41 N_("Document MIME-type to assume by default (when we are unable to\n"
42 "guess it properly from known information about the document).")),
44 NULL_OPTION_INFO,
47 #define get_opt_mime(which) mime_options[(which)].option
48 #define get_default_mime_type() get_opt_mime(MIME_DEFAULT_TYPE).value.string
50 /* Checks protocols headers for a suitable filename */
51 static unsigned char *
52 get_content_filename(struct uri *uri, struct cache_entry *cached)
54 unsigned char *filename, *pos;
56 if (!cached) cached = find_in_cache(uri);
58 if (!cached || !cached->head)
59 return NULL;
61 pos = parse_header(cached->head, "Content-Disposition", NULL);
62 if (!pos) return NULL;
64 filename = parse_header_param(pos, "filename");
65 mem_free(pos);
66 if (!filename) return NULL;
68 /* Remove start and ending quotes. */
69 if (filename[0] == '"') {
70 int len = strlen(filename);
72 if (len > 1 && filename[len - 1] == '"') {
73 filename[len - 1] = 0;
74 memmove(filename, filename + 1, len);
77 /* It was an empty quotation: "" */
78 if (!filename[1]) {
79 mem_free(filename);
80 return NULL;
84 /* We don't want to add any directories from the path so make sure we
85 * only add the filename. */
86 pos = get_filename_position(filename);
87 if (!*pos) {
88 mem_free(filename);
89 return NULL;
92 if (pos > filename)
93 memmove(filename, pos, strlen(pos) + 1);
95 return filename;
98 /* Checks if application/x-<extension> has any handlers. */
99 static inline unsigned char *
100 check_extension_type(unsigned char *extension)
102 /* Trim the extension so only last .<extension> is used. */
103 unsigned char *trimmed = strrchr(extension, '.');
104 struct mime_handler *handler;
105 unsigned char *content_type;
107 if (!trimmed)
108 return NULL;
110 content_type = straconcat("application/x-", trimmed + 1, NULL);
111 if (!content_type)
112 return NULL;
114 handler = get_mime_type_handler(content_type, 1);
115 if (handler) {
116 mem_free(handler);
117 return content_type;
120 mem_free(content_type);
121 return NULL;
124 /* Check if part of the extension coresponds to a supported encoding and if it
125 * has any handlers. */
126 static inline unsigned char *
127 check_encoding_type(unsigned char *extension)
129 enum stream_encoding encoding = guess_encoding(extension);
130 unsigned char **extension_list;
131 unsigned char *last_extension = strrchr(extension, '.');
133 if (encoding == ENCODING_NONE || !last_extension)
134 return NULL;
136 for (extension_list = listext_encoded(encoding);
137 extension_list && *extension_list;
138 extension_list++) {
139 unsigned char *content_type;
141 if (strcmp(*extension_list, last_extension))
142 continue;
144 *last_extension = '\0';
145 content_type = get_content_type_backends(extension);
146 *last_extension = '.';
148 return content_type;
151 return NULL;
154 #if 0
155 #define DEBUG_CONTENT_TYPE
156 #endif
158 #ifdef DEBUG_CONTENT_TYPE
159 #define debug_get_content_type_params(cached) \
160 DBG("get_content_type(head, url)\n=== head ===\n%s\n=== url ===\n%s\n", (cached)->head, struri((cached)->uri))
161 #define debug_ctype(ctype__) DBG("ctype= %s", (ctype__))
162 #define debug_extension(extension__) DBG("extension= %s", (extension__))
163 #else
164 #define debug_get_content_type_params(cached)
165 #define debug_ctype(ctype__)
166 #define debug_extension(extension__)
167 #endif
169 unsigned char *
170 get_extension_content_type(unsigned char *extension)
172 unsigned char *ctype;
174 assert(extension && *extension);
176 ctype = get_content_type_backends(extension);
177 debug_ctype(ctype);
178 if (ctype) return ctype;
180 ctype = check_encoding_type(extension);
181 debug_ctype(ctype);
182 if (ctype) return ctype;
184 ctype = check_extension_type(extension);
185 debug_ctype(ctype);
186 return ctype;
189 unsigned char *
190 get_cache_header_content_type(struct cache_entry *cached)
192 unsigned char *extension, *ctype;
194 ctype = parse_header(cached->head, "Content-Type", NULL);
195 if (ctype) {
196 unsigned char *end = strchr(ctype, ';');
197 int ctypelen;
199 if (end) *end = '\0';
201 ctypelen = strlen(ctype);
202 while (ctypelen && ctype[--ctypelen] <= ' ')
203 ctype[ctypelen] = '\0';
205 debug_ctype(ctype);
207 if (*ctype) {
208 return ctype;
211 mem_free(ctype);
214 /* This searches cached->head for filename so put here */
215 extension = get_content_filename(cached->uri, cached);
216 debug_extension(extension);
217 if (extension) {
218 ctype = get_extension_content_type(extension);
219 mem_free(extension);
220 if (ctype) {
221 return ctype;
225 return NULL;
228 unsigned char *
229 get_content_type(struct cache_entry *cached)
231 unsigned char *extension, *ctype;
233 debug_get_content_type_params(cached);
235 if (cached->content_type)
236 return cached->content_type;
238 /* If there's one in header, it's simple.. */
239 if (cached->head) {
240 ctype = get_cache_header_content_type(cached);
241 if (ctype && *ctype) {
242 cached->content_type = ctype;
243 return ctype;
245 mem_free_if(ctype);
248 /* We can't use the extension string we are getting below, because we
249 * want to support also things like "ps.gz" - that'd never work, as we
250 * would always compare only to "gz". */
251 /* Guess type accordingly to the extension */
252 extension = get_extension_from_uri(cached->uri);
253 debug_extension(extension);
255 if (extension) {
256 /* XXX: A little hack for making extension handling case
257 * insensitive. We could probably do it better by making
258 * guess_encoding() case independent the real problem however
259 * is with default (via option system) and mimetypes resolving
260 * doing that option and hash lookup will not be easy to
261 * convert. --jonas */
262 convert_to_lowercase(extension, strlen(extension));
264 ctype = get_extension_content_type(extension);
265 mem_free(extension);
266 if (ctype && *ctype) {
267 cached->content_type = ctype;
268 return ctype;
270 mem_free_if(ctype);
273 debug_ctype(get_default_mime_type());
275 /* Fallback.. use some hardwired default */
276 cached->content_type = stracpy(get_default_mime_type());
278 return cached->content_type;
281 struct mime_handler *
282 get_mime_type_handler(unsigned char *content_type, int xwin)
284 return get_mime_handler_backends(content_type, xwin);
287 struct string *
288 add_mime_filename_to_string(struct string *string, struct uri *uri)
290 unsigned char *filename = get_content_filename(uri, NULL);
292 assert(uri->data);
294 if (filename) {
295 add_shell_safe_to_string(string, filename, strlen(filename));
296 mem_free(filename);
298 return string;
301 return add_uri_to_string(string, uri, URI_FILENAME);
304 /* Backends dynamic area: */
306 #include "mime/backend/default.h"
307 #include "mime/backend/mailcap.h"
308 #include "mime/backend/mimetypes.h"
310 static struct module *mime_submodules[] = {
311 &default_mime_module,
312 #ifdef CONFIG_MAILCAP
313 &mailcap_mime_module,
314 #endif
315 #ifdef CONFIG_MIMETYPES
316 &mimetypes_mime_module,
317 #endif
318 NULL,
321 struct module mime_module = struct_module(
322 /* name: */ N_("MIME"),
323 /* options: */ mime_options,
324 /* hooks: */ NULL,
325 /* submodules: */ mime_submodules,
326 /* data: */ NULL,
327 /* init: */ NULL,
328 /* done: */ NULL