/* mhbuild.c -- expand/translate MIME composition files * * This code is Copyright (c) 2002, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ #include #include #include #include #include #include #include #include #include #include "sbr/m_maildir.h" #include "sbr/m_mktemp.h" #include "mhfree.h" #include "mhoutsbr.h" #define MHBUILD_SWITCHES \ X("auto", 0, AUTOSW) \ X("noauto", 0, NAUTOSW) \ X("check", 0, CHECKSW) \ X("nocheck", 0, NCHECKSW) \ X("directives", 0, DIRECTIVES) \ X("nodirectives", 0, NDIRECTIVES) \ X("headers", 0, HEADSW) \ X("noheaders", 0, NHEADSW) \ X("list", 0, LISTSW) \ X("nolist", 0, NLISTSW) \ X("realsize", 0, SIZESW) \ X("norealsize", 0, NSIZESW) \ X("rfc934mode", 0, RFC934SW) \ X("norfc934mode", 0, NRFC934SW) \ X("verbose", 0, VERBSW) \ X("noverbose", 0, NVERBSW) \ X("disposition", 0, DISPOSW) \ X("nodisposition", 0, NDISPOSW) \ X("rcache policy", 0, RCACHESW) \ X("wcache policy", 0, WCACHESW) \ X("contentid", 0, CONTENTIDSW) \ X("nocontentid", 0, NCONTENTIDSW) \ X("headerencoding encoding-algorithm", 0, HEADERENCSW) \ X("autoheaderencoding", 0, AUTOHEADERENCSW) \ X("maxunencoded", 0, MAXUNENCSW) \ X("version", 0, VERSIONSW) \ X("help", 0, HELPSW) \ X("debug", -5, DEBUGSW) \ X("dist", 0, DISTSW) \ #define X(sw, minchars, id) id, DEFINE_SWITCH_ENUM(MHBUILD); #undef X #define X(sw, minchars, id) { sw, minchars, id }, DEFINE_SWITCH_ARRAY(MHBUILD, switches); #undef X /* utf-8 is for Email Address Internationalization, using SMTPUTF8. */ #define MIMEENCODING_SWITCHES \ X("base64", 0, BASE64SW) \ X("quoted-printable", 0, QUOTEDPRINTSW) \ X("utf-8", 0, UTF8SW) \ #define X(sw, minchars, id) id, DEFINE_SWITCH_ENUM(MIMEENCODING); #undef X #define X(sw, minchars, id) { sw, minchars, id }, DEFINE_SWITCH_ARRAY(MIMEENCODING, encodingswitches); #undef X int debugsw = 0; int listsw = 0; int rfc934sw = 0; int contentidsw = 1; /* * Temporary files */ static char infile[BUFSIZ]; static int unlink_infile = 0; static char outfile[BUFSIZ]; static int unlink_outfile = 0; static void unlink_done (int) NORETURN; int main (int argc, char **argv) { int sizesw = 1, headsw = 1, directives = 1, autobuild = 0, dist = 0; int verbosw = 0, dispo = 0; size_t maxunencoded = MAXTEXTPERLN; int *icachesw; char *cp, buf[BUFSIZ]; char buffer[BUFSIZ], *compfile = NULL; char **argp, **arguments; CT ct, cts[2]; FILE *fp = NULL; FILE *fp_out = NULL; int header_encoding = CE_UNKNOWN; size_t n; if (nmh_init(argv[0], 2)) { return 1; } done=unlink_done; arguments = getarguments (invo_name, argc, argv, 1); argp = arguments; while ((cp = *argp++)) { if (cp[0] == '-' && cp[1] == '\0') { if (compfile) adios (NULL, "cannot specify both standard input and a file"); compfile = cp; listsw = 0; /* turn off -list if using standard in/out */ verbosw = 0; /* turn off -verbose listings */ break; } if (*cp == '-') { switch (smatch (++cp, switches)) { case AMBIGSW: ambigsw (cp, switches); done (1); case UNKWNSW: adios (NULL, "-%s unknown", cp); case HELPSW: snprintf (buf, sizeof(buf), "%s [switches] file", invo_name); print_help (buf, switches, 1); done (0); case VERSIONSW: print_version(invo_name); done (0); case AUTOSW: /* -auto implies -nodirectives */ autobuild = 1; directives = 0; continue; case NAUTOSW: /* * We're turning directives back on since this is likely here * to override a profile entry */ autobuild = 0; directives = 1; continue; case RCACHESW: icachesw = &rcachesw; goto do_cache; case WCACHESW: icachesw = &wcachesw; do_cache: ; if (!(cp = *argp++) || *cp == '-') adios (NULL, "missing argument to %s", argp[-2]); switch (*icachesw = smatch (cp, cache_policy)) { case AMBIGSW: ambigsw (cp, cache_policy); done (1); case UNKWNSW: adios (NULL, "%s unknown", cp); default: break; } continue; case CHECKSW: checksw++; continue; case NCHECKSW: checksw = 0; continue; case HEADSW: headsw++; continue; case NHEADSW: headsw = 0; continue; case DIRECTIVES: directives = 1; continue; case NDIRECTIVES: directives = 0; continue; case LISTSW: listsw++; continue; case NLISTSW: listsw = 0; continue; case RFC934SW: rfc934sw++; continue; case NRFC934SW: rfc934sw = 0; continue; case SIZESW: sizesw++; continue; case NSIZESW: sizesw = 0; continue; case CONTENTIDSW: contentidsw = 1; continue; case NCONTENTIDSW: contentidsw = 0; continue; case HEADERENCSW: { int encoding; if (!(cp = *argp++) || *cp == '-') adios (NULL, "missing argument to %s", argp[-2]); switch (encoding = smatch (cp, encodingswitches)) { case AMBIGSW: ambigsw (cp, encodingswitches); done (1); case UNKWNSW: adios (NULL, "%s unknown encoding algorithm", cp); case BASE64SW: header_encoding = CE_BASE64; break; case QUOTEDPRINTSW: header_encoding = CE_QUOTED; break; case UTF8SW: header_encoding = CE_8BIT; break; default: adios (NULL, "Internal error: algorithm %s", cp); } continue; } case AUTOHEADERENCSW: header_encoding = CE_UNKNOWN; continue; case MAXUNENCSW: if (!(cp = *argp++) || *cp == '-') adios (NULL, "missing argument to %s", argp[-2]); if ((maxunencoded = atoi(cp)) < 1) adios (NULL, "Invalid argument for %s: %s", argp[-2], cp); if (maxunencoded > 998) adios (NULL, "limit of -maxunencoded is 998"); continue; case VERBSW: verbosw++; continue; case NVERBSW: verbosw = 0; continue; case DISPOSW: dispo = 1; continue; case NDISPOSW: dispo = 0; continue; case DEBUGSW: debugsw = 1; continue; case DISTSW: dist = 1; continue; } } if (compfile) adios (NULL, "only one composition file allowed"); compfile = cp; } /* * Check if we've specified an additional profile */ if ((cp = getenv ("MHBUILD"))) { if ((fp = fopen (cp, "r"))) { readconfig(NULL, fp, cp, 0); fclose (fp); } else { admonish ("", "unable to read $MHBUILD profile (%s)", cp); } } /* * Read the standard profile setup */ if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) { readconfig(NULL, fp, cp, 0); fclose (fp); } /* Check for public cache location */ if ((cache_public = context_find (nmhcache)) && *cache_public != '/') cache_public = NULL; /* Check for private cache location */ if (!(cache_private = context_find (nmhprivcache))) cache_private = ".cache"; cache_private = getcpy (m_maildir (cache_private)); if (!context_find ("path")) free (path ("./", TFOLDER)); /* Check if we have a file to process */ if (!compfile) adios (NULL, "need to specify a %s composition file", invo_name); /* * Process the composition file from standard input. */ if (compfile[0] == '-' && compfile[1] == '\0') { if ((cp = m_mktemp2(NULL, invo_name, NULL, &fp)) == NULL) { adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } /* save a copy of the name for later removal */ strncpy (infile, cp, sizeof(infile)); unlink_infile = 1; /* copy standard input to temporary file */ while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) { if (fwrite(buffer, 1, n, fp) != n) { adios (NULL, "error copying to temporary file"); } } fclose (fp); /* build the content structures for MIME message */ ct = build_mime (infile, autobuild, dist, directives, header_encoding, maxunencoded, verbosw); /* * If ct == NULL, that means that -auto was set and a MIME version * header was already seen. Just use the input file as the output */ if (!ct) { if (! (fp = fopen(infile, "r"))) { adios(NULL, "Unable to open %s for reading", infile); } while ((n = fread(buffer, 1, sizeof(buffer), fp)) > 0) { if (fwrite(buffer, 1, n, stdout) != n) { adios(NULL, "error copying %s to stdout", infile); } } } else { /* output the message */ output_message_fp (ct, stdout, NULL); free_content (ct); } done (0); } /* * Process the composition file from a file. */ /* build the content structures for MIME message */ ct = build_mime (compfile, autobuild, dist, directives, header_encoding, maxunencoded, verbosw); /* * If ct == NULL, that means -auto was set and we found a MIME version * header. Simply exit and do nothing. */ if (! ct) done(0); cts[0] = ct; cts[1] = NULL; /* output MIME message to this temporary file */ if ((cp = m_mktemp2(compfile, invo_name, NULL, &fp_out)) == NULL) { adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } strncpy(outfile, cp, sizeof(outfile)); unlink_outfile = 1; /* output the message */ output_message_fp (ct, fp_out, outfile); fclose(fp_out); /* * List the message info */ if (listsw) list_all_messages (cts, headsw, sizesw, verbosw, debugsw, dispo); /* Rename composition draft */ snprintf (buffer, sizeof(buffer), "%s.orig", m_backup (compfile)); if (rename (compfile, buffer) == NOTOK) { adios (compfile, "unable to rename comp draft %s to", buffer); } /* Rename output file to take its place */ if (rename (outfile, compfile) == NOTOK) { advise (outfile, "unable to rename output %s to", compfile); rename (buffer, compfile); done (1); } unlink_outfile = 0; free_content (ct); done (0); return 1; } static void NORETURN unlink_done (int status) { /* * Check if we need to remove stray * temporary files. */ if (unlink_infile) (void) m_unlink (infile); if (unlink_outfile) (void) m_unlink (outfile); exit (status); }