#include <h/utils.h>
#include <h/signals.h>
#include <fcntl.h>
-#ifdef HAVE_ICONV
-# include <iconv.h>
-#endif
#define MHFIXMSG_SWITCHES \
X("decodetext 8bit|7bit", 0, DECODETEXTSW) \
X("nodecodetext", 0, NDECODETEXTSW) \
- X("textcodeset", 0, TEXTCODESETSW) \
- X("notextcodeset", 0, NTEXTCODESETSW) \
+ X("textcharset", 0, TEXTCHARSETSW) \
+ X("notextcharset", 0, NTEXTCHARSETSW) \
X("reformat", 0, REFORMATSW) \
X("noreformat", 0, NREFORMATSW) \
X("replacetextplain", 0, REPLACETEXTPLAINSW) \
#define quitser pipeser
/* mhparse.c */
-extern char *tmp; /* directory to place tmp files */
extern int skip_mp_cte_check; /* flag to InitMultiPart */
extern int suppress_bogus_mp_content_warning; /* flag to InitMultiPart */
extern int bogus_mp_content; /* flag from InitMultiPart */
int reformat;
int replacetextplain;
int decodetext;
- char *textcodeset;
+ char *textcharset;
} fix_transformations;
int mhfixmsgsbr (CT *, const fix_transformations *, char *);
static int fix_boundary (CT *, int *);
static int get_multipart_boundary (CT, char **);
static int replace_boundary (CT, char *, const char *);
-static char *update_attr (char *, const char *, const char *e);
static int fix_multipart_cte (CT, int *);
static int set_ce (CT, int);
static int ensure_text_plain (CT *, CT, int *, int);
static int decode_text_parts (CT, int, int *);
static int content_encoding (CT);
static int strip_crs (CT, int *);
-static int convert_codesets (CT, char *, int *);
-static int convert_codeset (CT, char *, int *);
-static char *content_codeset (CT);
+static int convert_charsets (CT, char *, int *);
static int write_content (CT, char *, char *, int, int);
static int remove_file (char *);
static void report (char *, char *, char *, ...);
-static char *upcase (char *);
static void pipeser (int);
fx.reformat = fx.fixcte = fx.fixboundary = 1;
fx.replacetextplain = 0;
fx.decodetext = CE_8BIT;
- fx.textcodeset = NULL;
+ fx.textcharset = NULL;
- done = freects_done;
-
-#ifdef LOCALE
- setlocale(LC_ALL, "");
-#endif
- invo_name = r1bindex (argv[0], '/');
+ if (nmh_init(argv[0], 1)) { return 1; }
- /* read user profile/context */
- context_read();
+ done = freects_done;
arguments = getarguments (invo_name, argc, argv, 1);
argp = arguments;
case NDECODETEXTSW:
fx.decodetext = 0;
continue;
- case TEXTCODESETSW:
+ case TEXTCHARSETSW:
if (! (cp = *argp++) || (*cp == '-' && cp[1]))
adios (NULL, "missing argument to %s", argp[-2]);
- fx.textcodeset = cp;
+ fx.textcharset = cp;
continue;
- case NTEXTCODESETSW:
- fx.textcodeset = 0;
+ case NTEXTCHARSETSW:
+ fx.textcharset = 0;
continue;
case FIXBOUNDARYSW:
fx.fixboundary = 1;
fclose (fp);
}
- /*
- * Check for storage directory. If specified,
- * then store temporary files there. Else we
- * store them in standard nmh directory.
- */
- if ((cp = context_find (nmhstorage)) && *cp)
- tmp = concat (cp, "/", invo_name, NULL);
- else
- tmp = add (m_maildir (invo_name), NULL);
-
suppress_bogus_mp_content_warning = skip_mp_cte_check = 1;
if (! context_find ("path"))
using_stdin = 1;
- if ((cp = m_mktemp2 (tmp, invo_name, &fd, NULL)) == NULL) {
- adios (NULL, "unable to create temporary file");
+ if ((cp = m_mktemp2 (NULL, invo_name, &fd, NULL)) == NULL) {
+ adios (NULL, "unable to create temporary file in %s",
+ get_temp_dir());
} else {
free (file);
file = add (cp, NULL);
- chmod (file, 0600);
cpydata (STDIN_FILENO, fd, "-", file);
}
if (close (fd)) {
- unlink (file);
+ (void) m_unlink (file);
adios (NULL, "failed to write temporary file");
}
}
status += mhfixmsgsbr (ctp, &fx, outfile);
if (using_stdin) {
- unlink (file);
+ (void) m_unlink (file);
if (! outfile) {
/* Just calling m_backup() unlinks the backup file. */
}
free (outfile);
- free (tmp);
free (file);
/* done is freects_done, which will clean up all of cts. */
modify_inplace = 1;
if ((*ctp)->c_file) {
- outfile = add (m_mktemp2 (tmp, invo_name, NULL, NULL), NULL);
+ char *tempfile;
+ if ((tempfile = m_mktemp2 (NULL, invo_name, NULL, NULL)) == NULL) {
+ adios (NULL, "unable to create temporary file in %s",
+ get_temp_dir());
+ }
+ outfile = add (tempfile, NULL);
} else {
adios (NULL, "missing both input and output filenames\n");
}
if (status == OK && fx->decodetext) {
status = decode_text_parts (*ctp, fx->decodetext, &message_mods);
}
- if (status == OK && fx->textcodeset != NULL) {
- status = convert_codesets (*ctp, fx->textcodeset, &message_mods);
+ if (status == OK && fx->textcharset != NULL) {
+ status = convert_charsets (*ctp, fx->textcharset, &message_mods);
}
if (! (*ctp)->c_umask) {
}
if (modify_inplace) {
- if (status != OK) unlink (outfile);
+ if (status != OK) (void) m_unlink (outfile);
free (outfile);
outfile = NULL;
}
if (get_multipart_boundary (*ct, &part_boundary) == OK) {
char *fixed;
- if ((fixed = m_mktemp2 (tmp, invo_name, NULL, &(*ct)->c_fp))) {
+ if ((fixed = m_mktemp2 (NULL, invo_name, NULL, &(*ct)->c_fp))) {
if (replace_boundary (*ct, fixed, part_boundary) == OK) {
char *filename = add ((*ct)->c_file, NULL);
status = NOTOK;
}
} else {
- advise (NULL, "unable to create temporary file");
+ advise (NULL, "unable to create temporary file in %s",
+ get_temp_dir());
status = NOTOK;
}
}
-/* Change the value of a name=value pair in a header field body.
- If the name isn't there, append them. In any case, a new
- string will be allocated and must be free'd by the caller.
- Trims any trailing newlines. */
-static char *
-update_attr (char *body, const char *name, const char *value) {
- char *bp = nmh_strcasestr (body, name);
- char *new_body;
-
- if (bp) {
- char *other_attrs = strchr (bp, ';');
-
- *(bp + strlen (name)) = '\0';
- new_body = concat (body, "\"", value, "\"", NULL);
-
- if (other_attrs) {
- char *cp;
-
- /* Trim any trailing newlines. */
- for (cp = &other_attrs[strlen (other_attrs) - 1];
- cp > other_attrs && *cp == '\n';
- *cp-- = '\0') continue;
- new_body = add (other_attrs, new_body);
- }
- } else {
- char *cp;
-
- /* Append name/value pair, after first removing a final newline
- and (extraneous) semicolon. */
- if (*(cp = &body[strlen (body) - 1]) == '\n') *cp = '\0';
- if (*(cp = &body[strlen (body) - 1]) == ';') *cp = '\0';
- new_body = concat (body, "; ", name, "\"", value, "\"", NULL);
- }
-
- return new_body;
-}
-
-
static int
fix_multipart_cte (CT ct, int *message_mods) {
int status = OK;
contains the decoded contents. And the decoding function, such
as openQuoted, will have set ...->ce_unlink to 1 so that it will
be unlinked by free_content (). */
- tmp_plain_file = add (m_mktemp2 (tmp, invo_name, NULL, NULL), NULL);
+ char *tempfile;
+
+ if ((tempfile = m_mktemp2 (NULL, invo_name, NULL, NULL)) == NULL) {
+ advise (NULL, "unable to create temporary file in %s",
+ get_temp_dir());
+ }
+ tmp_plain_file = add (tempfile, NULL);
if (reformat_part (tp_part, tmp_plain_file,
tp_part->c_ctinfo.ci_type,
tp_part->c_ctinfo.ci_subtype,
}
free_content (tp_part);
- unlink (tmp_plain_file);
+ (void) m_unlink (tmp_plain_file);
free (tmp_plain_file);
return NULL;
decode_part (CT ct) {
char *tmp_decoded;
int status;
+ char *tempfile;
- tmp_decoded = add (m_mktemp2 (tmp, invo_name, NULL, NULL), NULL);
+ if ((tempfile = m_mktemp2 (NULL, invo_name, NULL, NULL)) == NULL) {
+ adios (NULL, "unable to create temporary file in %s", get_temp_dir());
+ }
+ tmp_decoded = add (tempfile, NULL);
/* The following call will load ct->c_cefile.ce_file with the tmp
filename of the decoded content. tmp_decoded will contain the
encoded output, get rid of that. */
status = output_message (ct, tmp_decoded);
- unlink (tmp_decoded);
+ (void) m_unlink (tmp_decoded);
free (tmp_decoded);
return status;
/* Unlink decoded content tmp file and free its filename to avoid
leaks. The file stream should already have been closed. */
if (ct->c_cefile.ce_unlink) {
- unlink (ct->c_cefile.ce_file);
+ (void) m_unlink (ct->c_cefile.ce_file);
free (ct->c_cefile.ce_file);
ct->c_cefile.ce_file = NULL;
ct->c_cefile.ce_unlink = 0;
static int
charset_encoding (CT ct) {
/* norm_charmap() is case sensitive. */
- char *codeset = upcase (content_codeset (ct));
+ char *charset = upcase (content_charset (ct));
int encoding =
- strcmp (norm_charmap (codeset), "US-ASCII") ? CE_8BIT : CE_7BIT;
+ strcmp (norm_charmap (charset), "US-ASCII") ? CE_8BIT : CE_7BIT;
- free (codeset);
+ free (charset);
return encoding;
}
: ct->c_ctline ? ct->c_ctline
: "");
}
- unlink (ct->c_cefile.ce_file);
+ (void) m_unlink (ct->c_cefile.ce_file);
free (ct->c_cefile.ce_file);
ct->c_cefile.ce_file = NULL;
} else if (ct->c_encoding == CE_QUOTED &&
: ct->c_ctline ? ct->c_ctline
: "");
}
- unlink (ct->c_cefile.ce_file);
+ (void) m_unlink (ct->c_cefile.ce_file);
free (ct->c_cefile.ce_file);
ct->c_cefile.ce_file = NULL;
} else {
static int
strip_crs (CT ct, int *message_mods) {
/* norm_charmap() is case sensitive. */
- char *codeset = upcase (content_codeset (ct));
+ char *charset = upcase (content_charset (ct));
int status = OK;
/* Only strip carriage returns if content is ASCII or another
- codeset that has the same readily recognizable CR followed by a
+ charset that has the same readily recognizable CR followed by a
LF. We can include UTF-8 here because if the high-order bit of
a UTF-8 byte is 0, then it must be a single-byte ASCII
character. */
- if (! strcmp (norm_charmap (codeset), "US-ASCII") ||
- ! strncmp (norm_charmap (codeset), "ISO-8859-", 9) ||
- ! strncmp (norm_charmap (codeset), "UTF-8", 5) ||
- ! strncmp (norm_charmap (codeset), "WINDOWS-12", 10)) {
+ if (! strcmp (norm_charmap (charset), "US-ASCII") ||
+ ! strncmp (norm_charmap (charset), "ISO-8859-", 9) ||
+ ! strncmp (norm_charmap (charset), "UTF-8", 5) ||
+ ! strncmp (norm_charmap (charset), "WINDOWS-12", 10)) {
char **file = NULL;
FILE **fp = NULL;
size_t begin;
if (has_crs) {
int fd;
- char *stripped_content_file =
- add (m_mktemp2 (tmp, invo_name, &fd, NULL), NULL);
+ char *stripped_content_file;
+ char *tempfile = m_mktemp2 (NULL, invo_name, &fd, NULL);
+
+ if (tempfile == NULL) {
+ adios (NULL, "unable to create temporary file in %s",
+ get_temp_dir());
+ }
+ stripped_content_file = add (tempfile, NULL);
/* Strip each CR before a LF from the content. */
fseeko (*fp, begin, SEEK_SET);
if (close (fd)) {
admonish (NULL, "unable to write temporary file %s",
stripped_content_file);
- unlink (stripped_content_file);
+ (void) m_unlink (stripped_content_file);
status = NOTOK;
} else {
/* Replace the decoded file with the converted one. */
if (ct->c_cefile.ce_file) {
if (ct->c_cefile.ce_unlink) {
- unlink (ct->c_cefile.ce_file);
+ (void) m_unlink (ct->c_cefile.ce_file);
}
free (ct->c_cefile.ce_file);
}
}
}
- free (codeset);
+ free (charset);
return status;
}
-char *
-content_codeset (CT ct) {
- const char *const charset = "charset";
- char *default_codeset = NULL;
- CI ctinfo = &ct->c_ctinfo;
- char **ap, **vp;
- char **src_codeset = NULL;
-
- for (ap = ctinfo->ci_attrs, vp = ctinfo->ci_values; *ap; ++ap, ++vp) {
- if (! strcasecmp (*ap, charset)) {
- src_codeset = vp;
- break;
- }
- }
-
- /* RFC 2045, Sec. 5.2: default to us-ascii. */
- if (src_codeset == NULL) src_codeset = &default_codeset;
- if (*src_codeset == NULL) *src_codeset = "US-ASCII";
-
- return *src_codeset;
-}
-
-
static int
-convert_codesets (CT ct, char *dest_codeset, int *message_mods) {
+convert_charsets (CT ct, char *dest_charset, int *message_mods) {
int status = OK;
switch (ct->c_type) {
case CT_TEXT:
if (ct->c_subtype == TEXT_PLAIN) {
- status = convert_codeset (ct, dest_codeset, message_mods);
+ status = convert_charset (ct, dest_charset, message_mods);
+ if (verbosw && status == OK) {
+ report (ct->c_partno, ct->c_file, "convert %s to %s",
+ content_charset(ct), dest_charset);
+ }
}
break;
For now, it gets passed along as-is by InitMultiPart(). */
for (part = m->mp_parts; status == OK && part; part = part->mp_next) {
status =
- convert_codesets (part->mp_part, dest_codeset, message_mods);
+ convert_charsets (part->mp_part, dest_charset, message_mods);
}
break;
}
e = (struct exbody *) ct->c_ctparams;
status =
- convert_codesets (e->eb_content, dest_codeset, message_mods);
+ convert_charsets (e->eb_content, dest_charset, message_mods);
}
break;
}
-static int
-convert_codeset (CT ct, char *dest_codeset, int *message_mods) {
- char *src_codeset = content_codeset (ct);
- int status = OK;
-
- /* norm_charmap() is case sensitive. */
- char *src_codeset_u = upcase (src_codeset);
- char *dest_codeset_u = upcase (dest_codeset);
- int different_codesets =
- strcmp (norm_charmap (src_codeset), norm_charmap (dest_codeset));
-
- free (dest_codeset_u);
- free (src_codeset_u);
-
- if (different_codesets) {
-#ifdef HAVE_ICONV
- iconv_t conv_desc = NULL;
- char *dest;
- int fd = -1;
- char **file = NULL;
- FILE **fp = NULL;
- size_t begin;
- size_t end;
- int opened_input_file = 0;
- char src_buffer[BUFSIZ];
- HF hf;
-
- if ((conv_desc = iconv_open (dest_codeset, src_codeset)) ==
- (iconv_t) -1) {
- advise (NULL, "Can't convert %s to %s", src_codeset, dest_codeset);
- return -1;
- }
-
- dest = add (m_mktemp2 (tmp, invo_name, &fd, NULL), NULL);
-
- if (ct->c_cefile.ce_file) {
- file = &ct->c_cefile.ce_file;
- fp = &ct->c_cefile.ce_fp;
- begin = end = 0;
- } else if (ct->c_file) {
- file = &ct->c_file;
- fp = &ct->c_fp;
- begin = (size_t) ct->c_begin;
- end = (size_t) ct->c_end;
- } /* else no input file: shouldn't happen */
-
- if (file && *file && fp) {
- if (! *fp) {
- if ((*fp = fopen (*file, "r")) == NULL) {
- advise (*file, "unable to open for reading");
- status = NOTOK;
- } else {
- opened_input_file = 1;
- }
- }
- }
-
- if (fp && *fp) {
- size_t inbytes;
- size_t bytes_to_read =
- end > 0 && end > begin ? end - begin : sizeof src_buffer;
-
- fseeko (*fp, begin, SEEK_SET);
- while ((inbytes = fread (src_buffer, 1,
- min (bytes_to_read, sizeof src_buffer),
- *fp)) > 0) {
- char dest_buffer[BUFSIZ];
- char *ib = src_buffer, *ob = dest_buffer;
- size_t outbytes = sizeof dest_buffer;
- size_t outbytes_before = outbytes;
-
- if (end > 0) bytes_to_read -= inbytes;
-
- if (iconv (conv_desc, &ib, &inbytes, &ob, &outbytes) ==
- (size_t) -1) {
- status = NOTOK;
- break;
- } else {
- write (fd, dest_buffer, outbytes_before - outbytes);
- }
- }
-
- if (opened_input_file) {
- fclose (*fp);
- *fp = NULL;
- }
- }
-
- iconv_close (conv_desc);
- close (fd);
-
- if (status == OK) {
- /* Replace the decoded file with the converted one. */
- if (ct->c_cefile.ce_file) {
- if (ct->c_cefile.ce_unlink) {
- unlink (ct->c_cefile.ce_file);
- }
- free (ct->c_cefile.ce_file);
- }
- ct->c_cefile.ce_file = dest;
- ct->c_cefile.ce_unlink = 1;
-
- ++*message_mods;
- if (verbosw) {
- report (ct->c_partno, ct->c_file, "convert %s to %s",
- src_codeset, dest_codeset);
- }
-
- /* Update ci_attrs. */
- src_codeset = dest_codeset;
-
- /* Update ct->c_ctline. */
- if (ct->c_ctline) {
- char *ctline =
- update_attr (ct->c_ctline, "charset=", dest_codeset);
-
- free (ct->c_ctline);
- ct->c_ctline = ctline;
- } /* else no CT line, which is odd */
-
- /* Update Content-Type header field. */
- for (hf = ct->c_first_hf; hf; hf = hf->next) {
- if (! strcasecmp (TYPE_FIELD, hf->name)) {
- char *ctline_less_newline =
- update_attr (hf->value, "charset=", dest_codeset);
- char *ctline = concat (ctline_less_newline, "\n", NULL);
- free (ctline_less_newline);
-
- free (hf->value);
- hf->value = ctline;
- break;
- }
- }
- } else {
- unlink (dest);
- }
-#else /* ! HAVE_ICONV */
- NMH_UNUSED (message_mods);
-
- advise (NULL, "Can't convert %s to %s without iconv", src_codeset,
- dest_codeset);
- status = NOTOK;
-#endif /* ! HAVE_ICONV */
- }
-
- return status;
-}
-
-
static int
write_content (CT ct, char *input_filename, char *outfile, int modify_inplace,
int message_mods) {
}
if (new != -1) close (new);
if (old != -1) close (old);
- unlink (outfile);
+ (void) m_unlink (outfile);
if (i < 0) {
/* The -file argument processing used path() to
} else {
admonish (NULL, "unable to remove input file %s, "
"not modifying it", infile);
- unlink (outfile);
+ (void) m_unlink (outfile);
status = NOTOK;
}
}
} else {
/* No modifications and didn't need the tmp outfile. */
- unlink (outfile);
+ (void) m_unlink (outfile);
}
} else {
/* Output is going to some file. Produce it whether or not
}
-static char *
-upcase (char *str) {
- char *up = cpytrim (str);
- char *cp;
-
- for (cp = up; *cp; ++cp) *cp = toupper ((unsigned char) *cp);
-
- return up;
-}
-
-
static void
pipeser (int i)
{