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