void content_error (char *, CT, char *, ...);
void flush_errors (void);
-/* mhlistsbr.c */
-int list_switch (CT, int, int, int, int);
-int list_content (CT, int, int, int, int);
-
/*
* prototypes
*/
static int parse_display_string (CT, char *, int *, int *, int *, int *, char *,
char *, size_t, int multipart);
static int convert_content_charset (CT, char **);
-static int parameter_value (CI, const char *, const char *, const char **);
static void intrser (int);
int fd, int xlist, int xpause, int xstdin, int xtty)
{
pid_t child_id;
- int i;
- char *vec[4], exec[BUFSIZ + sizeof "exec "];
+ int i, vecp;
+ char **vec, *file;
if (debugsw || cracked) {
fflush (stdout);
char prompt[BUFSIZ];
if (ct->c_type == CT_MULTIPART)
- list_content (ct, -1, 1, 0, 0);
+ list_content (ct, -1, 1, 0, 0, 0);
else
- list_switch (ct, -1, 1, 0, 0);
+ list_switch (ct, -1, 1, 0, 0, 0);
if (xpause && isatty (fileno (stdout))) {
int intr;
}
}
- snprintf (exec, sizeof(exec), "exec %s", buffer);
-
- vec[0] = "/bin/sh";
- vec[1] = "-c";
- vec[2] = exec;
- vec[3] = NULL;
+ vec = argsplit(buffer, &file, &vecp);
+ vec[vecp++] = NULL;
fflush (stdout);
if (!xstdin)
dup2 (fd, 0);
close (fd);
- execvp ("/bin/sh", vec);
+ execvp (file, vec);
fprintf (stderr, "unable to exec ");
perror ("/bin/sh");
_exit (-1);
/* NOTREACHED */
default:
+ arglist_free(file, vec);
+
if (!serial) {
ct->c_pid = child_id;
if (xtty)
* if it is not a text part of a multipart/alternative
*/
if (!alternate || ct->c_subtype == TEXT_PLAIN) {
- snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
+ snprintf (buffer, sizeof(buffer), "%%p%s %%F", progsw ? progsw :
moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
cp = (ct->c_showproc = add (buffer, NULL));
return show_content_aux (ct, serial, alternate, cp, NULL);
if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
return NOTOK;
- /* I'm not sure if this is necessary? */
p->c_storage = add (file, NULL);
if (p->c_showproc && !strcmp (p->c_showproc, "true"))
/* default method for message/rfc822 */
if (ct->c_subtype == MESSAGE_RFC822) {
- cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
+ cp = (ct->c_showproc = add ("%pecho -file %F", NULL));
return show_content_aux (ct, serial, alternate, cp, NULL);
}
case 'a':
/* insert parameters from Content-Type field */
{
- char **ap, **ep;
+ PM pm;
char *s = "";
- for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+ for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+ snprintf (bp, buflen, "%s%s=\"%s\"", s, pm->pm_name,
+ get_param_value(pm, '?'));
len = strlen (bp);
bp += len;
buflen -= len;
for (part = m->mp_parts; part; part = part->mp_next) {
p = part->mp_part;
- snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
+ snprintf (bp, buflen, "%s%s", s, p->c_storage);
len = strlen (bp);
bp += len;
buflen -= len;
s = " ";
}
- /* set our starting pointer back to bp, to avoid
- * requoting the filenames we just added
- */
- pp = bp;
} else {
/* insert filename containing content */
- snprintf (bp, buflen, "'%s'", file);
+ snprintf (bp, buflen, "%s", file);
+
+ /*
+ * Old comments below are left here for posterity.
+ * This was/is tricky.
+ */
/* since we've quoted the file argument, set things up
* to look past it, to avoid problems with the quoting
* logic below. (I know, I should figure out what's
* broken with the quoting logic, but..)
*/
- len = strlen(bp);
- buflen -= len;
- bp += len;
- pp = bp;
+ /*
+ * Here's the email that submitted the patch with
+ * the comment above:
+ * https://www.mail-archive.com/nmh-workers@mhost.com/
+ * msg00288.html
+ * I can't tell from that exactly what was broken,
+ * beyond misquoting of the filename. The profile
+ * had appearances of %F both with and without quotes.
+ * The unquoted ones should have been quoted by the
+ * code below.
+ * The fix was to always quote the filename. But
+ * that broke '%F' because it expanded to ''filename''.
+ */
+ /*
+ * Old comments above are left here for posterity.
+ * The quoting below should work properly now.
+ */
}
break;
goto raw;
case '{' : {
- static const char param[] = "charset";
- const char *value;
- const int found = parameter_value (ci, param, cp, &value);
-
- if (found == OK) {
- /* Because it'll get incremented in the next iteration,
- just increment by 1 for the '{'. */
- cp += strlen(param) + 1;
-
- /* cp points to the param and it's set in the
- Content-Type header. */
- strncpy (bp, value, buflen);
- break;
- } else if (found == 1) {
- /* cp points to the param and it's not set in the
- Content-Type header, so skip it. */
- cp += strlen(param) + 1;
+ const char *closing_brace = strchr(cp, '}');
- if (*cp == '\0') {
- break;
+ if (closing_brace) {
+ const size_t param_len = closing_brace - cp - 1;
+ char *param = mh_xmalloc(param_len + 1);
+ char *value;
+
+ (void) strncpy(param, cp + 1, param_len);
+ param[param_len] = '\0';
+ value = get_param(ci->ci_first_pm, param, '?', 0);
+ free(param);
+
+ cp += param_len + 1; /* Skip both braces, too. */
+
+ if (value) {
+ /* %{param} is set in the Content-Type header.
+ After the break below, quote it if necessary. */
+ (void) strncpy(bp, value, buflen);
+ free(value);
} else {
- if (*(cp + 1) == '\0') {
- break;
- } else {
- ++cp;
- /* Increment cp again so that the last
- character of the %{} token isn't output
- after falling thru below. */
- ++cp;
- }
+ /* %{param} not found, so skip it completely. cp
+ was advanced above. */
+ continue;
}
} else {
- /* cp points to an unrecognized parameter. Output
- it as-is, starting here with the "%{". */
- *bp++ = '%';
- *bp++ = '{';
- *bp = '\0';
- buflen -= 2;
- break;
+ /* This will get confused if there are multiple %{}'s,
+ but its real purpose is to avoid doing bad things
+ above if a closing brace wasn't found. */
+ admonish(NULL,
+ "no closing brace for display string escape %s",
+ cp);
}
-
- /* No parameter was found, so fall thru to default to
- output the rest of the text as-is. */
+ break;
}
default:
len = strlen (bp);
bp += len;
buflen -= len;
+ *bp = '\0';
/* Did we actually insert something? */
if (bp != pp) {
/* Insert single quote if not inside quotes already */
if (!quoted && buflen) {
len = strlen (pp);
- memmove (pp + 1, pp, len);
+ memmove (pp + 1, pp, len+1);
*pp++ = '\'';
buflen--;
bp++;
+ quoted = 1;
}
/* Escape existing quotes */
while ((pp = strchr (pp, '\'')) && buflen > 3) {
len = strlen (pp++);
- memmove (pp + 3, pp, len);
- *pp++ = '\\';
- *pp++ = '\'';
- *pp++ = '\'';
- buflen -= 3;
- bp += 3;
+ if (quoted) {
+ /* Quoted. Let this quote close that quoting.
+ Insert an escaped quote to replace it and
+ another quote to reopen quoting, which will be
+ closed below. */
+ memmove (pp + 2, pp, len);
+ *pp++ = '\\';
+ *pp++ = '\'';
+ buflen -= 2;
+ bp += 2;
+ quoted = 0;
+ } else {
+ /* Not quoted. This should not be reached with
+ the current code, but handle the condition
+ in case the code changes. Just escape the
+ quote. */
+ memmove (pp, pp-1, len+1);
+ *(pp++-1) = '\\';
+ buflen -= 1;
+ bp += 1;
+ }
}
/* If pp is still set, that means we ran out of space. */
if (pp)
buflen = 0;
- if (!quoted && buflen) {
- *bp++ = '\'';
- *bp = '\0';
- buflen--;
+ /* Close quoting. */
+ if (quoted && buflen) {
+ /* See if we need to close the quote by looking
+ for an odd number of unescaped close quotes in
+ the remainder of the display string. */
+ int found_quote = 0, escaped = 0;
+ char *c;
+
+ for (c = cp+1; *c; ++c) {
+ if (*c == '\\') {
+ escaped = ! escaped;
+ } else {
+ if (escaped) {
+ escaped = 0;
+ } else {
+ if (*c == '\'') {
+ found_quote = ! found_quote;
+ }
+ }
+ }
+ }
+ if (! found_quote) {
+ *bp++ = '\'';
+ buflen--;
+ quoted = 0;
+ }
}
}
} else {
raw:
*bp++ = *cp;
- *bp = '\0';
buflen--;
if (*cp == '\'')
quoted = !quoted;
}
+
+ *bp = '\0';
}
if (buflen <= 0 ||
++*message_mods;
- /* Update ci_attrs. */
- src_charset = dest_charset;
-
/* Update ct->c_ctline. */
if (ct->c_ctline) {
- char *ctline =
- update_attr (ct->c_ctline, "charset=", dest_charset);
+ char *ctline = concat(" ", ct->c_ctinfo.ci_type, "/",
+ ct->c_ctinfo.ci_subtype, NULL);
+ char *outline;
+
+ replace_param(&ct->c_ctinfo.ci_first_pm,
+ &ct->c_ctinfo.ci_last_pm, "charset",
+ dest_charset, 0);
+ outline = output_params(strlen(TYPE_FIELD) + 1 + strlen(ctline),
+ ct->c_ctinfo.ci_first_pm, NULL, 0);
+ if (outline) {
+ ctline = add(outline, ctline);
+ free(outline);
+ }
free (ct->c_ctline);
ct->c_ctline = ctline;
/* 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_charset);
- char *ctline = concat (ctline_less_newline, "\n", NULL);
- free (ctline_less_newline);
+ char *ctline = concat (ct->c_ctline, "\n", NULL);
free (hf->value);
hf->value = ctline;
}
-/*
- * Return values:
- * NOTOK if cp doesn't point to {param}
- * OK if cp points to {param} and that attribute exists, and returns
- * its value
- * 1 if cp points to {param} and that attribute doesn't exist
- */
-int
-parameter_value (CI ci, const char *param, const char *cp, const char **value) {
- int found = NOTOK;
- char *braced_param = concat ("{", param, "}", NULL);
-
- if (! strncasecmp(cp, braced_param, strlen (braced_param))) {
- char **ap, **ep;
-
- found = 1;
-
- for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ++ap, ++ep) {
- if (!strcasecmp (*ap, param)) {
- found = OK;
- *value = *ep;
- break;
- }
- }
- }
-
- free (braced_param);
- return found;
-}
-
-
static void
intrser (int i)
{