]> diplodocus.org Git - nmh/blob - sbr/mime_type.c
mhbuildsbr.c: Flip logic, moving goto to then-block; no need for else.
[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 char *content_type = NULL; /* mime content type */
25 char *p;
26 static int loaded_defaults = 0;
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) == 0) {
37 if ((mimeencoding = get_file_info(MIMEENCODINGPROC, file_name))) {
38 content_type = concat(mimetype, "; charset=", mimeencoding,
39 NULL);
40 free (mimetype);
41 } else {
42 content_type = mimetype;
43 }
44 } else {
45 content_type = mimetype;
46 }
47 #else /* MIMEENCODINGPROC */
48 content_type = mimetype;
49 #endif /* MIMEENCODINGPROC */
50 }
51 #endif /* MIMETYPEPROC */
52
53 /*
54 * If we didn't get the MIME type from the contents (or we don't support
55 * the necessary command) then use the mhshow suffix.
56 */
57
58 if (content_type == NULL) {
59 struct node *np; /* Content scan node pointer */
60 FILE *fp; /* File pointer for mhn.defaults */
61
62 if (! loaded_defaults &&
63 (fp = fopen(p = etcpath("mhn.defaults"), "r"))) {
64 loaded_defaults = 1;
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 binary = 0, c;
88
89 if (!(fp = fopen(file_name, "r"))) {
90 inform("unable to access file \"%s\"", file_name);
91 return NULL;
92 }
93
94 while ((c = getc(fp)) != EOF) {
95 if (! isascii(c) || c == 0) {
96 binary = 1;
97 break;
98 }
99 }
100
101 fclose(fp);
102
103 content_type =
104 strdup(binary ? "application/octet-stream" : "text/plain");
105 }
106 }
107
108 return content_type;
109 }
110
111
112 #ifdef MIMETYPEPROC
113 /*
114 * Get information using proc about a file.
115 */
116 static char *
117 get_file_info(const char *proc, const char *file_name) {
118 char *cmd, *cp;
119 char *quotec = "'";
120
121 if ((cp = strchr(file_name, '\''))) {
122 /* file_name contains a single quote. */
123 if (strchr(file_name, '"')) {
124 inform("filenames containing both single and double quotes "
125 "are unsupported for attachment");
126 return NULL;
127 }
128 quotec = "\"";
129 }
130
131 if ((cmd = concat(proc, " ", quotec, file_name, quotec, NULL))) {
132 FILE *fp;
133
134 if ((fp = popen(cmd, "r")) != NULL) {
135 char buf[max(BUFSIZ, 2048)];
136
137 buf[0] = '\0';
138 if (fgets(buf, sizeof buf, fp)) {
139 char *eol;
140
141 /* Skip leading <filename>:<whitespace>, if present. */
142 if ((cp = strchr(buf, ':')) != NULL) {
143 ++cp;
144 while (*cp && isblank((unsigned char) *cp)) {
145 ++cp;
146 }
147 } else {
148 cp = buf;
149 }
150
151 /* Truncate at newline (LF or CR), if present. */
152 if ((eol = strpbrk(cp, "\n\r")) != NULL) {
153 *eol = '\0';
154 }
155 } else if (buf[0] == '\0') {
156 /* This can happen on Cygwin if the popen()
157 mysteriously fails. Return NULL so that the caller
158 will use another method to determine the info. */
159 free (cp);
160 cp = NULL;
161 }
162
163 (void) pclose(fp);
164 } else {
165 inform("no output from %s", cmd);
166 }
167
168 free(cmd);
169 } else {
170 inform("concat with \"%s\" failed, out of memory?", proc);
171 }
172
173 return cp ? strdup(cp) : NULL;
174 }
175 #endif /* MIMETYPEPROC */