* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / mimetype.c
blob9f688133db5ee566faa5dae7620e7d02b5ebf777
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/mimetype.h"
17 #include "../pith/mimedesc.h"
18 #include "../pith/state.h"
19 #include "../pith/conf.h"
20 #include "../pith/mailcap.h"
21 #include "../pith/util.h"
24 * We've decided not to implement the RFC1524 standard minimum path, because
25 * some of us think it is harder to debug a problem when you may be misled
26 * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
28 #if defined(DOS) || defined(OS2)
29 #define MT_PATH_SEPARATOR ';'
30 #define MT_USER_FILE "MIMETYPE"
31 #define MT_STDPATH NULL
32 #else /* !DOS */
33 #define MT_PATH_SEPARATOR ':'
34 #define MT_USER_FILE NULL
35 #define MT_STDPATH \
36 ".mime.types:/etc/mime.types:/usr/local/lib/mime.types"
37 #endif /* !DOS */
39 #define LINE_BUF_SIZE 2000
43 * Types used to pass parameters and operator functions to the
44 * mime.types searching routines.
46 #define MT_MAX_FILE_EXTENSION 3
50 * Internal prototypes
52 int mt_browse_types_file(MT_OPERATORPROC, MT_MAP_T *, char *);
53 int mt_srch_by_type(MT_MAP_T *, FILE *);
58 * Exported function that does the work of sniffing the mime.types
59 * files and filling in the body pointer if found. Returns 1 (TRUE) if
60 * extension found, and body pointer filled in, 0 (FALSE) otherwise.
62 int
63 set_mime_type_by_extension(struct mail_bodystruct *body, char *filename)
65 MT_MAP_T e2b;
67 if(mt_get_file_ext(filename, &e2b.from.ext)
68 && mt_srch_mime_type(mt_srch_by_ext, &e2b)){
69 body->type = e2b.to.mime.type;
70 body->subtype = e2b.to.mime.subtype; /* NOTE: subtype was malloc'd */
71 return(1);
74 return(0);
79 * Exported function that maps from mime types to file extensions.
81 int
82 set_mime_extension_by_type (char *ext, char *mtype)
84 MT_MAP_T t2e;
86 t2e.from.mime_type = mtype;
87 t2e.to.ext = ext;
88 return (mt_srch_mime_type (mt_srch_by_type, &t2e));
92 int
93 check_mime_type_by_extension (char *ext, char *mtype)
95 MT_MAP_T e2t;
96 char mimet[128];
98 if((e2t.from.ext = ext) != NULL && *e2t.from.ext
99 && mt_srch_mime_type(mt_srch_by_ext, &e2t)){
100 snprintf(mimet, sizeof(mimet), "%s/%s",
101 body_type_names(e2t.to.mime.type), e2t.to.mime.subtype);
102 mimet[sizeof(mimet) - 1] = '\0';
103 fs_give((void **)& e2t.to.mime.subtype);
104 return strucmp(mimet, mtype) ? 0 : 1;
107 return(0);
112 * Separate and return a pointer to the first character in the 'filename'
113 * character buffer that comes after the rightmost '.' character in the
114 * filename. (What I mean is a pointer to the filename - extension).
116 * Returns 1 if an extension is found, 0 otherwise.
119 mt_get_file_ext(char *filename, char **extension)
121 dprint((5, "mt_get_file_ext : filename=\"%s\", ",
122 filename ? filename : "?"));
124 for(*extension = NULL; filename && *filename; filename++)
125 if(*filename == '.')
126 *extension = filename + 1;
128 dprint((5, "extension=\"%s\"\n",
129 (extension && *extension) ? *extension : "?"));
131 return(*extension ? 1 : 0);
136 * Build a list of possible mime.type files. For each one that exists
137 * call the mt_operator function.
138 * Loop terminates when mt_operator returns non-zero.
141 mt_srch_mime_type(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map)
143 char *s, *pathcopy, *path;
144 int rv = 0;
146 dprint((5, "- mt_srch_mime_type -\n"));
148 pathcopy = mc_conf_path(ps_global->VAR_MIMETYPE_PATH, getenv("MIMETYPES"),
149 MT_USER_FILE, MT_PATH_SEPARATOR, MT_STDPATH);
151 path = pathcopy; /* overloaded "path" */
153 dprint((7, "mime_types: path: %s\n", path ? path : "?"));
154 while(path){
155 if((s = strindex(path, MT_PATH_SEPARATOR)) != NULL)
156 *s++ = '\0';
158 if((rv = mt_browse_types_file(mt_operator, mt_map, path)) != 0)
159 break;
161 path = s;
164 if(pathcopy)
165 fs_give((void **)&pathcopy);
167 if(!rv && mime_os_specific_access()){
168 if(mt_operator == mt_srch_by_ext){
169 char buf[256];
171 buf[0] = '\0';
172 if(mime_get_os_mimetype_from_ext(mt_map->from.ext, buf, 256)){
173 if((s = strindex(buf, '/')) != NULL){
174 *s++ = '\0';
175 mt_map->to.mime.type = mt_translate_type(buf);
176 mt_map->to.mime.subtype = cpystr(s);
177 rv = 1;
181 else if(mt_operator == mt_srch_by_type){
182 if(mime_get_os_ext_from_mimetype(mt_map->from.mime_type,
183 mt_map->to.ext, 32)){
184 /* the 32 comes from var ext[] in display_attachment() */
185 if(*(s = mt_map->to.ext) == '.')
186 while((*s = *(s+1)) != '\0')
187 s++;
189 rv = 1;
192 else
193 alpine_panic("Unhandled mime type search");
196 /* if we still can not find the type, but it is a .docx (or alike) extension
197 set the type here. Do not use the grope function.
199 if(rv == 0){
200 rv = 1; /* assume success */
201 mt_map->to.mime.type = TYPEAPPLICATION;
202 if(!strucmp(mt_map->from.ext, "docx"))
203 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT");
204 else if(!strucmp(mt_map->from.ext, "xslx"))
205 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.SHEET");
206 else if(!strucmp(mt_map->from.ext, "xltx"))
207 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.TEMPLATE");
208 else if(!strucmp(mt_map->from.ext, "potx"))
209 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.TEMPLATE");
210 else if(!strucmp(mt_map->from.ext, "ppsx"))
211 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDESHOW");
212 else if(!strucmp(mt_map->from.ext, "pptx"))
213 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.PRESENTATION");
214 else if(!strucmp(mt_map->from.ext, "sldx"))
215 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDE");
216 else if(!strucmp(mt_map->from.ext, "dotx"))
217 mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.TEMPLATE");
218 else if(!strucmp(mt_map->from.ext, "xlam"))
219 mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.ADDIN.MACROENABLED.12");
220 else if(!strucmp(mt_map->from.ext, "xslb"))
221 mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.SHEET.BINARY.MACROENABLED.12");
222 else rv = 0; /* else, failure */
226 return(rv);
231 * Try to match a file extension against extensions found in the file
232 * ``filename'' if that file exists. return 1 if a match
233 * was found and 0 in all other cases.
236 mt_browse_types_file(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map, char *filename)
238 int rv = 0;
239 FILE *file;
241 dprint((7, "mt_browse_types_file(%s)\n", filename ? filename : "?"));
242 if((file = our_fopen(filename, "rb")) != NULL){
243 rv = (*mt_operator)(mt_map, file);
244 fclose(file);
246 else{
247 dprint((1, "mt_browse: FAILED open(%s) : %s.\n",
248 filename ? filename : "?", error_description(errno)));
251 return(rv);
256 * scan each line of the file. Treat each line as a mime type definition.
257 * The first word is a type/subtype specification. All following words
258 * are file extensions belonging to that type/subtype. Words are separated
259 * bij whitespace characters.
260 * If a file extension occurs more than once, then the first definition
261 * determines the file type and subtype.
264 mt_srch_by_ext(MT_MAP_T *e2b, FILE *file)
266 char buffer[LINE_BUF_SIZE];
268 /* construct a loop reading the file line by line. Then check each
269 * line for a matching definition.
271 while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
272 char *typespec;
273 char *try_extension;
275 if(buffer[0] == '#')
276 continue; /* comment */
278 /* divide the input buffer into words separated by whitespace.
279 * The first words is the type and subtype. All following words
280 * are file extensions.
282 dprint((5, "traverse: buffer=\"%s\"\n", buffer));
283 typespec = strtok(buffer," \t"); /* extract type,subtype */
284 if(!typespec)
285 continue;
287 dprint((5, "typespec=\"%s\"\n", typespec ? typespec : "?"));
288 while((try_extension = strtok(NULL, " \t\n\r")) != NULL){
289 /* compare the extensions, and assign the type if a match
290 * is found.
292 dprint((5,"traverse: trying ext \"%s\"\n",try_extension));
293 if(strucmp(try_extension, e2b->from.ext) == 0){
294 /* split the 'type/subtype' specification */
295 char *type, *subtype = NULL;
297 type = strtok(typespec,"/");
298 if(type)
299 subtype = strtok(NULL,"/");
301 dprint((5, "traverse: type=%s, subtype=%s.\n",
302 type ? type : "<null>",
303 subtype ? subtype : "<null>"));
304 /* The type is encoded as a small integer. we have to
305 * translate the character string naming the type into
306 * the corresponding number.
308 e2b->to.mime.type = mt_translate_type(type);
309 e2b->to.mime.subtype = cpystr(subtype ? subtype : "x-unknown");
310 return 1; /* a match has been found */
315 dprint((5, "traverse: search failed.\n"));
316 return 0;
321 * scan each line of the file. Treat each line as a mime type definition.
322 * Here we are looking for a matching type. When that is found return the
323 * first extension that is three chars or less.
326 mt_srch_by_type(MT_MAP_T *t2e, FILE *file)
328 char buffer[LINE_BUF_SIZE];
330 /* construct a loop reading the file line by line. Then check each
331 * line for a matching definition.
333 while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
334 char *typespec;
335 char *try_extension;
337 if(buffer[0] == '#')
338 continue; /* comment */
340 /* divide the input buffer into words separated by whitespace.
341 * The first words is the type and subtype. All following words
342 * are file extensions.
344 dprint((5, "traverse: buffer=%s.\n", buffer));
345 typespec = strtok(buffer," \t"); /* extract type,subtype */
346 dprint((5, "typespec=%s.\n", typespec ? typespec : "?"));
347 if (strucmp (typespec, t2e->from.mime_type) == 0) {
348 while((try_extension = strtok(NULL, " \t\n\r")) != NULL){
349 if (strlen (try_extension) <= MT_MAX_FILE_EXTENSION) {
350 strncpy (t2e->to.ext, try_extension, 32);
352 * not sure of the 32, so don't write to byte 32
353 * on purpose with
354 * t2e->to.ext[31] = '\0';
355 * in case that breaks something
358 return (1);
364 dprint((5, "traverse: search failed.\n"));
365 return 0;
370 * Translate a character string representing a content type into a short
371 * integer number, according to the coding described in c-client/mail.h
372 * List of content types taken from rfc1521, September 1993.
375 mt_translate_type(char *type)
377 int i;
379 for (i=0;(i<=TYPEMAX) && body_types[i] && strucmp(type,body_types[i]);i++)
382 if (i > TYPEMAX)
383 i = TYPEOTHER;
384 else if (!body_types[i]) /* if empty slot, assign it to this type */
385 body_types[i] = cpystr (type);
387 return(i);