1 /* Support for mime.types files for mapping file extensions to content types */
3 /* Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu>
4 * Copyright (C) 2003-2004 The ELinks Project */
17 #include "config/options.h"
18 #include "intl/gettext/libintl.h"
19 #include "main/module.h"
20 #include "mime/backend/common.h"
21 #include "mime/backend/mimetypes.h"
22 #include "mime/mime.h"
23 #include "session/session.h"
24 #include "util/hash.h"
25 #include "util/lists.h"
26 #include "util/memory.h"
28 #define BACKEND_NAME "mimetypes"
30 struct mimetypes_entry
{
31 unsigned char *content_type
;
32 unsigned char extension
[1];
35 enum mimetypes_option
{
44 /* Keep options in alphabetical order. */
45 static struct option_info mimetypes_options
[] = {
46 INIT_OPT_TREE("mime", N_("Mimetypes files"),
48 N_("Options for the support of mime.types files. These files "
49 "can be used to find the content type of a URL by looking at "
50 "the extension of the file name.")),
52 INIT_OPT_BOOL("mime.mimetypes", N_("Enable"),
54 N_("Enable mime.types support.")),
56 INIT_OPT_STRING("mime.mimetypes", N_("Path"),
57 "path", 0, DEFAULT_MIMETYPES_PATH
,
58 N_("The search path for mime.types files. "
59 "Colon-separated list of files.")),
64 #define get_opt_mimetypes(which) mimetypes_options[(which)].option
65 #define get_mimetypes(which) get_opt_mimetypes(which).value
66 #define get_mimetypes_enable() get_mimetypes(MIMETYPES_ENABLE).number
67 #define get_mimetypes_path() get_mimetypes(MIMETYPES_PATH).string
70 static struct hash
*mimetypes_map
= NULL
;
74 done_mimetypes_entry(struct mimetypes_entry
*entry
)
77 mem_free_if(entry
->content_type
);
81 /* Parsing of a mime.types file with the format:
83 * basetype/subtype extension1 [extension2 ... extensionN]
85 * Comments starts with '#'. */
88 parse_mimetypes_extensions(unsigned char *token
, unsigned char *ctype
)
90 int ctypelen
= strlen(ctype
);
92 /* Cycle through the file extensions */
94 struct mimetypes_entry
*entry
;
95 unsigned char *extension
;
96 struct hash_item
*item
;
102 skip_nonspace(token
);
107 extlen
= strlen(extension
);
108 /* First check if the type is already known. If it is
109 * drop it. This way first files are priotized. */
110 item
= get_hash_item(mimetypes_map
, extension
, extlen
);
113 entry
= mem_calloc(1, sizeof(*entry
) + extlen
);
114 if (!entry
) continue;
116 entry
->content_type
= memacpy(ctype
, ctypelen
);
117 if (!entry
->content_type
) {
118 done_mimetypes_entry(entry
);
122 memcpy(entry
->extension
, extension
, extlen
);
124 item
= add_hash_item(mimetypes_map
, entry
->extension
, extlen
,
127 done_mimetypes_entry(entry
);
132 parse_mimetypes_file(unsigned char *filename
)
134 FILE *file
= fopen(filename
, "rb");
135 unsigned char line
[MAX_STR_LEN
];
139 while (fgets(line
, MAX_STR_LEN
- 1, file
)) {
140 unsigned char *ctype
= line
;
141 unsigned char *token
;
143 /* Weed out any comments */
144 token
= strchr(line
, '#');
150 /* Position on the next field in this line */
152 skip_nonspace(token
);
154 if (!*token
) continue;
157 /* Check if malformed content type */
158 if (!strchr(ctype
, '/')) continue;
160 parse_mimetypes_extensions(token
, ctype
);
167 init_mimetypes_map(void)
171 mimetypes_map
= init_hash8();
175 /* Determine the path */
176 path
= get_mimetypes_path();
177 if (!path
|| !*path
) path
= DEFAULT_MIMETYPES_PATH
;
180 unsigned char *filename
= get_next_path_filename(&path
, ':');
182 if (!filename
) continue;
183 parse_mimetypes_file(filename
);
187 return mimetypes_map
;
191 done_mimetypes(struct module
*module
)
193 struct hash_item
*item
;
199 foreach_hash_item (item
, *mimetypes_map
, i
) {
201 struct mimetypes_entry
*entry
= item
->value
;
203 done_mimetypes_entry(entry
);
207 free_hash(&mimetypes_map
);
211 change_hook_mimetypes(struct session
*ses
, struct option
*current
, struct option
*changed
)
213 if (changed
== &get_opt_mimetypes(MIMETYPES_PATH
)
214 || (changed
== &get_opt_mimetypes(MIMETYPES_ENABLE
)
215 && !get_mimetypes_enable())) {
216 done_mimetypes(&mimetypes_mime_module
);
223 init_mimetypes(struct module
*module
)
225 static const struct change_hook_info mimetypes_change_hooks
[] = {
226 { "mime.mimetypes", change_hook_mimetypes
},
230 register_change_hooks(mimetypes_change_hooks
);
232 if (get_cmd_opt_bool("anonymous"))
233 get_mimetypes_enable() = 0;
237 static unsigned char *
238 get_content_type_mimetypes(unsigned char *extension
)
240 struct hash_item
*item
;
243 if (!get_mimetypes_enable()
244 || (!mimetypes_map
&& !init_mimetypes_map()))
247 extension
++; /* Skip the leading '.' */
248 extensionlen
= strlen(extension
);
249 while (extensionlen
) {
250 unsigned char *trimmed
;
252 /* First the given type is looked up. */
253 item
= get_hash_item(mimetypes_map
, extension
, extensionlen
);
255 /* Check list of entries */
256 if (item
&& item
->value
) {
257 struct mimetypes_entry
*entry
= item
->value
;
259 return stracpy(entry
->content_type
);
262 /* Try to trim the extension from the left. */
263 trimmed
= strchr(extension
, '.');
267 extensionlen
-= trimmed
- extension
+ 1;
268 extension
= trimmed
+ 1;
274 const struct mime_backend mimetypes_mime_backend
= {
275 /* get_content_type: */ get_content_type_mimetypes
,
276 /* get_mime_handler: */ NULL
,
279 struct module mimetypes_mime_module
= struct_module(
280 /* name: */ N_("Mimetypes files"),
281 /* options: */ mimetypes_options
,
283 /* submodules: */ NULL
,
285 /* init: */ init_mimetypes
,
286 /* done: */ done_mimetypes