]> diplodocus.org Git - nmh/blob - sbr/mime_type.c
Various IMAP protocol improvements
[nmh] / sbr / mime_type.c
1 /* mime_type.c -- routine to determine the MIME Content-Type of a file
2 *
3 * This code is Copyright (c) 2014, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/mh.h>
9 #include <h/utils.h>
10 #include <h/tws.h>
11 #include "mime_type.h"
12
13 #ifdef MIMETYPEPROC
14 static char *get_file_info(const char *, const char *);
15 #endif /* MIMETYPEPROC */
16
17 /*
18 * Try to use external command to determine mime type, and possibly
19 * encoding. If that fails try using the filename extension. Caller
20 * is responsible for free'ing returned memory.
21 */
22 char *
23 mime_type(const char *file_name)
24 {
25 char *content_type = NULL; /* mime content type */
26 char *p;
27
28 #ifdef MIMETYPEPROC
29 char *mimetype;
30
31 if ((mimetype = get_file_info(MIMETYPEPROC, file_name))) {
32 #ifdef MIMEENCODINGPROC
33 /* Try to append charset for text content. */
34 char *mimeencoding;
35
36 if (!strncasecmp(mimetype, "text", 4) &&
37 (mimeencoding = get_file_info(MIMEENCODINGPROC, file_name))) {
38 content_type = concat(mimetype, "; charset=", mimeencoding, NULL);
39 free(mimeencoding);
40 free(mimetype);
41 } else
42 content_type = mimetype;
43 #else /* MIMEENCODINGPROC */
44 content_type = mimetype;
45 #endif /* MIMEENCODINGPROC */
46 }
47 #endif /* MIMETYPEPROC */
48
49 /*
50 * If we didn't get the MIME type from the contents (or we don't support
51 * the necessary command) then use the mhshow suffix.
52 */
53
54 if (content_type == NULL) {
55 struct node *np; /* Content scan node pointer */
56 FILE *fp; /* File pointer for mhn.defaults */
57
58 static bool loaded_defaults;
59 if (! loaded_defaults &&
60 (fp = fopen(p = etcpath("mhn.defaults"), "r"))) {
61 loaded_defaults = true;
62 readconfig(NULL, fp, p, 0);
63 fclose(fp);
64 }
65
66 if ((p = strrchr(file_name, '.')) != NULL) {
67 for (np = m_defs; np; np = np->n_next) {
68 if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 &&
69 strcasecmp(p, FENDNULL(np->n_field)) == 0) {
70 content_type = strdup(np->n_name + 14);
71 break;
72 }
73 }
74 }
75
76 /*
77 * If we didn't match any filename extension, try to infer the
78 * content type. If we have binary, assume application/octet-stream;
79 * otherwise, assume text/plain.
80 */
81
82 if (content_type == NULL) {
83 FILE *fp;
84 int c;
85
86 if (!(fp = fopen(file_name, "r"))) {
87 inform("unable to access file \"%s\"", file_name);
88 return NULL;
89 }
90
91 bool binary = false;
92 while ((c = getc(fp)) != EOF) {
93 if (! isascii(c) || c == 0) {
94 binary = true;
95 break;
96 }
97 }
98
99 fclose(fp);
100
101 content_type =
102 strdup(binary ? "application/octet-stream" : "text/plain");
103 }
104 }
105
106 return content_type;
107 }
108
109
110 #ifdef MIMETYPEPROC
111 /*
112 * Get information using proc about a file.
113 * Non-null return value must be free(3)'d. */
114 static char *
115 get_file_info(const char *proc, const char *file_name)
116 {
117 char *quotec;
118 char *cmd;
119 FILE *fp;
120 bool ok;
121 char buf[max(BUFSIZ, 2048)];
122 char *info;
123 char *needle;
124
125 if (strchr(file_name, '\'')) {
126 if (strchr(file_name, '"')) {
127 inform("filenames containing both single and double quotes "
128 "are unsupported for attachment");
129 return NULL;
130 }
131 quotec = "\"";
132 } else
133 quotec = "'";
134
135 cmd = concat(proc, " ", quotec, file_name, quotec, NULL);
136 if (!cmd) {
137 inform("concat with \"%s\" failed, out of memory?", proc);
138 return NULL;
139 }
140
141 if ((fp = popen(cmd, "r")) == NULL) {
142 inform("no output from %s", cmd);
143 free(cmd);
144 return NULL;
145 }
146
147 ok = fgets(buf, sizeof buf, fp);
148 free(cmd);
149 (void)pclose(fp);
150 if (!ok)
151 return NULL;
152
153 /* s#^[^:]*:[ \t]*##. */
154 info = buf;
155 if ((needle = strchr(info, ':'))) {
156 info = needle + 1;
157 while (isblank((unsigned char)*info))
158 info++;
159 }
160
161 /* s#[\n\r].*##. */
162 if ((needle = strpbrk(info, "\n\r")))
163 *needle = '\0';
164
165 return strdup(info);
166 }
167 #endif /* MIMETYPEPROC */