1 /* Functionality for handling mime types */
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
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"
37 static union option_info mime_options
[] = {
38 INIT_OPT_TREE("", N_("MIME"),
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 "
45 "(when we are unable to guess it properly "
46 "from known information about the document).")),
51 #define get_opt_mime(which) mime_options[(which)].option
52 #define get_default_mime_type() get_opt_mime(MIME_DEFAULT_TYPE).value.string
54 /* Checks protocols headers for a suitable filename */
55 static unsigned char *
56 get_content_filename(struct uri
*uri
, struct cache_entry
*cached
)
58 unsigned char *filename
, *pos
;
60 if (!cached
) cached
= find_in_cache(uri
);
62 if (!cached
|| !cached
->head
)
65 pos
= parse_header(cached
->head
, "Content-Disposition", NULL
);
66 if (!pos
) return NULL
;
68 parse_header_param(pos
, "filename", &filename
);
70 if (!filename
) return NULL
;
72 /* Remove start and ending quotes. */
73 if (filename
[0] == '"') {
74 int len
= strlen(filename
);
76 if (len
> 1 && filename
[len
- 1] == '"') {
77 filename
[len
- 1] = 0;
78 memmove(filename
, filename
+ 1, len
);
81 /* It was an empty quotation: "" */
88 /* We don't want to add any directories from the path so make sure we
89 * only add the filename. */
90 pos
= get_filename_position(filename
);
97 memmove(filename
, pos
, strlen(pos
) + 1);
102 /* Checks if application/x-<extension> has any handlers. */
103 static inline unsigned char *
104 check_extension_type(unsigned char *extension
)
106 /* Trim the extension so only last .<extension> is used. */
107 unsigned char *trimmed
= strrchr(extension
, '.');
108 struct mime_handler
*handler
;
109 unsigned char *content_type
;
114 content_type
= straconcat("application/x-", trimmed
+ 1,
115 (unsigned char *) NULL
);
119 handler
= get_mime_type_handler(content_type
, 1);
125 mem_free(content_type
);
129 /* Check if part of the extension coresponds to a supported encoding and if it
130 * has any handlers. */
131 static inline unsigned char *
132 check_encoding_type(unsigned char *extension
)
134 enum stream_encoding encoding
= guess_encoding(extension
);
135 const unsigned char *const *extension_list
;
136 unsigned char *last_extension
= strrchr(extension
, '.');
138 if (encoding
== ENCODING_NONE
|| !last_extension
)
141 for (extension_list
= listext_encoded(encoding
);
142 extension_list
&& *extension_list
;
144 unsigned char *content_type
;
146 if (strcmp(*extension_list
, last_extension
))
149 *last_extension
= '\0';
150 content_type
= get_content_type_backends(extension
);
151 *last_extension
= '.';
160 #define DEBUG_CONTENT_TYPE
163 #ifdef DEBUG_CONTENT_TYPE
164 #define debug_get_content_type_params(cached) \
165 DBG("get_content_type(head, url)\n=== head ===\n%s\n=== url ===\n%s\n", (cached)->head, struri((cached)->uri))
166 #define debug_ctype(ctype__) DBG("ctype= %s", (ctype__))
167 #define debug_extension(extension__) DBG("extension= %s", (extension__))
169 #define debug_get_content_type_params(cached)
170 #define debug_ctype(ctype__)
171 #define debug_extension(extension__)
175 get_extension_content_type(unsigned char *extension
)
177 unsigned char *ctype
;
179 assert(extension
&& *extension
);
181 ctype
= get_content_type_backends(extension
);
183 if (ctype
) return ctype
;
185 ctype
= check_encoding_type(extension
);
187 if (ctype
) return ctype
;
189 ctype
= check_extension_type(extension
);
195 get_cache_header_content_type(struct cache_entry
*cached
)
197 unsigned char *extension
, *ctype
;
199 ctype
= parse_header(cached
->head
, "Content-Type", NULL
);
201 unsigned char *end
= strchr(ctype
, ';');
204 if (end
) *end
= '\0';
206 ctypelen
= strlen(ctype
);
207 while (ctypelen
&& ctype
[--ctypelen
] <= ' ')
208 ctype
[ctypelen
] = '\0';
219 /* This searches cached->head for filename so put here */
220 extension
= get_content_filename(cached
->uri
, cached
);
221 debug_extension(extension
);
223 ctype
= get_extension_content_type(extension
);
233 static unsigned char *
234 get_fragment_content_type(struct cache_entry
*cached
)
236 struct fragment
*fragment
;
238 unsigned char *sample
;
239 unsigned char *ctype
= NULL
;
241 if (list_empty(cached
->frag
))
244 fragment
= cached
->frag
.next
;
245 if (fragment
->offset
)
248 length
= fragment
->length
> 1024 ? 1024 : fragment
->length
;
249 sample
= memacpy(fragment
->data
, length
);
253 if (c_strcasestr(sample
, "<html>"))
254 ctype
= stracpy("text/html");
262 get_content_type(struct cache_entry
*cached
)
264 unsigned char *extension
, *ctype
;
266 debug_get_content_type_params(cached
);
268 if (cached
->content_type
)
269 return cached
->content_type
;
271 /* If there's one in header, it's simple.. */
273 ctype
= get_cache_header_content_type(cached
);
274 if (ctype
&& *ctype
) {
275 cached
->content_type
= ctype
;
281 /* We can't use the extension string we are getting below, because we
282 * want to support also things like "ps.gz" - that'd never work, as we
283 * would always compare only to "gz". */
284 /* Guess type accordingly to the extension */
285 extension
= get_extension_from_uri(cached
->uri
);
286 debug_extension(extension
);
289 /* XXX: A little hack for making extension handling case
290 * insensitive. We could probably do it better by making
291 * guess_encoding() case independent the real problem however
292 * is with default (via option system) and mimetypes resolving
293 * doing that option and hash lookup will not be easy to
294 * convert. --jonas */
295 convert_to_lowercase_locale_indep(extension
, strlen(extension
));
297 ctype
= get_extension_content_type(extension
);
299 if (ctype
&& *ctype
) {
300 cached
->content_type
= ctype
;
306 ctype
= get_fragment_content_type(cached
);
307 if (ctype
&& *ctype
) {
308 cached
->content_type
= ctype
;
312 debug_ctype(get_default_mime_type());
314 /* text/plain for pager mode */
315 if (cached
->uri
&& cached
->uri
->string
316 && !strcmp(cached
->uri
->string
, "file:///dev/stdin")) {
317 cached
->content_type
= stracpy("text/plain");
319 /* Fallback.. use some hardwired default */
320 cached
->content_type
= stracpy(get_default_mime_type());
322 return cached
->content_type
;
325 struct mime_handler
*
326 get_mime_type_handler(unsigned char *content_type
, int xwin
)
328 return get_mime_handler_backends(content_type
, xwin
);
332 add_mime_filename_to_string(struct string
*string
, struct uri
*uri
)
334 unsigned char *filename
= get_content_filename(uri
, NULL
);
339 add_shell_safe_to_string(string
, filename
, strlen(filename
));
345 return add_uri_to_string(string
, uri
, URI_FILENAME
);
348 /* Backends dynamic area: */
350 #include "mime/backend/default.h"
351 #include "mime/backend/mailcap.h"
352 #include "mime/backend/mimetypes.h"
354 static struct module
*mime_submodules
[] = {
355 &default_mime_module
,
356 #ifdef CONFIG_MAILCAP
357 &mailcap_mime_module
,
359 #ifdef CONFIG_MIMETYPES
360 &mimetypes_mime_module
,
365 struct module mime_module
= struct_module(
366 /* name: */ N_("MIME"),
367 /* options: */ mime_options
,
369 /* submodules: */ mime_submodules
,