]> diplodocus.org Git - nmh/blob - uip/mhbuild.c
b6f26eb9a2e9f08e5f0adb306bd2a2c5bd56ca5c
[nmh] / uip / mhbuild.c
1 /* mhbuild.c -- expand/translate MIME composition files
2 *
3 * This code is Copyright (c) 2002, 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 <fcntl.h>
10 #include <h/md5.h>
11 #include <h/mts.h>
12 #include <h/tws.h>
13 #include <h/mime.h>
14 #include <h/mhparse.h>
15 #include <h/mhcachesbr.h>
16 #include "h/done.h"
17 #include <h/utils.h>
18 #include "sbr/m_maildir.h"
19 #include "sbr/m_mktemp.h"
20 #include "mhfree.h"
21 #include "mhoutsbr.h"
22
23 #define MHBUILD_SWITCHES \
24 X("auto", 0, AUTOSW) \
25 X("noauto", 0, NAUTOSW) \
26 X("check", 0, CHECKSW) \
27 X("nocheck", 0, NCHECKSW) \
28 X("directives", 0, DIRECTIVES) \
29 X("nodirectives", 0, NDIRECTIVES) \
30 X("headers", 0, HEADSW) \
31 X("noheaders", 0, NHEADSW) \
32 X("list", 0, LISTSW) \
33 X("nolist", 0, NLISTSW) \
34 X("realsize", 0, SIZESW) \
35 X("norealsize", 0, NSIZESW) \
36 X("rfc934mode", 0, RFC934SW) \
37 X("norfc934mode", 0, NRFC934SW) \
38 X("verbose", 0, VERBSW) \
39 X("noverbose", 0, NVERBSW) \
40 X("disposition", 0, DISPOSW) \
41 X("nodisposition", 0, NDISPOSW) \
42 X("rcache policy", 0, RCACHESW) \
43 X("wcache policy", 0, WCACHESW) \
44 X("contentid", 0, CONTENTIDSW) \
45 X("nocontentid", 0, NCONTENTIDSW) \
46 X("headerencoding encoding-algorithm", 0, HEADERENCSW) \
47 X("autoheaderencoding", 0, AUTOHEADERENCSW) \
48 X("maxunencoded", 0, MAXUNENCSW) \
49 X("version", 0, VERSIONSW) \
50 X("help", 0, HELPSW) \
51 X("debug", -5, DEBUGSW) \
52 X("dist", 0, DISTSW) \
53
54 #define X(sw, minchars, id) id,
55 DEFINE_SWITCH_ENUM(MHBUILD);
56 #undef X
57
58 #define X(sw, minchars, id) { sw, minchars, id },
59 DEFINE_SWITCH_ARRAY(MHBUILD, switches);
60 #undef X
61
62 /* utf-8 is for Email Address Internationalization, using SMTPUTF8. */
63 #define MIMEENCODING_SWITCHES \
64 X("base64", 0, BASE64SW) \
65 X("quoted-printable", 0, QUOTEDPRINTSW) \
66 X("utf-8", 0, UTF8SW) \
67
68 #define X(sw, minchars, id) id,
69 DEFINE_SWITCH_ENUM(MIMEENCODING);
70 #undef X
71
72 #define X(sw, minchars, id) { sw, minchars, id },
73 DEFINE_SWITCH_ARRAY(MIMEENCODING, encodingswitches);
74 #undef X
75
76 int debugsw = 0;
77
78 int listsw = 0;
79 int rfc934sw = 0;
80 int contentidsw = 1;
81
82 /*
83 * Temporary files
84 */
85 static char infile[BUFSIZ];
86 static char outfile[BUFSIZ];
87
88
89 int
90 main (int argc, char **argv)
91 {
92 int sizesw = 1, headsw = 1, directives = 1, autobuild = 0, dist = 0;
93 int verbosw = 0, dispo = 0;
94 size_t maxunencoded = MAXTEXTPERLN;
95 int *icachesw;
96 char *cp, buf[BUFSIZ];
97 char buffer[BUFSIZ], *compfile = NULL;
98 char **argp, **arguments;
99 CT ct, cts[2];
100 FILE *fp = NULL;
101 FILE *fp_out = NULL;
102 int header_encoding = CE_UNKNOWN;
103 size_t n;
104
105 if (nmh_init(argv[0], 2)) { return 1; }
106
107 arguments = getarguments (invo_name, argc, argv, 1);
108 argp = arguments;
109
110 while ((cp = *argp++)) {
111 if (cp[0] == '-' && cp[1] == '\0') {
112 if (compfile)
113 adios (NULL, "cannot specify both standard input and a file");
114 compfile = cp;
115 listsw = 0; /* turn off -list if using standard in/out */
116 verbosw = 0; /* turn off -verbose listings */
117 break;
118 }
119 if (*cp == '-') {
120 switch (smatch (++cp, switches)) {
121 case AMBIGSW:
122 ambigsw (cp, switches);
123 done (1);
124 case UNKWNSW:
125 adios (NULL, "-%s unknown", cp);
126
127 case HELPSW:
128 snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
129 print_help (buf, switches, 1);
130 done (0);
131 case VERSIONSW:
132 print_version(invo_name);
133 done (0);
134
135 case AUTOSW:
136 /* -auto implies -nodirectives */
137 autobuild = 1;
138 directives = 0;
139 continue;
140 case NAUTOSW:
141 /*
142 * We're turning directives back on since this is likely here
143 * to override a profile entry
144 */
145 autobuild = 0;
146 directives = 1;
147 continue;
148
149 case RCACHESW:
150 icachesw = &rcachesw;
151 goto do_cache;
152 case WCACHESW:
153 icachesw = &wcachesw;
154 do_cache: ;
155 if (!(cp = *argp++) || *cp == '-')
156 adios (NULL, "missing argument to %s", argp[-2]);
157 switch (*icachesw = smatch (cp, cache_policy)) {
158 case AMBIGSW:
159 ambigsw (cp, cache_policy);
160 done (1);
161 case UNKWNSW:
162 adios (NULL, "%s unknown", cp);
163 default:
164 break;
165 }
166 continue;
167
168 case CHECKSW:
169 checksw++;
170 continue;
171 case NCHECKSW:
172 checksw = 0;
173 continue;
174
175 case HEADSW:
176 headsw++;
177 continue;
178 case NHEADSW:
179 headsw = 0;
180 continue;
181
182 case DIRECTIVES:
183 directives = 1;
184 continue;
185 case NDIRECTIVES:
186 directives = 0;
187 continue;
188
189 case LISTSW:
190 listsw++;
191 continue;
192 case NLISTSW:
193 listsw = 0;
194 continue;
195
196 case RFC934SW:
197 rfc934sw++;
198 continue;
199 case NRFC934SW:
200 rfc934sw = 0;
201 continue;
202
203 case SIZESW:
204 sizesw++;
205 continue;
206 case NSIZESW:
207 sizesw = 0;
208 continue;
209
210 case CONTENTIDSW:
211 contentidsw = 1;
212 continue;
213 case NCONTENTIDSW:
214 contentidsw = 0;
215 continue;
216
217 case HEADERENCSW: {
218 int encoding;
219
220 if (!(cp = *argp++) || *cp == '-')
221 adios (NULL, "missing argument to %s", argp[-2]);
222 switch (encoding = smatch (cp, encodingswitches)) {
223 case AMBIGSW:
224 ambigsw (cp, encodingswitches);
225 done (1);
226 case UNKWNSW:
227 adios (NULL, "%s unknown encoding algorithm", cp);
228 case BASE64SW:
229 header_encoding = CE_BASE64;
230 break;
231 case QUOTEDPRINTSW:
232 header_encoding = CE_QUOTED;
233 break;
234 case UTF8SW:
235 header_encoding = CE_8BIT;
236 break;
237 default:
238 adios (NULL, "Internal error: algorithm %s", cp);
239 }
240 continue;
241 }
242
243 case AUTOHEADERENCSW:
244 header_encoding = CE_UNKNOWN;
245 continue;
246
247 case MAXUNENCSW:
248 if (!(cp = *argp++) || *cp == '-')
249 adios (NULL, "missing argument to %s", argp[-2]);
250 if ((maxunencoded = atoi(cp)) < 1)
251 adios (NULL, "Invalid argument for %s: %s", argp[-2], cp);
252 if (maxunencoded > 998)
253 adios (NULL, "limit of -maxunencoded is 998");
254 continue;
255
256 case VERBSW:
257 verbosw++;
258 continue;
259 case NVERBSW:
260 verbosw = 0;
261 continue;
262 case DISPOSW:
263 dispo = 1;
264 continue;
265 case NDISPOSW:
266 dispo = 0;
267 continue;
268 case DEBUGSW:
269 debugsw = 1;
270 continue;
271 case DISTSW:
272 dist = 1;
273 continue;
274 }
275 }
276 if (compfile)
277 adios (NULL, "only one composition file allowed");
278 compfile = cp;
279 }
280
281 /*
282 * Check if we've specified an additional profile
283 */
284 if ((cp = getenv ("MHBUILD"))) {
285 if ((fp = fopen (cp, "r"))) {
286 readconfig(NULL, fp, cp, 0);
287 fclose (fp);
288 } else {
289 admonish ("", "unable to read $MHBUILD profile (%s)", cp);
290 }
291 }
292
293 /*
294 * Read the standard profile setup
295 */
296 if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
297 readconfig(NULL, fp, cp, 0);
298 fclose (fp);
299 }
300
301 /* Check for public cache location */
302 if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
303 cache_public = NULL;
304
305 /* Check for private cache location */
306 if (!(cache_private = context_find (nmhprivcache)))
307 cache_private = ".cache";
308 cache_private = getcpy (m_maildir (cache_private));
309
310 if (!context_find ("path"))
311 free (path ("./", TFOLDER));
312
313 /* Check if we have a file to process */
314 if (!compfile)
315 adios (NULL, "need to specify a %s composition file", invo_name);
316
317 /*
318 * Process the composition file from standard input.
319 */
320 if (compfile[0] == '-' && compfile[1] == '\0') {
321 if ((cp = m_mktemp2(NULL, invo_name, NULL, &fp)) == NULL) {
322 adios(NULL, "unable to create temporary file in %s",
323 get_temp_dir());
324 }
325 strncpy (infile, cp, sizeof(infile));
326
327 /* copy standard input to temporary file */
328 while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
329 if (fwrite(buffer, 1, n, fp) != n) {
330 adios (NULL, "error copying to temporary file");
331 }
332 }
333 fclose (fp);
334
335 /* build the content structures for MIME message */
336 ct = build_mime (infile, autobuild, dist, directives, header_encoding,
337 maxunencoded, verbosw);
338
339 /*
340 * If ct == NULL, that means that -auto was set and a MIME version
341 * header was already seen. Just use the input file as the output
342 */
343
344 if (!ct) {
345 if (! (fp = fopen(infile, "r"))) {
346 adios(NULL, "Unable to open %s for reading", infile);
347 }
348 while ((n = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
349 if (fwrite(buffer, 1, n, stdout) != n) {
350 adios(NULL, "error copying %s to stdout", infile);
351 }
352 }
353 } else {
354 /* output the message */
355 output_message_fp (ct, stdout, NULL);
356 free_content (ct);
357 }
358
359 done (0);
360 }
361
362 /*
363 * Process the composition file from a file.
364 */
365
366 /* build the content structures for MIME message */
367 ct = build_mime (compfile, autobuild, dist, directives, header_encoding,
368 maxunencoded, verbosw);
369
370 /*
371 * If ct == NULL, that means -auto was set and we found a MIME version
372 * header. Simply exit and do nothing.
373 */
374
375 if (! ct)
376 done(0);
377
378 cts[0] = ct;
379 cts[1] = NULL;
380
381 /* output MIME message to this temporary file */
382 if ((cp = m_mktemp2(compfile, invo_name, NULL, &fp_out)) == NULL) {
383 adios(NULL, "unable to create temporary file");
384 }
385 strncpy(outfile, cp, sizeof(outfile));
386
387 /* output the message */
388 output_message_fp (ct, fp_out, outfile);
389 fclose(fp_out);
390
391 /*
392 * List the message info
393 */
394 if (listsw)
395 list_all_messages (cts, headsw, sizesw, verbosw, debugsw, dispo);
396
397 /* Rename composition draft */
398 snprintf (buffer, sizeof(buffer), "%s.orig", m_backup (compfile));
399 if (rename (compfile, buffer) == NOTOK) {
400 adios (compfile, "unable to rename comp draft %s to", buffer);
401 }
402
403 /* Rename output file to take its place */
404 if (rename (outfile, compfile) == NOTOK) {
405 advise (outfile, "unable to rename output %s to", compfile);
406 rename (buffer, compfile);
407 done (1);
408 }
409 /* Remove from atexit(3) list of files to unlink. */
410 if (!(m_unlink(outfile) == -1 && errno == ENOENT)) {
411 adios(outfile, "file exists after rename:");
412 }
413
414 free_content (ct);
415 done (0);
416 return 1;
417 }