1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: mimetype.c 343 2006-12-22 18:25:39Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
#include "../pith/headers.h"
19
#include "../pith/mimetype.h"
20
#include "../pith/state.h"
21
#include "../pith/conf.h"
22
#include "../pith/mailcap.h"
23
#include "../pith/util.h"
26
* We've decided not to implement the RFC1524 standard minimum path, because
27
* some of us think it is harder to debug a problem when you may be misled
28
* into looking at the wrong mailcap entry. Likewise for MIME.Types files.
30
#if defined(DOS) || defined(OS2)
31
#define MT_PATH_SEPARATOR ';'
32
#define MT_USER_FILE "MIMETYPE"
33
#define MT_STDPATH NULL
35
#define MT_PATH_SEPARATOR ':'
36
#define MT_USER_FILE NULL
38
".mime.types:/etc/mime.types:/usr/local/lib/mime.types"
41
#define LINE_BUF_SIZE 2000
45
* Types used to pass parameters and operator functions to the
46
* mime.types searching routines.
48
#define MT_MAX_FILE_EXTENSION 3
50
typedef int (* MT_OPERATORPROC)(MT_MAP_T *, FILE *);
56
int mt_get_file_ext(char *, char **);
57
int mt_srch_mime_type(MT_OPERATORPROC, MT_MAP_T *);
58
int mt_browse_types_file(MT_OPERATORPROC, MT_MAP_T *, char *);
59
int mt_srch_by_type(MT_MAP_T *, FILE *);
60
int mt_translate_type(char *);
65
* Exported function that does the work of sniffing the mime.types
66
* files and filling in the body pointer if found. Returns 1 (TRUE) if
67
* extension found, and body pointer filled in, 0 (FALSE) otherwise.
70
set_mime_type_by_extension(struct mail_bodystruct *body, char *filename)
74
if(mt_get_file_ext(filename, &e2b.from.ext)
75
&& mt_srch_mime_type(mt_srch_by_ext, &e2b)){
76
body->type = e2b.to.mime.type;
77
body->subtype = e2b.to.mime.subtype; /* NOTE: subtype was malloc'd */
86
* Exported function that maps from mime types to file extensions.
89
set_mime_extension_by_type (char *ext, char *mtype)
93
t2e.from.mime_type = mtype;
95
return (mt_srch_mime_type (mt_srch_by_type, &t2e));
102
* Separate and return a pointer to the first character in the 'filename'
103
* character buffer that comes after the rightmost '.' character in the
104
* filename. (What I mean is a pointer to the filename - extension).
106
* Returns 1 if an extension is found, 0 otherwise.
109
mt_get_file_ext(char *filename, char **extension)
111
dprint((5, "mt_get_file_ext : filename=\"%s\", ",
112
filename ? filename : "?"));
114
for(*extension = NULL; filename && *filename; filename++)
116
*extension = filename + 1;
118
dprint((5, "extension=\"%s\"\n",
119
(extension && *extension) ? *extension : "?"));
121
return(*extension ? 1 : 0);
126
* Build a list of possible mime.type files. For each one that exists
127
* call the mt_operator function.
128
* Loop terminates when mt_operator returns non-zero.
131
mt_srch_mime_type(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map)
133
char *s, *pathcopy, *path;
136
dprint((5, "- mt_srch_mime_type -\n"));
138
pathcopy = mc_conf_path(ps_global->VAR_MIMETYPE_PATH, getenv("MIMETYPES"),
139
MT_USER_FILE, MT_PATH_SEPARATOR, MT_STDPATH);
141
path = pathcopy; /* overloaded "path" */
143
dprint((7, "mime_types: path: %s\n", path ? path : "?"));
145
if(s = strindex(path, MT_PATH_SEPARATOR))
148
if(rv = mt_browse_types_file(mt_operator, mt_map, path))
155
fs_give((void **)&pathcopy);
157
if(!rv && mime_os_specific_access()){
158
if(mt_operator == mt_srch_by_ext){
161
if(mime_get_os_mimetype_from_ext(mt_map->from.ext, buf, 256)){
162
if(s = strindex(buf, '/')){
164
mt_map->to.mime.type = mt_translate_type(buf);
165
mt_map->to.mime.subtype = cpystr(s);
170
else if(mt_operator == mt_srch_by_type){
171
if(mime_get_os_ext_from_mimetype(mt_map->from.mime_type,
172
mt_map->to.ext, 32)){
173
/* the 32 comes from var ext[] in display_attachment() */
174
if(*(s = mt_map->to.ext) == '.')
182
panic("Unhandled mime type search");
191
* Try to match a file extension against extensions found in the file
192
* ``filename'' if that file exists. return 1 if a match
193
* was found and 0 in all other cases.
196
mt_browse_types_file(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map, char *filename)
201
if(file = our_fopen(filename, "rb")){
202
rv = (*mt_operator)(mt_map, file);
206
dprint((1, "mt_browse: FAILED open(%s) : %s.\n",
207
filename ? filename : "?", error_description(errno)));
214
* scan each line of the file. Treat each line as a mime type definition.
215
* The first word is a type/subtype specification. All following words
216
* are file extensions belonging to that type/subtype. Words are separated
217
* bij whitespace characters.
218
* If a file extension occurs more than once, then the first definition
219
* determines the file type and subtype.
222
mt_srch_by_ext(MT_MAP_T *e2b, FILE *file)
224
char buffer[LINE_BUF_SIZE];
225
#if defined(DOS) || defined(OS2)
226
#define STRCMP strucmp
228
#define STRCMP strcmp
231
/* construct a loop reading the file line by line. Then check each
232
* line for a matching definition.
234
while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
239
continue; /* comment */
241
/* remove last character from the buffer */
242
buffer[strlen(buffer)-1] = '\0';
243
/* divide the input buffer into words separated by whitespace.
244
* The first words is the type and subtype. All following words
245
* are file extensions.
247
dprint((5, "traverse: buffer=\"%s\"\n", buffer));
248
typespec = strtok(buffer," \t"); /* extract type,subtype */
252
dprint((5, "typespec=\"%s\"\n", typespec ? typespec : "?"));
253
while((try_extension = strtok(NULL, " \t")) != NULL){
254
/* compare the extensions, and assign the type if a match
257
dprint((5,"traverse: trying ext \"%s\"\n",try_extension));
258
if(STRCMP(try_extension, e2b->from.ext) == 0){
259
/* split the 'type/subtype' specification */
260
char *type, *subtype = NULL;
262
type = strtok(typespec,"/");
264
subtype = strtok(NULL,"/");
266
dprint((5, "traverse: type=%s, subtype=%s.\n",
267
type ? type : "<null>",
268
subtype ? subtype : "<null>"));
269
/* The type is encoded as a small integer. we have to
270
* translate the character string naming the type into
271
* the corresponding number.
273
e2b->to.mime.type = mt_translate_type(type);
274
e2b->to.mime.subtype = cpystr(subtype ? subtype : "x-unknown");
275
return 1; /* a match has been found */
280
dprint((5, "traverse: search failed.\n"));
286
* scan each line of the file. Treat each line as a mime type definition.
287
* Here we are looking for a matching type. When that is found return the
288
* first extension that is three chars or less.
291
mt_srch_by_type(MT_MAP_T *t2e, FILE *file)
293
char buffer[LINE_BUF_SIZE];
295
/* construct a loop reading the file line by line. Then check each
296
* line for a matching definition.
298
while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
303
continue; /* comment */
305
/* remove last character from the buffer */
306
buffer[strlen(buffer)-1] = '\0';
307
/* divide the input buffer into words separated by whitespace.
308
* The first words is the type and subtype. All following words
309
* are file extensions.
311
dprint((5, "traverse: buffer=%s.\n", buffer));
312
typespec = strtok(buffer," \t"); /* extract type,subtype */
313
dprint((5, "typespec=%s.\n", typespec ? typespec : "?"));
314
if (strucmp (typespec, t2e->from.mime_type) == 0) {
315
while((try_extension = strtok(NULL, " \t")) != NULL) {
316
if (strlen (try_extension) <= MT_MAX_FILE_EXTENSION) {
317
strncpy (t2e->to.ext, try_extension, 32);
319
* not sure of the 32, so don't write to byte 32
321
* t2e->to.ext[31] = '\0';
322
* in case that breaks something
331
dprint((5, "traverse: search failed.\n"));
337
* Translate a character string representing a content type into a short
338
* integer number, according to the coding described in c-client/mail.h
339
* List of content types taken from rfc1521, September 1993.
342
mt_translate_type(char *type)
346
for (i=0;(i<=TYPEMAX) && body_types[i] && strucmp(type,body_types[i]);i++)
351
else if (!body_types[i]) /* if empty slot, assign it to this type */
352
body_types[i] = cpystr (type);