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