Compilation fix: _GNU_SOURCE has to be defined on GNU platforms to get
[elinks.git] / src / mime / mime.c
blob9af170821426dacdd27fd84dbafd5b662a4cc60b
1 /* Functionality for handling mime types */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
5 #endif
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <string.h>
13 #include "elinks.h"
15 #include "cache/cache.h"
16 #include "config/options.h"
17 #include "encoding/encoding.h"
18 #include "intl/gettext/libintl.h"
19 #include "main/module.h"
20 #include "mime/backend/common.h"
21 #include "mime/mime.h"
22 #include "protocol/header.h" /* For parse_header() */
23 #include "protocol/uri.h"
24 #include "util/conv.h"
25 #include "util/file.h"
26 #include "util/memory.h"
27 #include "util/string.h"
30 enum mime_options {
31 MIME_TREE,
32 MIME_DEFAULT_TYPE,
34 MIME_OPTIONS,
37 static struct option_info mime_options[] = {
38 INIT_OPT_TREE("", N_("MIME"),
39 "mime", OPT_SORT,
40 N_("MIME-related options (handlers of various MIME types).")),
42 INIT_OPT_STRING("mime", N_("Default MIME-type"),
43 "default_type", 0, DEFAULT_MIME_TYPE,
44 N_("Document MIME-type to assume by default (when we are unable to\n"
45 "guess it properly from known information about the document).")),
47 NULL_OPTION_INFO,
50 #define get_opt_mime(which) mime_options[(which)].option
51 #define get_default_mime_type() get_opt_mime(MIME_DEFAULT_TYPE).value.string
53 /* Checks protocols headers for a suitable filename */
54 static unsigned char *
55 get_content_filename(struct uri *uri, struct cache_entry *cached)
57 unsigned char *filename, *pos;
59 if (!cached) cached = find_in_cache(uri);
61 if (!cached || !cached->head)
62 return NULL;
64 pos = parse_header(cached->head, "Content-Disposition", NULL);
65 if (!pos) return NULL;
67 parse_header_param(pos, "filename", &filename);
68 mem_free(pos);
69 if (!filename) return NULL;
71 /* Remove start and ending quotes. */
72 if (filename[0] == '"') {
73 int len = strlen(filename);
75 if (len > 1 && filename[len - 1] == '"') {
76 filename[len - 1] = 0;
77 memmove(filename, filename + 1, len);
80 /* It was an empty quotation: "" */
81 if (!filename[1]) {
82 mem_free(filename);
83 return NULL;
87 /* We don't want to add any directories from the path so make sure we
88 * only add the filename. */
89 pos = get_filename_position(filename);
90 if (!*pos) {
91 mem_free(filename);
92 return NULL;
95 if (pos > filename)
96 memmove(filename, pos, strlen(pos) + 1);
98 return filename;
101 /* Checks if application/x-<extension> has any handlers. */
102 static inline unsigned char *
103 check_extension_type(unsigned char *extension)
105 /* Trim the extension so only last .<extension> is used. */
106 unsigned char *trimmed = strrchr(extension, '.');
107 struct mime_handler *handler;
108 unsigned char *content_type;
110 if (!trimmed)
111 return NULL;
113 content_type = straconcat("application/x-", trimmed + 1, NULL);
114 if (!content_type)
115 return NULL;
117 handler = get_mime_type_handler(content_type, 1);
118 if (handler) {
119 mem_free(handler);
120 return content_type;
123 mem_free(content_type);
124 return NULL;
127 /* Check if part of the extension coresponds to a supported encoding and if it
128 * has any handlers. */
129 static inline unsigned char *
130 check_encoding_type(unsigned char *extension)
132 enum stream_encoding encoding = guess_encoding(extension);
133 unsigned char **extension_list;
134 unsigned char *last_extension = strrchr(extension, '.');
136 if (encoding == ENCODING_NONE || !last_extension)
137 return NULL;
139 for (extension_list = listext_encoded(encoding);
140 extension_list && *extension_list;
141 extension_list++) {
142 unsigned char *content_type;
144 if (strcmp(*extension_list, last_extension))
145 continue;
147 *last_extension = '\0';
148 content_type = get_content_type_backends(extension);
149 *last_extension = '.';
151 return content_type;
154 return NULL;
157 #if 0
158 #define DEBUG_CONTENT_TYPE
159 #endif
161 #ifdef DEBUG_CONTENT_TYPE
162 #define debug_get_content_type_params(cached) \
163 DBG("get_content_type(head, url)\n=== head ===\n%s\n=== url ===\n%s\n", (cached)->head, struri((cached)->uri))
164 #define debug_ctype(ctype__) DBG("ctype= %s", (ctype__))
165 #define debug_extension(extension__) DBG("extension= %s", (extension__))
166 #else
167 #define debug_get_content_type_params(cached)
168 #define debug_ctype(ctype__)
169 #define debug_extension(extension__)
170 #endif
172 unsigned char *
173 get_extension_content_type(unsigned char *extension)
175 unsigned char *ctype;
177 assert(extension && *extension);
179 ctype = get_content_type_backends(extension);
180 debug_ctype(ctype);
181 if (ctype) return ctype;
183 ctype = check_encoding_type(extension);
184 debug_ctype(ctype);
185 if (ctype) return ctype;
187 ctype = check_extension_type(extension);
188 debug_ctype(ctype);
189 return ctype;
192 unsigned char *
193 get_cache_header_content_type(struct cache_entry *cached)
195 unsigned char *extension, *ctype;
197 ctype = parse_header(cached->head, "Content-Type", NULL);
198 if (ctype) {
199 unsigned char *end = strchr(ctype, ';');
200 int ctypelen;
202 if (end) *end = '\0';
204 ctypelen = strlen(ctype);
205 while (ctypelen && ctype[--ctypelen] <= ' ')
206 ctype[ctypelen] = '\0';
208 debug_ctype(ctype);
210 if (*ctype) {
211 return ctype;
214 mem_free(ctype);
217 /* This searches cached->head for filename so put here */
218 extension = get_content_filename(cached->uri, cached);
219 debug_extension(extension);
220 if (extension) {
221 ctype = get_extension_content_type(extension);
222 mem_free(extension);
223 if (ctype) {
224 return ctype;
228 return NULL;
231 unsigned char *
232 get_fragment_content_type(struct cache_entry *cached)
234 struct fragment *fragment;
235 size_t length;
236 unsigned char *sample;
237 unsigned char *ctype = NULL;
239 if (list_empty(cached->frag))
240 return NULL;
242 fragment = cached->frag.next;
243 if (fragment->offset)
244 return NULL;
246 length = fragment->length > 1024 ? 1024 : fragment->length;
247 sample = memacpy(fragment->data, length);
248 if (!sample)
249 return NULL;
251 if (strcasestr(sample, "<html>"))
252 ctype = stracpy("text/html");
254 mem_free(sample);
256 return ctype;
259 unsigned char *
260 get_content_type(struct cache_entry *cached)
262 unsigned char *extension, *ctype;
264 debug_get_content_type_params(cached);
266 if (cached->content_type)
267 return cached->content_type;
269 /* If there's one in header, it's simple.. */
270 if (cached->head) {
271 ctype = get_cache_header_content_type(cached);
272 if (ctype && *ctype) {
273 cached->content_type = ctype;
274 return ctype;
276 mem_free_if(ctype);
279 /* We can't use the extension string we are getting below, because we
280 * want to support also things like "ps.gz" - that'd never work, as we
281 * would always compare only to "gz". */
282 /* Guess type accordingly to the extension */
283 extension = get_extension_from_uri(cached->uri);
284 debug_extension(extension);
286 if (extension) {
287 /* XXX: A little hack for making extension handling case
288 * insensitive. We could probably do it better by making
289 * guess_encoding() case independent the real problem however
290 * is with default (via option system) and mimetypes resolving
291 * doing that option and hash lookup will not be easy to
292 * convert. --jonas */
293 convert_to_lowercase(extension, strlen(extension));
295 ctype = get_extension_content_type(extension);
296 mem_free(extension);
297 if (ctype && *ctype) {
298 cached->content_type = ctype;
299 return ctype;
301 mem_free_if(ctype);
304 ctype = get_fragment_content_type(cached);
305 if (ctype && *ctype) {
306 cached->content_type = ctype;
307 return ctype;
310 debug_ctype(get_default_mime_type());
312 /* Fallback.. use some hardwired default */
313 cached->content_type = stracpy(get_default_mime_type());
315 return cached->content_type;
318 struct mime_handler *
319 get_mime_type_handler(unsigned char *content_type, int xwin)
321 return get_mime_handler_backends(content_type, xwin);
324 struct string *
325 add_mime_filename_to_string(struct string *string, struct uri *uri)
327 unsigned char *filename = get_content_filename(uri, NULL);
329 assert(uri->data);
331 if (filename) {
332 add_shell_safe_to_string(string, filename, strlen(filename));
333 mem_free(filename);
335 return string;
338 return add_uri_to_string(string, uri, URI_FILENAME);
341 /* Backends dynamic area: */
343 #include "mime/backend/default.h"
344 #include "mime/backend/mailcap.h"
345 #include "mime/backend/mimetypes.h"
347 static struct module *mime_submodules[] = {
348 &default_mime_module,
349 #ifdef CONFIG_MAILCAP
350 &mailcap_mime_module,
351 #endif
352 #ifdef CONFIG_MIMETYPES
353 &mimetypes_mime_module,
354 #endif
355 NULL,
358 struct module mime_module = struct_module(
359 /* name: */ N_("MIME"),
360 /* options: */ mime_options,
361 /* hooks: */ NULL,
362 /* submodules: */ mime_submodules,
363 /* data: */ NULL,
364 /* init: */ NULL,
365 /* done: */ NULL