NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
handler.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <stddef.h>
31 #include <ctype.h>
32 #include <iconv.h>
33 #include <limits.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include "mutt/mutt.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "mutt.h"
45 #include "handler.h"
46 #include "copy.h"
47 #include "enriched.h"
48 #include "filter.h"
49 #include "globals.h"
50 #include "keymap.h"
51 #include "mailcap.h"
52 #include "mutt_attach.h"
53 #include "mutt_logging.h"
54 #include "muttlib.h"
55 #include "ncrypt/ncrypt.h"
56 #include "opcodes.h"
57 #include "options.h"
58 #include "rfc3676.h"
59 #include "state.h"
60 #ifdef ENABLE_NLS
61 #include <libintl.h>
62 #endif
63 
64 /* These Config Variables are only used in handler.c */
72 
73 #define BUFI_SIZE 1000
74 #define BUFO_SIZE 2000
75 
76 #define TXT_HTML 1
77 #define TXT_PLAIN 2
78 #define TXT_ENRICHED 3
79 
87 typedef int (*handler_t)(struct Body *b, struct State *s);
88 
95 static void print_part_line(struct State *s, struct Body *b, int n)
96 {
97  char length[5];
98  mutt_str_pretty_size(length, sizeof(length), b->length);
100  char *charset = mutt_param_get(&b->parameter, "charset");
101  if (n != 0)
102  {
103  state_printf(s, _("[-- Alternative Type #%d: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
104  n, TYPE(b), b->subtype, charset ? "; charset=" : "",
105  charset ? charset : "", ENCODING(b->encoding), length);
106  }
107  else
108  {
109  state_printf(s, _("[-- Type: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
110  TYPE(b), b->subtype, charset ? "; charset=" : "",
111  charset ? charset : "", ENCODING(b->encoding), length);
112  }
113 }
114 
122 static void convert_to_state(iconv_t cd, char *bufi, size_t *l, struct State *s)
123 {
124  char bufo[BUFO_SIZE];
125  const char *ib = NULL;
126  char *ob = NULL;
127  size_t ibl, obl;
128 
129  if (!bufi)
130  {
131  if (cd != (iconv_t)(-1))
132  {
133  ob = bufo;
134  obl = sizeof(bufo);
135  iconv(cd, NULL, NULL, &ob, &obl);
136  if (ob != bufo)
137  state_prefix_put(bufo, ob - bufo, s);
138  }
139  return;
140  }
141 
142  if (cd == (iconv_t)(-1))
143  {
144  state_prefix_put(bufi, *l, s);
145  *l = 0;
146  return;
147  }
148 
149  ib = bufi;
150  ibl = *l;
151  while (true)
152  {
153  ob = bufo;
154  obl = sizeof(bufo);
155  mutt_ch_iconv(cd, &ib, &ibl, &ob, &obl, 0, "?", NULL);
156  if (ob == bufo)
157  break;
158  state_prefix_put(bufo, ob - bufo, s);
159  }
160  memmove(bufi, ib, ibl);
161  *l = ibl;
162 }
163 
171 static void decode_xbit(struct State *s, long len, bool istext, iconv_t cd)
172 {
173  if (!istext)
174  {
175  mutt_file_copy_bytes(s->fp_in, s->fp_out, len);
176  return;
177  }
178 
179  state_set_prefix(s);
180 
181  int c;
182  char bufi[BUFI_SIZE];
183  size_t l = 0;
184  while (((c = fgetc(s->fp_in)) != EOF) && len--)
185  {
186  if ((c == '\r') && len)
187  {
188  const int ch = fgetc(s->fp_in);
189  if (ch == '\n')
190  {
191  c = ch;
192  len--;
193  }
194  else
195  ungetc(ch, s->fp_in);
196  }
197 
198  bufi[l++] = c;
199  if (l == sizeof(bufi))
200  convert_to_state(cd, bufi, &l, s);
201  }
202 
203  convert_to_state(cd, bufi, &l, s);
204  convert_to_state(cd, 0, 0, s);
205 
207 }
208 
216 static int qp_decode_triple(char *s, char *d)
217 {
218  /* soft line break */
219  if ((s[0] == '=') && (s[1] == '\0'))
220  return 1;
221 
222  /* quoted-printable triple */
223  if ((s[0] == '=') && isxdigit((unsigned char) s[1]) && isxdigit((unsigned char) s[2]))
224  {
225  *d = (hexval(s[1]) << 4) | hexval(s[2]);
226  return 0;
227  }
228 
229  /* something else */
230  return -1;
231 }
232 
240 static void qp_decode_line(char *dest, char *src, size_t *l, int last)
241 {
242  char *d = NULL, *s = NULL;
243  char c = 0;
244 
245  int kind = -1;
246  bool soft = false;
247 
248  /* decode the line */
249 
250  for (d = dest, s = src; *s;)
251  {
252  switch ((kind = qp_decode_triple(s, &c)))
253  {
254  case 0:
255  *d++ = c;
256  s += 3;
257  break; /* qp triple */
258  case -1:
259  *d++ = *s++;
260  break; /* single character */
261  case 1:
262  soft = true;
263  s++;
264  break; /* soft line break */
265  }
266  }
267 
268  if (!soft && (last == '\n'))
269  {
270  /* neither \r nor \n as part of line-terminating CRLF
271  * may be qp-encoded, so remove \r and \n-terminate;
272  * see RFC2045, sect. 6.7, (1): General 8bit representation */
273  if ((kind == 0) && (c == '\r'))
274  *(d - 1) = '\n';
275  else
276  *d++ = '\n';
277  }
278 
279  *d = '\0';
280  *l = d - dest;
281 }
282 
308 static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd)
309 {
310  char line[256];
311  char decline[512];
312  size_t l = 0;
313  size_t l3;
314 
315  if (istext)
316  state_set_prefix(s);
317 
318  while (len > 0)
319  {
320  /* It's ok to use a fixed size buffer for input, even if the line turns
321  * out to be longer than this. Just process the line in chunks. This
322  * really shouldn't happen according the MIME spec, since Q-P encoded
323  * lines are at most 76 characters, but we should be liberal about what
324  * we accept. */
325  if (!fgets(line, MIN((ssize_t) sizeof(line), len + 1), s->fp_in))
326  break;
327 
328  size_t linelen = strlen(line);
329  len -= linelen;
330 
331  /* inspect the last character we read so we can tell if we got the
332  * entire line. */
333  const int last = (linelen != 0) ? line[linelen - 1] : 0;
334 
335  /* chop trailing whitespace if we got the full line */
336  if (last == '\n')
337  {
338  while ((linelen > 0) && IS_SPACE(line[linelen - 1]))
339  linelen--;
340  line[linelen] = '\0';
341  }
342 
343  /* decode and do character set conversion */
344  qp_decode_line(decline + l, line, &l3, last);
345  l += l3;
346  convert_to_state(cd, decline, &l, s);
347  }
348 
349  convert_to_state(cd, 0, 0, s);
351 }
352 
358 static unsigned char decode_byte(char ch)
359 {
360  if ((ch < 32) || (ch > 95))
361  return 0;
362  return ch - 32;
363 }
364 
372 static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
373 {
374  char tmps[128];
375  char *pt = NULL;
376  char bufi[BUFI_SIZE];
377  size_t k = 0;
378 
379  if (istext)
380  state_set_prefix(s);
381 
382  while (len > 0)
383  {
384  if (!fgets(tmps, sizeof(tmps), s->fp_in))
385  return;
386  len -= mutt_str_strlen(tmps);
387  if (mutt_str_startswith(tmps, "begin ", CASE_MATCH))
388  break;
389  }
390  while (len > 0)
391  {
392  if (!fgets(tmps, sizeof(tmps), s->fp_in))
393  return;
394  len -= mutt_str_strlen(tmps);
395  if (mutt_str_startswith(tmps, "end", CASE_MATCH))
396  break;
397  pt = tmps;
398  const unsigned char linelen = decode_byte(*pt);
399  pt++;
400  for (unsigned char c = 0; c < linelen;)
401  {
402  for (char l = 2; l <= 6; l += 2)
403  {
404  char out = decode_byte(*pt) << l;
405  pt++;
406  out |= (decode_byte(*pt) >> (6 - l));
407  bufi[k++] = out;
408  c++;
409  if (c == linelen)
410  break;
411  }
412  convert_to_state(cd, bufi, &k, s);
413  pt++;
414  }
415  }
416 
417  convert_to_state(cd, bufi, &k, s);
418  convert_to_state(cd, 0, 0, s);
419 
421 }
422 
433 static bool is_mmnoask(const char *buf)
434 {
435  const char *val = mutt_str_getenv("MM_NOASK");
436  if (!val)
437  return false;
438 
439  char *p = NULL;
440  char tmp[1024];
441  char *q = NULL;
442 
443  if (mutt_str_strcmp(val, "1") == 0)
444  return true;
445 
446  mutt_str_strfcpy(tmp, val, sizeof(tmp));
447  p = tmp;
448 
449  while ((p = strtok(p, ",")))
450  {
451  q = strrchr(p, '/');
452  if (q)
453  {
454  if (q[1] == '*')
455  {
456  if (mutt_str_strncasecmp(buf, p, q - p) == 0)
457  return true;
458  }
459  else
460  {
461  if (mutt_str_strcasecmp(buf, p) == 0)
462  return true;
463  }
464  }
465  else
466  {
467  const size_t plen = mutt_str_startswith(buf, p, CASE_IGNORE);
468  if ((plen != 0) && (buf[plen] == '/'))
469  return true;
470  }
471 
472  p = NULL;
473  }
474 
475  return false;
476 }
477 
484 static bool is_autoview(struct Body *b)
485 {
486  char type[128];
487  bool is_av = false;
488 
489  snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
490 
491  if (C_ImplicitAutoview)
492  {
493  /* $implicit_autoview is essentially the same as "auto_view *" */
494  is_av = true;
495  }
496  else
497  {
498  /* determine if this type is on the user's auto_view list */
499  mutt_check_lookup_list(b, type, sizeof(type));
500  struct ListNode *np = NULL;
501  STAILQ_FOREACH(np, &AutoViewList, entries)
502  {
503  int i = mutt_str_strlen(np->data) - 1;
504  if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
505  (mutt_str_strncasecmp(type, np->data, i) == 0)) ||
506  (mutt_str_strcasecmp(type, np->data) == 0))
507  {
508  is_av = true;
509  break;
510  }
511  }
512 
513  if (is_mmnoask(type))
514  is_av = true;
515  }
516 
517  /* determine if there is a mailcap entry suitable for auto_view
518  *
519  * @warning type is altered by this call as a result of 'mime_lookup' support */
520  if (is_av)
521  return mailcap_lookup(b, type, NULL, MUTT_MC_AUTOVIEW);
522 
523  return false;
524 }
525 
529 static int autoview_handler(struct Body *a, struct State *s)
530 {
531  struct MailcapEntry *entry = mailcap_entry_new();
532  char buf[1024];
533  char type[256];
534  struct Buffer *cmd = mutt_buffer_pool_get();
535  struct Buffer *tempfile = mutt_buffer_pool_get();
536  char *fname = NULL;
537  FILE *fp_in = NULL;
538  FILE *fp_out = NULL;
539  FILE *fp_err = NULL;
540  pid_t pid;
541  int rc = 0;
542 
543  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
544  mailcap_lookup(a, type, entry, MUTT_MC_AUTOVIEW);
545 
546  fname = mutt_str_strdup(a->filename);
547  mutt_file_sanitize_filename(fname, true);
548  mailcap_expand_filename(entry->nametemplate, fname, tempfile);
549  FREE(&fname);
550 
551  if (entry->command)
552  {
553  mutt_buffer_strcpy(cmd, entry->command);
554 
555  /* mailcap_expand_command returns 0 if the file is required */
556  bool piped = mailcap_expand_command(a, mutt_b2s(tempfile), type, cmd);
557 
558  if (s->flags & MUTT_DISPLAY)
559  {
561  state_printf(s, _("[-- Autoview using %s --]\n"), mutt_b2s(cmd));
562  mutt_message(_("Invoking autoview command: %s"), mutt_b2s(cmd));
563  }
564 
565  fp_in = mutt_file_fopen(mutt_b2s(tempfile), "w+");
566  if (!fp_in)
567  {
568  mutt_perror("fopen");
569  mailcap_entry_free(&entry);
570  rc = -1;
571  goto cleanup;
572  }
573 
574  mutt_file_copy_bytes(s->fp_in, fp_in, a->length);
575 
576  if (!piped)
577  {
578  mutt_file_fclose(&fp_in);
579  pid = mutt_create_filter(mutt_b2s(cmd), NULL, &fp_out, &fp_err);
580  }
581  else
582  {
583  unlink(mutt_b2s(tempfile));
584  fflush(fp_in);
585  rewind(fp_in);
586  pid = mutt_create_filter_fd(mutt_b2s(cmd), NULL, &fp_out, &fp_err,
587  fileno(fp_in), -1, -1);
588  }
589 
590  if (pid < 0)
591  {
592  mutt_perror(_("Can't create filter"));
593  if (s->flags & MUTT_DISPLAY)
594  {
596  state_printf(s, _("[-- Can't run %s. --]\n"), mutt_b2s(cmd));
597  }
598  rc = -1;
599  goto bail;
600  }
601 
602  if (s->prefix)
603  {
604  while (fgets(buf, sizeof(buf), fp_out))
605  {
606  state_puts(s->prefix, s);
607  state_puts(buf, s);
608  }
609  /* check for data on stderr */
610  if (fgets(buf, sizeof(buf), fp_err))
611  {
612  if (s->flags & MUTT_DISPLAY)
613  {
615  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_b2s(cmd));
616  }
617 
618  state_puts(s->prefix, s);
619  state_puts(buf, s);
620  while (fgets(buf, sizeof(buf), fp_err))
621  {
622  state_puts(s->prefix, s);
623  state_puts(buf, s);
624  }
625  }
626  }
627  else
628  {
629  mutt_file_copy_stream(fp_out, s->fp_out);
630  /* Check for stderr messages */
631  if (fgets(buf, sizeof(buf), fp_err))
632  {
633  if (s->flags & MUTT_DISPLAY)
634  {
636  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_b2s(cmd));
637  }
638 
639  state_puts(buf, s);
640  mutt_file_copy_stream(fp_err, s->fp_out);
641  }
642  }
643 
644  bail:
645  mutt_file_fclose(&fp_out);
646  mutt_file_fclose(&fp_err);
647 
648  mutt_wait_filter(pid);
649  if (piped)
650  mutt_file_fclose(&fp_in);
651  else
652  mutt_file_unlink(mutt_b2s(tempfile));
653 
654  if (s->flags & MUTT_DISPLAY)
656  }
657 
658 cleanup:
659  mailcap_entry_free(&entry);
660 
662  mutt_buffer_pool_release(&tempfile);
663 
664  return rc;
665 }
666 
675 static int text_plain_handler(struct Body *b, struct State *s)
676 {
677  char *buf = NULL;
678  size_t sz = 0;
679 
680  while ((buf = mutt_file_read_line(buf, &sz, s->fp_in, NULL, 0)))
681  {
682  if ((mutt_str_strcmp(buf, "-- ") != 0) && C_TextFlowed)
683  {
684  size_t len = mutt_str_strlen(buf);
685  while ((len > 0) && (buf[len - 1] == ' '))
686  buf[--len] = '\0';
687  }
688  if (s->prefix)
689  state_puts(s->prefix, s);
690  state_puts(buf, s);
691  state_putc('\n', s);
692  }
693 
694  FREE(&buf);
695  return 0;
696 }
697 
701 static int message_handler(struct Body *a, struct State *s)
702 {
703  struct stat st;
704  struct Body *b = NULL;
705  LOFF_T off_start;
706  int rc = 0;
707 
708  off_start = ftello(s->fp_in);
709  if (off_start < 0)
710  return -1;
711 
712  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
713  (a->encoding == ENC_UUENCODED))
714  {
715  fstat(fileno(s->fp_in), &st);
716  b = mutt_body_new();
717  b->length = (LOFF_T) st.st_size;
719  }
720  else
721  b = a;
722 
723  if (b->parts)
724  {
725  CopyHeaderFlags chflags = CH_DECODE | CH_FROM;
726  if ((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY | MUTT_PRINTING)) && C_Weed))
727  chflags |= CH_WEED | CH_REORDER;
728  if (s->prefix)
729  chflags |= CH_PREFIX;
730  if (s->flags & MUTT_DISPLAY)
731  chflags |= CH_DISPLAY;
732 
733  mutt_copy_hdr(s->fp_in, s->fp_out, off_start, b->parts->offset, chflags, s->prefix);
734 
735  if (s->prefix)
736  state_puts(s->prefix, s);
737  state_putc('\n', s);
738 
739  rc = mutt_body_handler(b->parts, s);
740  }
741 
742  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
743  (a->encoding == ENC_UUENCODED))
744  {
745  mutt_body_free(&b);
746  }
747 
748  return rc;
749 }
750 
754 static int external_body_handler(struct Body *b, struct State *s)
755 {
756  const char *str = NULL;
757  char strbuf[1024];
758 
759  const char *access_type = mutt_param_get(&b->parameter, "access-type");
760  if (!access_type)
761  {
762  if (s->flags & MUTT_DISPLAY)
763  {
765  state_puts(_("[-- Error: message/external-body has no access-type "
766  "parameter --]\n"),
767  s);
768  return 0;
769  }
770  else
771  return -1;
772  }
773 
774  const char *expiration = mutt_param_get(&b->parameter, "expiration");
775  time_t expire;
776  if (expiration)
777  expire = mutt_date_parse_date(expiration, NULL);
778  else
779  expire = -1;
780 
781  if (mutt_str_strcasecmp(access_type, "x-mutt-deleted") == 0)
782  {
783  if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING))
784  {
785  char pretty_size[10];
786  char *length = mutt_param_get(&b->parameter, "length");
787  if (length)
788  {
789  long size = strtol(length, NULL, 10);
790  mutt_str_pretty_size(pretty_size, sizeof(pretty_size), size);
791  if (expire != -1)
792  {
793  str = ngettext(
794  /* L10N: If the translation of this string is a multi line string, then
795  each line should start with "[-- " and end with " --]".
796  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
797  expands to a date as returned by `mutt_date_parse_date()`.
798 
799  Note: The size argument printed is not the actual number as passed
800  to gettext but the prettified version, e.g. size = 2048 will be
801  printed as 2K. Your language might be sensitive to that: For
802  example although '1K' and '1024' represent the same number your
803  language might inflect the noun 'byte' differently.
804 
805  Sadly, we can't do anything about that at the moment besides
806  passing the precise size in bytes. If you are interested the
807  function responsible for the prettification is
808  mutt_str_pretty_size() in mutt/string.c. */
809  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n"
810  "[-- on %s --]\n",
811  "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n"
812  "[-- on %s --]\n",
813  size);
814  }
815  else
816  {
817  str = ngettext(
818  /* L10N: If the translation of this string is a multi line string, then
819  each line should start with "[-- " and end with " --]".
820  The first "%s/%s" is a MIME type, e.g. "text/plain".
821 
822  Note: The size argument printed is not the actual number as passed
823  to gettext but the prettified version, e.g. size = 2048 will be
824  printed as 2K. Your language might be sensitive to that: For
825  example although '1K' and '1024' represent the same number your
826  language might inflect the noun 'byte' differently.
827 
828  Sadly, we can't do anything about that at the moment besides
829  passing the precise size in bytes. If you are interested the
830  function responsible for the prettification is
831  mutt_str_pretty_size() in mutt/string.c. */
832  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n",
833  "[-- This %s/%s attachment (size %s bytes) has been deleted "
834  "--]\n",
835  size);
836  }
837  }
838  else
839  {
840  pretty_size[0] = '\0';
841  if (expire != -1)
842  {
843  /* L10N: If the translation of this string is a multi line string, then
844  each line should start with "[-- " and end with " --]".
845  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
846  expands to a date as returned by `mutt_date_parse_date()`.
847 
848  Caution: Argument three %3$ is also defined but should not be used
849  in this translation! */
850  str = _("[-- This %s/%s attachment has been deleted --]\n[-- on %4$s "
851  "--]\n");
852  }
853  else
854  {
855  /* L10N: If the translation of this string is a multi line string, then
856  each line should start with "[-- " and end with " --]".
857  The first "%s/%s" is a MIME type, e.g. "text/plain". */
858  str = _("[-- This %s/%s attachment has been deleted --]\n");
859  }
860  }
861 
862  snprintf(strbuf, sizeof(strbuf), str, TYPE(b->parts), b->parts->subtype,
863  pretty_size, expiration);
864  state_attach_puts(strbuf, s);
865  if (b->parts->filename)
866  {
868  state_printf(s, _("[-- name: %s --]\n"), b->parts->filename);
869  }
870 
871  CopyHeaderFlags chflags = CH_DECODE;
872  if (C_Weed)
873  chflags |= CH_WEED | CH_REORDER;
874 
875  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset, chflags, NULL);
876  }
877  }
878  else if (expiration && (expire < mutt_date_epoch()))
879  {
880  if (s->flags & MUTT_DISPLAY)
881  {
882  /* L10N: If the translation of this string is a multi line string, then
883  each line should start with "[-- " and end with " --]".
884  The "%s/%s" is a MIME type, e.g. "text/plain". */
885  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated external source has --]\n[-- expired. --]\n"),
886  TYPE(b->parts), b->parts->subtype);
887  state_attach_puts(strbuf, s);
888 
890  if (C_Weed)
891  chflags |= CH_WEED | CH_REORDER;
892 
893  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset, chflags, NULL);
894  }
895  }
896  else
897  {
898  if (s->flags & MUTT_DISPLAY)
899  {
900  /* L10N: If the translation of this string is a multi line string, then
901  each line should start with "[-- " and end with " --]".
902  The "%s/%s" is a MIME type, e.g. "text/plain". The %s after
903  access-type is an access-type as defined by the MIME RFCs, e.g. "FTP",
904  "LOCAL-FILE", "MAIL-SERVER". */
905  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated access-type %s is unsupported --]\n"),
906  TYPE(b->parts), b->parts->subtype, access_type);
907  state_attach_puts(strbuf, s);
908 
910  if (C_Weed)
911  chflags |= CH_WEED | CH_REORDER;
912 
913  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset, chflags, NULL);
914  }
915  }
916 
917  return 0;
918 }
919 
923 static int alternative_handler(struct Body *a, struct State *s)
924 {
925  struct Body *choice = NULL;
926  struct Body *b = NULL;
927  bool mustfree = false;
928  int rc = 0;
929 
930  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
931  (a->encoding == ENC_UUENCODED))
932  {
933  struct stat st;
934  mustfree = true;
935  fstat(fileno(s->fp_in), &st);
936  b = mutt_body_new();
937  b->length = (long) st.st_size;
939  s->fp_in, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size,
940  (mutt_str_strcasecmp("digest", a->subtype) == 0));
941  }
942  else
943  b = a;
944 
945  a = b;
946 
947  /* First, search list of preferred types */
948  struct ListNode *np = NULL;
949  STAILQ_FOREACH(np, &AlternativeOrderList, entries)
950  {
951  int btlen; /* length of basetype */
952  bool wild; /* do we have a wildcard to match all subtypes? */
953 
954  char *c = strchr(np->data, '/');
955  if (c)
956  {
957  wild = ((c[1] == '*') && (c[2] == '\0'));
958  btlen = c - np->data;
959  }
960  else
961  {
962  wild = true;
963  btlen = mutt_str_strlen(np->data);
964  }
965 
966  if (a->parts)
967  b = a->parts;
968  else
969  b = a;
970  while (b)
971  {
972  const char *bt = TYPE(b);
973  if ((mutt_str_strncasecmp(bt, np->data, btlen) == 0) && (bt[btlen] == 0))
974  {
975  /* the basetype matches */
976  if (wild || (mutt_str_strcasecmp(np->data + btlen + 1, b->subtype) == 0))
977  {
978  choice = b;
979  }
980  }
981  b = b->next;
982  }
983 
984  if (choice)
985  break;
986  }
987 
988  /* Next, look for an autoviewable type */
989  if (!choice)
990  {
991  if (a->parts)
992  b = a->parts;
993  else
994  b = a;
995  while (b)
996  {
997  if (is_autoview(b))
998  choice = b;
999  b = b->next;
1000  }
1001  }
1002 
1003  /* Then, look for a text entry */
1004  if (!choice)
1005  {
1006  if (a->parts)
1007  b = a->parts;
1008  else
1009  b = a;
1010  int type = 0;
1011  while (b)
1012  {
1013  if (b->type == TYPE_TEXT)
1014  {
1015  if ((mutt_str_strcasecmp("plain", b->subtype) == 0) && (type <= TXT_PLAIN))
1016  {
1017  choice = b;
1018  type = TXT_PLAIN;
1019  }
1020  else if ((mutt_str_strcasecmp("enriched", b->subtype) == 0) && (type <= TXT_ENRICHED))
1021  {
1022  choice = b;
1023  type = TXT_ENRICHED;
1024  }
1025  else if ((mutt_str_strcasecmp("html", b->subtype) == 0) && (type <= TXT_HTML))
1026  {
1027  choice = b;
1028  type = TXT_HTML;
1029  }
1030  }
1031  b = b->next;
1032  }
1033  }
1034 
1035  /* Finally, look for other possibilities */
1036  if (!choice)
1037  {
1038  if (a->parts)
1039  b = a->parts;
1040  else
1041  b = a;
1042  while (b)
1043  {
1044  if (mutt_can_decode(b))
1045  choice = b;
1046  b = b->next;
1047  }
1048  }
1049 
1050  if (choice)
1051  {
1052  if (s->flags & MUTT_DISPLAY && !C_Weed)
1053  {
1054  fseeko(s->fp_in, choice->hdr_offset, SEEK_SET);
1055  mutt_file_copy_bytes(s->fp_in, s->fp_out, choice->offset - choice->hdr_offset);
1056  }
1057 
1058  if (mutt_str_strcmp("info", C_ShowMultipartAlternative) == 0)
1059  {
1060  print_part_line(s, choice, 0);
1061  }
1062  mutt_body_handler(choice, s);
1063 
1064  if (mutt_str_strcmp("info", C_ShowMultipartAlternative) == 0)
1065  {
1066  if (a->parts)
1067  b = a->parts;
1068  else
1069  b = a;
1070  int count = 0;
1071  while (b)
1072  {
1073  if (choice != b)
1074  {
1075  count += 1;
1076  if (count == 1)
1077  state_putc('\n', s);
1078 
1079  print_part_line(s, b, count);
1080  }
1081  b = b->next;
1082  }
1083  }
1084  }
1085  else if (s->flags & MUTT_DISPLAY)
1086  {
1087  /* didn't find anything that we could display! */
1088  state_mark_attach(s);
1089  state_puts(_("[-- Error: Could not display any parts of "
1090  "Multipart/Alternative --]\n"),
1091  s);
1092  rc = -1;
1093  }
1094 
1095  if (mustfree)
1096  mutt_body_free(&a);
1097 
1098  return rc;
1099 }
1100 
1105 static int multilingual_handler(struct Body *a, struct State *s)
1106 {
1107  struct Body *b = NULL;
1108  bool mustfree = false;
1109  int rc = 0;
1110 
1112  "RFC8255 >> entering in handler multilingual handler\n");
1113  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1114  (a->encoding == ENC_UUENCODED))
1115  {
1116  struct stat st;
1117  mustfree = true;
1118  fstat(fileno(s->fp_in), &st);
1119  b = mutt_body_new();
1120  b->length = (long) st.st_size;
1122  s->fp_in, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size,
1123  (mutt_str_strcasecmp("digest", a->subtype) == 0));
1124  }
1125  else
1126  b = a;
1127 
1128  a = b;
1129 
1130  if (a->parts)
1131  b = a->parts;
1132  else
1133  b = a;
1134 
1135  if (C_PreferredLanguages)
1136  {
1137  struct Buffer *langs = mutt_buffer_pool_get();
1138  cs_str_string_get(Config, "preferred_languages", langs);
1139  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1140  mutt_b2s(langs));
1141  mutt_buffer_pool_release(&langs);
1142  }
1143 
1144  struct Body *choice = NULL;
1145  struct Body *first_part = NULL;
1146  struct Body *zxx_part = NULL;
1147  struct ListNode *np = NULL;
1148 
1149  STAILQ_FOREACH(np, &C_PreferredLanguages->head, entries)
1150  {
1151  while (b)
1152  {
1153  if (mutt_can_decode(b))
1154  {
1155  if (!first_part)
1156  first_part = b;
1157 
1158  if (b->language && (mutt_str_strcmp("zxx", b->language) == 0))
1159  zxx_part = b;
1160 
1161  mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1162  np->data, b->language);
1163  if (b->language && (mutt_str_strcmp(np->data, b->language) == 0))
1164  {
1165  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1166  np->data, b->language);
1167  choice = b;
1168  break;
1169  }
1170  }
1171 
1172  b = b->next;
1173  }
1174 
1175  if (choice)
1176  break;
1177 
1178  if (a->parts)
1179  b = a->parts;
1180  else
1181  b = a;
1182  }
1183 
1184  if (choice)
1185  mutt_body_handler(choice, s);
1186  else
1187  {
1188  if (zxx_part)
1189  mutt_body_handler(zxx_part, s);
1190  else
1191  mutt_body_handler(first_part, s);
1192  }
1193 
1194  if (mustfree)
1195  mutt_body_free(&a);
1196 
1197  return rc;
1198 }
1199 
1203 static int multipart_handler(struct Body *a, struct State *s)
1204 {
1205  struct Body *b = NULL, *p = NULL;
1206  struct stat st;
1207  int count;
1208  int rc = 0;
1209 
1210  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1211  (a->encoding == ENC_UUENCODED))
1212  {
1213  fstat(fileno(s->fp_in), &st);
1214  b = mutt_body_new();
1215  b->length = (long) st.st_size;
1217  s->fp_in, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size,
1218  (mutt_str_strcasecmp("digest", a->subtype) == 0));
1219  }
1220  else
1221  b = a;
1222 
1223  for (p = b->parts, count = 1; p; p = p->next, count++)
1224  {
1225  if (s->flags & MUTT_DISPLAY)
1226  {
1227  state_mark_attach(s);
1228  if (p->description || p->filename || p->form_name)
1229  {
1230  /* L10N: %s is the attachment description, filename or form_name. */
1231  state_printf(s, _("[-- Attachment #%d: %s --]\n"), count,
1232  p->description ? p->description :
1233  p->filename ? p->filename : p->form_name);
1234  }
1235  else
1236  state_printf(s, _("[-- Attachment #%d --]\n"), count);
1237  print_part_line(s, p, 0);
1238  if (!C_Weed)
1239  {
1240  fseeko(s->fp_in, p->hdr_offset, SEEK_SET);
1241  mutt_file_copy_bytes(s->fp_in, s->fp_out, p->offset - p->hdr_offset);
1242  }
1243  else
1244  state_putc('\n', s);
1245  }
1246 
1247  rc = mutt_body_handler(p, s);
1248  state_putc('\n', s);
1249 
1250  if (rc != 0)
1251  {
1252  mutt_error(_("One or more parts of this message could not be displayed"));
1253  mutt_debug(LL_DEBUG1, "Failed on attachment #%d, type %s/%s\n", count,
1254  TYPE(p), NONULL(p->subtype));
1255  }
1256 
1257  if ((s->flags & MUTT_REPLYING) && C_IncludeOnlyfirst && (s->flags & MUTT_FIRSTDONE))
1258  {
1259  break;
1260  }
1261  }
1262 
1263  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1264  (a->encoding == ENC_UUENCODED))
1265  {
1266  mutt_body_free(&b);
1267  }
1268 
1269  /* make failure of a single part non-fatal */
1270  if (rc < 0)
1271  rc = 1;
1272  return rc;
1273 }
1274 
1284 static int run_decode_and_handler(struct Body *b, struct State *s,
1285  handler_t handler, bool plaintext)
1286 {
1287  char *save_prefix = NULL;
1288  FILE *fp = NULL;
1289  size_t tmplength = 0;
1290  LOFF_T tmpoffset = 0;
1291  int decode = 0;
1292  int rc = 0;
1293 
1294  fseeko(s->fp_in, b->offset, SEEK_SET);
1295 
1296 #ifdef USE_FMEMOPEN
1297  char *temp = NULL;
1298  size_t tempsize = 0;
1299 #endif
1300 
1301  /* see if we need to decode this part before processing it */
1302  if ((b->encoding == ENC_BASE64) || (b->encoding == ENC_QUOTED_PRINTABLE) ||
1303  (b->encoding == ENC_UUENCODED) || (plaintext || mutt_is_text_part(b)))
1304  /* text subtypes may require character set conversion even with 8bit encoding */
1305  {
1306  const int orig_type = b->type;
1307 #ifndef USE_FMEMOPEN
1308  char tempfile[PATH_MAX];
1309 #endif
1310  if (!plaintext)
1311  {
1312  /* decode to a tempfile, saving the original destination */
1313  fp = s->fp_out;
1314 #ifdef USE_FMEMOPEN
1315  s->fp_out = open_memstream(&temp, &tempsize);
1316  if (!s->fp_out)
1317  {
1318  mutt_error(_("Unable to open 'memory stream'"));
1319  mutt_debug(LL_DEBUG1, "Can't open 'memory stream'\n");
1320  return -1;
1321  }
1322 #else
1323  mutt_mktemp(tempfile, sizeof(tempfile));
1324  s->fp_out = mutt_file_fopen(tempfile, "w");
1325  if (!s->fp_out)
1326  {
1327  mutt_error(_("Unable to open temporary file"));
1328  mutt_debug(LL_DEBUG1, "Can't open %s\n", tempfile);
1329  return -1;
1330  }
1331 #endif
1332  /* decoding the attachment changes the size and offset, so save a copy
1333  * of the "real" values now, and restore them after processing */
1334  tmplength = b->length;
1335  tmpoffset = b->offset;
1336 
1337  /* if we are decoding binary bodies, we don't want to prefix each
1338  * line with the prefix or else the data will get corrupted. */
1339  save_prefix = s->prefix;
1340  s->prefix = NULL;
1341 
1342  decode = 1;
1343  }
1344  else
1345  b->type = TYPE_TEXT;
1346 
1347  mutt_decode_attachment(b, s);
1348 
1349  if (decode)
1350  {
1351  b->length = ftello(s->fp_out);
1352  b->offset = 0;
1353 #ifdef USE_FMEMOPEN
1354  /* When running under torify, mutt_file_fclose(&s->fp_out) does not seem to
1355  * update tempsize. On the other hand, fflush does. See
1356  * https://github.com/neomutt/neomutt/issues/440 */
1357  fflush(s->fp_out);
1358 #endif
1359  mutt_file_fclose(&s->fp_out);
1360 
1361  /* restore final destination and substitute the tempfile for input */
1362  s->fp_out = fp;
1363  fp = s->fp_in;
1364 #ifdef USE_FMEMOPEN
1365  if (tempsize)
1366  {
1367  s->fp_in = fmemopen(temp, tempsize, "r");
1368  }
1369  else
1370  { /* fmemopen can't handle zero-length buffers */
1371  s->fp_in = mutt_file_fopen("/dev/null", "r");
1372  }
1373  if (!s->fp_in)
1374  {
1375  mutt_perror(_("failed to re-open 'memory stream'"));
1376  return -1;
1377  }
1378 #else
1379  s->fp_in = fopen(tempfile, "r");
1380  unlink(tempfile);
1381 #endif
1382  /* restore the prefix */
1383  s->prefix = save_prefix;
1384  }
1385 
1386  b->type = orig_type;
1387  }
1388 
1389  /* process the (decoded) body part */
1390  if (handler)
1391  {
1392  rc = handler(b, s);
1393  if (rc != 0)
1394  {
1395  mutt_debug(LL_DEBUG1, "Failed on attachment of type %s/%s\n", TYPE(b),
1396  NONULL(b->subtype));
1397  }
1398 
1399  if (decode)
1400  {
1401  b->length = tmplength;
1402  b->offset = tmpoffset;
1403 
1404  /* restore the original source stream */
1405  mutt_file_fclose(&s->fp_in);
1406 #ifdef USE_FMEMOPEN
1407  FREE(&temp);
1408 #endif
1409  s->fp_in = fp;
1410  }
1411  }
1412  s->flags |= MUTT_FIRSTDONE;
1413 
1414  return rc;
1415 }
1416 
1420 static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
1421 {
1422  struct Body *octetstream = b->parts->next;
1423 
1424  /* clear out any mime headers before the handler, so they can't be spoofed. */
1426  mutt_env_free(&octetstream->mime_headers);
1427 
1428  int rc;
1429  /* Some clients improperly encode the octetstream part. */
1430  if (octetstream->encoding != ENC_7BIT)
1431  rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, 0);
1432  else
1433  rc = crypt_pgp_encrypted_handler(octetstream, s);
1434  b->goodsig |= octetstream->goodsig;
1435 
1436  /* Relocate protected headers onto the multipart/encrypted part */
1437  if (!rc && octetstream->mime_headers)
1438  {
1439  b->mime_headers = octetstream->mime_headers;
1440  octetstream->mime_headers = NULL;
1441  }
1442 
1443  return rc;
1444 }
1445 
1449 static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
1450 {
1451  struct Body *octetstream = b->parts->next->next;
1452 
1453  /* clear out any mime headers before the handler, so they can't be spoofed. */
1455  mutt_env_free(&octetstream->mime_headers);
1456 
1457  /* exchange encodes the octet-stream, so re-run it through the decoder */
1458  int rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, false);
1459  b->goodsig |= octetstream->goodsig;
1460 #ifdef USE_AUTOCRYPT
1461  b->is_autocrypt |= octetstream->is_autocrypt;
1462 #endif
1463 
1464  /* Relocate protected headers onto the multipart/encrypted part */
1465  if (!rc && octetstream->mime_headers)
1466  {
1467  b->mime_headers = octetstream->mime_headers;
1468  octetstream->mime_headers = NULL;
1469  }
1470 
1471  return rc;
1472 }
1473 
1481 void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
1482 {
1483  char buf[5];
1484  int ch, i;
1485  char bufi[BUFI_SIZE];
1486  size_t l = 0;
1487 
1488  buf[4] = '\0';
1489 
1490  if (istext)
1491  state_set_prefix(s);
1492 
1493  while (len > 0)
1494  {
1495  for (i = 0; (i < 4) && (len > 0); len--)
1496  {
1497  ch = fgetc(s->fp_in);
1498  if (ch == EOF)
1499  break;
1500  if ((ch >= 0) && (ch < 128) && ((base64val(ch) != -1) || (ch == '=')))
1501  buf[i++] = ch;
1502  }
1503  if (i != 4)
1504  {
1505  /* "i" may be zero if there is trailing whitespace, which is not an error */
1506  if (i != 0)
1507  mutt_debug(LL_DEBUG2, "didn't get a multiple of 4 chars\n");
1508  break;
1509  }
1510 
1511  const int c1 = base64val(buf[0]);
1512  const int c2 = base64val(buf[1]);
1513  ch = (c1 << 2) | (c2 >> 4);
1514  bufi[l++] = ch;
1515 
1516  if (buf[2] == '=')
1517  break;
1518  const int c3 = base64val(buf[2]);
1519  ch = ((c2 & 0xf) << 4) | (c3 >> 2);
1520  bufi[l++] = ch;
1521 
1522  if (buf[3] == '=')
1523  break;
1524  const int c4 = base64val(buf[3]);
1525  ch = ((c3 & 0x3) << 6) | c4;
1526  bufi[l++] = ch;
1527 
1528  if ((l + 8) >= sizeof(bufi))
1529  convert_to_state(cd, bufi, &l, s);
1530  }
1531 
1532  convert_to_state(cd, bufi, &l, s);
1533  convert_to_state(cd, 0, 0, s);
1534 
1535  state_reset_prefix(s);
1536 }
1537 
1545 int mutt_body_handler(struct Body *b, struct State *s)
1546 {
1547  if (!b || !s)
1548  return -1;
1549 
1550  bool plaintext = false;
1551  handler_t handler = NULL;
1552  handler_t encrypted_handler = NULL;
1553  int rc = 0;
1554 
1555  int oflags = s->flags;
1556 
1557  /* first determine which handler to use to process this part */
1558 
1559  if (is_autoview(b))
1560  {
1561  handler = autoview_handler;
1562  s->flags &= ~MUTT_CHARCONV;
1563  }
1564  else if (b->type == TYPE_TEXT)
1565  {
1566  if (mutt_str_strcasecmp("plain", b->subtype) == 0)
1567  {
1568  /* avoid copying this part twice since removing the transfer-encoding is
1569  * the only operation needed. */
1570  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1571  {
1572  encrypted_handler = crypt_pgp_application_handler;
1573  handler = encrypted_handler;
1574  }
1575  else if (C_ReflowText &&
1576  (mutt_str_strcasecmp("flowed",
1577  mutt_param_get(&b->parameter, "format")) == 0))
1578  {
1579  handler = rfc3676_handler;
1580  }
1581  else
1582  {
1583  handler = text_plain_handler;
1584  }
1585  }
1586  else if (mutt_str_strcasecmp("enriched", b->subtype) == 0)
1587  handler = text_enriched_handler;
1588  else /* text body type without a handler */
1589  plaintext = false;
1590  }
1591  else if (b->type == TYPE_MESSAGE)
1592  {
1593  if (mutt_is_message_type(b->type, b->subtype))
1594  handler = message_handler;
1595  else if (mutt_str_strcasecmp("delivery-status", b->subtype) == 0)
1596  plaintext = true;
1597  else if (mutt_str_strcasecmp("external-body", b->subtype) == 0)
1598  handler = external_body_handler;
1599  }
1600  else if (b->type == TYPE_MULTIPART)
1601  {
1602  if ((mutt_str_strcmp("inline", C_ShowMultipartAlternative) != 0) &&
1603  (mutt_str_strcasecmp("alternative", b->subtype) == 0))
1604  {
1605  handler = alternative_handler;
1606  }
1607  else if ((mutt_str_strcmp("inline", C_ShowMultipartAlternative) != 0) &&
1608  (mutt_str_strcasecmp("multilingual", b->subtype) == 0))
1609  {
1610  handler = multilingual_handler;
1611  }
1612  else if ((WithCrypto != 0) && (mutt_str_strcasecmp("signed", b->subtype) == 0))
1613  {
1614  if (!mutt_param_get(&b->parameter, "protocol"))
1615  mutt_error(_("Error: multipart/signed has no protocol"));
1616  else if (s->flags & MUTT_VERIFY)
1617  handler = mutt_signed_handler;
1618  }
1620  {
1621  encrypted_handler = valid_pgp_encrypted_handler;
1622  handler = encrypted_handler;
1623  }
1625  {
1626  encrypted_handler = malformed_pgp_encrypted_handler;
1627  handler = encrypted_handler;
1628  }
1629 
1630  if (!handler)
1631  handler = multipart_handler;
1632 
1633  if ((b->encoding != ENC_7BIT) && (b->encoding != ENC_8BIT) && (b->encoding != ENC_BINARY))
1634  {
1635  mutt_debug(LL_DEBUG1, "Bad encoding type %d for multipart entity, assuming 7 bit\n",
1636  b->encoding);
1637  b->encoding = ENC_7BIT;
1638  }
1639  }
1640  else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1641  {
1642  if (OptDontHandlePgpKeys && (mutt_str_strcasecmp("pgp-keys", b->subtype) == 0))
1643  {
1644  /* pass raw part through for key extraction */
1645  plaintext = true;
1646  }
1647  else if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1648  {
1649  encrypted_handler = crypt_pgp_application_handler;
1650  handler = encrypted_handler;
1651  }
1652  else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(b))
1653  {
1654  encrypted_handler = crypt_smime_application_handler;
1655  handler = encrypted_handler;
1656  }
1657  }
1658 
1659  /* only respect disposition == attachment if we're not
1660  * displaying from the attachment menu (i.e. pager) */
1661  if ((!C_HonorDisposition || ((b->disposition != DISP_ATTACH) || OptViewAttach)) &&
1662  (plaintext || handler))
1663  {
1664  /* Prevent encrypted attachments from being included in replies
1665  * unless $include_encrypted is set. */
1666  if ((s->flags & MUTT_REPLYING) && (s->flags & MUTT_FIRSTDONE) &&
1667  encrypted_handler && !C_IncludeEncrypted)
1668  {
1669  goto cleanup;
1670  }
1671 
1672  rc = run_decode_and_handler(b, s, handler, plaintext);
1673  }
1674  /* print hint to use attachment menu for disposition == attachment
1675  * if we're not already being called from there */
1676  else if ((s->flags & MUTT_DISPLAY) || ((b->disposition == DISP_ATTACH) && !OptViewAttach &&
1677  C_HonorDisposition && (plaintext || handler)))
1678  {
1679  const char *str = NULL;
1680  char keystroke[128];
1681  keystroke[0] = '\0';
1682 
1683  if (!OptViewAttach)
1684  {
1685  if (km_expand_key(keystroke, sizeof(keystroke),
1686  km_find_func(MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1687  {
1689  {
1690  /* L10N: Caution: Arguments %1$s and %2$s are also defined but should
1691  not be used in this translation!
1692  %3$s expands to a keystroke/key binding, e.g. 'v'. */
1693  str = _(
1694  "[-- This is an attachment (use '%3$s' to view this part) --]\n");
1695  }
1696  else
1697  {
1698  /* L10N: %s/%s is a MIME type, e.g. "text/plain".
1699  The last %s expands to a keystroke/key binding, e.g. 'v'. */
1700  str =
1701  _("[-- %s/%s is unsupported (use '%s' to view this part) --]\n");
1702  }
1703  }
1704  else
1705  {
1707  {
1708  str = _("[-- This is an attachment (need 'view-attachments' bound to "
1709  "key) --]\n");
1710  }
1711  else
1712  {
1713  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1714  str = _("[-- %s/%s is unsupported (need 'view-attachments' bound to "
1715  "key) --]\n");
1716  }
1717  }
1718  }
1719  else
1720  {
1722  str = _("[-- This is an attachment --]\n");
1723  else
1724  {
1725  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1726  str = _("[-- %s/%s is unsupported --]\n");
1727  }
1728  }
1729  state_mark_attach(s);
1730  state_printf(s, str, TYPE(b), b->subtype, keystroke);
1731  }
1732 
1733 cleanup:
1734  s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
1735  if (rc != 0)
1736  {
1737  mutt_debug(LL_DEBUG1, "Bailing on attachment of type %s/%s\n", TYPE(b),
1738  NONULL(b->subtype));
1739  }
1740 
1741  return rc;
1742 }
1743 
1749 bool mutt_can_decode(struct Body *a)
1750 {
1751  if (is_autoview(a))
1752  return true;
1753  else if (a->type == TYPE_TEXT)
1754  return true;
1755  else if (a->type == TYPE_MESSAGE)
1756  return true;
1757  else if (a->type == TYPE_MULTIPART)
1758  {
1759  if (WithCrypto)
1760  {
1761  if ((mutt_str_strcasecmp(a->subtype, "signed") == 0) ||
1762  (mutt_str_strcasecmp(a->subtype, "encrypted") == 0))
1763  {
1764  return true;
1765  }
1766  }
1767 
1768  for (struct Body *b = a->parts; b; b = b->next)
1769  {
1770  if (mutt_can_decode(b))
1771  return true;
1772  }
1773  }
1774  else if ((WithCrypto != 0) && (a->type == TYPE_APPLICATION))
1775  {
1776  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(a))
1777  return true;
1779  return true;
1780  }
1781 
1782  return false;
1783 }
1784 
1790 void mutt_decode_attachment(struct Body *b, struct State *s)
1791 {
1792  int istext = mutt_is_text_part(b);
1793  iconv_t cd = (iconv_t)(-1);
1794 
1795  if (istext && s->flags & MUTT_CHARCONV)
1796  {
1797  char *charset = mutt_param_get(&b->parameter, "charset");
1798  if (!charset && C_AssumedCharset)
1799  charset = mutt_ch_get_default_charset();
1800  if (charset && C_Charset)
1802  }
1803  else if (istext && b->charset)
1805 
1806  fseeko(s->fp_in, b->offset, SEEK_SET);
1807  switch (b->encoding)
1808  {
1809  case ENC_QUOTED_PRINTABLE:
1810  decode_quoted(s, b->length,
1811  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1813  cd);
1814  break;
1815  case ENC_BASE64:
1816  mutt_decode_base64(s, b->length,
1817  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1819  cd);
1820  break;
1821  case ENC_UUENCODED:
1822  decode_uuencoded(s, b->length,
1823  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1825  cd);
1826  break;
1827  default:
1828  decode_xbit(s, b->length,
1829  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1831  cd);
1832  break;
1833  }
1834 
1835  if (cd != (iconv_t)(-1))
1836  iconv_close(cd);
1837 }
pid_t mutt_create_filter(const char *s, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:217
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:410
char * C_AssumedCharset
Config: If a message is missing a character set, assume this character set.
Definition: charset.c:53
A mailcap entry.
Definition: mailcap.h:37
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email&#39;s attachment.
Definition: handler.c:1790
int state_printf(struct State *s, const char *fmt,...)
Write a formatted string to the State.
Definition: state.c:147
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:428
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
static void decode_xbit(struct State *s, long len, bool istext, iconv_t cd)
Decode xbit-encoded text.
Definition: handler.c:171
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
Manage keymappings.
#define NONULL(x)
Definition: string2.h:37
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:63
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1383
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:100
static bool is_autoview(struct Body *b)
Should email body be filtered by mailcap.
Definition: handler.c:484
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
static int alternative_handler(struct Body *a, struct State *s)
Handler for multipart alternative emails - Implements handler_t.
Definition: handler.c:923
static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
Decode uuencoded text.
Definition: handler.c:372
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
Structs that make up an email.
Rich text handler.
Mailcap autoview field.
Definition: mailcap.h:61
int(* handler_t)(struct Body *b, struct State *s)
typedef handler_t - Manage a PGP or S/MIME encrypted MIME part
Definition: handler.c:87
int crypt_pgp_application_handler(struct Body *m, struct State *s)
Wrapper for CryptModuleSpecs::application_handler()
Definition: cryptglue.c:226
7-bit text
Definition: mime.h:49
#define mutt_message(...)
Definition: logging.h:83
int mutt_signed_handler(struct Body *a, struct State *s)
Verify a "multipart/signed" body - Implements handler_t.
Definition: crypt.c:1119
#define TXT_HTML
Definition: handler.c:76
Pass files through external commands (filters)
static size_t plen
Length of cached packet.
Definition: pgppacket.c:38
NeoMutt Logging.
static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd)
Decode an attachment encoded with quoted-printable.
Definition: handler.c:308
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:111
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:192
RFC1524 Mailcap routines.
String manipulation buffer.
Definition: buffer.h:33
char * prefix
String to add to the beginning of each output line.
Definition: state.h:48
#define state_reset_prefix(state)
Definition: state.h:53
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
Is this a valid multi-part encrypted message?
Definition: crypt.c:477
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:55
#define _(a)
Definition: message.h:28
WHERE struct ConfigSet * Config
Wrapper around the user&#39;s config settings.
Definition: globals.h:40
bool is_autocrypt
Flag autocrypt-decrypted messages for replying.
Definition: body.h:85
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:662
void state_prefix_put(const char *buf, size_t buflen, struct State *s)
Write a prefixed fixed-string to the State.
Definition: state.c:165
int text_enriched_handler(struct Body *a, struct State *s)
Handler for enriched text - Implements handler_t.
Definition: enriched.c:460
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
parse a multipart structure
Definition: parse.c:1454
struct Body * next
next attachment in the list
Definition: body.h:53
8-bit text
Definition: mime.h:50
FILE * fp_out
File to write to.
Definition: state.h:47
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
String list.
Definition: slist.h:43
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:459
All user-callable functions.
static unsigned char decode_byte(char ch)
Decode a uuencoded byte.
Definition: handler.c:358
int crypt_smime_application_handler(struct Body *m, struct State *s)
Wrapper for CryptModuleSpecs::application_handler()
Definition: cryptglue.c:435
void state_attach_puts(const char *t, struct State *s)
Write a string to the state.
Definition: state.c:64
FILE * fp_in
File to read from.
Definition: state.h:46
void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
Decode base64-encoded text.
Definition: handler.c:1481
The body of an email.
Definition: body.h:34
unsigned int disposition
content-disposition
Definition: body.h:67
Convenience wrapper for the config headers.
int mailcap_expand_command(struct Body *a, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition: mailcap.c:70
Hundreds of global variables to back the user variables.
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1049
void mailcap_expand_filename(const char *nametemplate, const char *oldfile, struct Buffer *newfile)
Expand a new filename from a template or existing filename.
Definition: mailcap.c:517
Some miscellaneous functions.
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
static bool is_mmnoask(const char *buf)
Metamail compatibility: should the attachment be autoviewed?
Definition: handler.c:433
Pager pager (email viewer)
Definition: keymap.h:69
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
parse a Message/RFC822 body
Definition: parse.c:1556
static void qp_decode_line(char *dest, char *src, size_t *l, int last)
Decode a line of quoted-printable text.
Definition: handler.c:240
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
Log at debug level 2.
Definition: logging.h:57
static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for invalid pgp-encrypted emails - Implements handler_t.
Definition: handler.c:1449
struct ListHead head
Definition: slist.h:45
#define CH_WEED
Weed the headers?
Definition: copy.h:52
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
#define TXT_PLAIN
Definition: handler.c:77
#define ENCODING(x)
Definition: mime.h:85
int cs_str_string_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
Get a config item as a string.
Definition: set.c:698
Content is attached.
Definition: mime.h:63
int crypt_pgp_encrypted_handler(struct Body *a, struct State *s)
Wrapper for CryptModuleSpecs::encrypted_handler()
Definition: cryptglue.c:239
WHERE bool OptDontHandlePgpKeys
(pseudo) used to extract PGP keys
Definition: options.h:34
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:109
unsigned int encoding
content-transfer-encoding
Definition: body.h:66
Base-64 encoded text.
Definition: mime.h:52
static int message_handler(struct Body *a, struct State *s)
Handler for message/rfc822 body parts - Implements handler_t.
Definition: handler.c:701
bool C_ReflowText
Config: Reformat paragraphs of &#39;format=flowed&#39; text.
Definition: handler.c:70
char * command
Definition: mailcap.h:39
char * subtype
content-type subtype
Definition: body.h:37
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function&#39;s mapping in a Menu.
Definition: keymap.c:808
size_t mutt_ch_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, const char **inrepls, const char *outrepl, int *iconverrno)
Change the encoding of a string.
Definition: charset.c:612
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:613
#define mutt_b2s(buf)
Definition: buffer.h:41
bool goodsig
Good cryptographic signature.
Definition: body.h:81
#define BUFI_SIZE
Definition: handler.c:73
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:560
#define TXT_ENRICHED
Definition: handler.c:78
const char * line
Definition: common.c:36
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
int mutt_str_strncasecmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:656
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:76
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
#define PATH_MAX
Definition: mutt.h:52
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:437
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:780
#define MUTT_VERIFY
Perform signature verification.
Definition: state.h:33
#define CH_REORDER
Re-order output of headers (specified by &#39;hdr_order&#39;)
Definition: copy.h:58
Type: &#39;text/*&#39;.
Definition: mime.h:38
static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for valid pgp-encrypted emails - Implements handler_t.
Definition: handler.c:1420
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1727
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1749
static int run_decode_and_handler(struct Body *b, struct State *s, handler_t handler, bool plaintext)
Run an appropriate decoder for an email.
Definition: handler.c:1284
static void print_part_line(struct State *s, struct Body *b, int n)
Print a separator for the Mime part.
Definition: handler.c:95
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:53
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
pid_t mutt_create_filter_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:65
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, int flags)
Set up iconv for conversions.
Definition: charset.c:559
#define MUTT_WEED
Weed headers even when not in display mode.
Definition: state.h:35
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:49
RFC3676 Format Flowed routines.
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:515
Ignore case when comparing strings.
Definition: string2.h:68
static int multilingual_handler(struct Body *a, struct State *s)
Handler for multi-lingual emails - Implements handler_t.
Definition: handler.c:1105
bool C_IncludeEncrypted
Config: Whether to include encrypted content when replying.
Definition: handler.c:67
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:619
Handling of email attachments.
API for encryption/signing of emails.
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:238
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
#define hexval(ch)
Definition: mime.h:75
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
void state_mark_attach(struct State *s)
Write a unique marker around content.
Definition: state.c:41
static int qp_decode_triple(char *s, char *d)
Decode a quoted-printable triplet.
Definition: handler.c:216
bool C_HonorDisposition
Config: Don&#39;t display MIME parts inline if they have a disposition of &#39;attachment&#39;.
Definition: handler.c:65
unsigned int type
content-type primary type
Definition: body.h:65
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: ncrypt.h:134
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
Type: &#39;message/*&#39;.
Definition: mime.h:35
#define IS_SPACE(ch)
Definition: string2.h:38
char * nametemplate
Definition: mailcap.h:45
Keep track when processing files.
WHERE bool C_TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:268
#define TYPE(body)
Definition: mime.h:83
#define state_set_prefix(state)
Definition: state.h:52
#define state_puts(str, state)
Definition: state.h:54
#define base64val(ch)
Definition: base64.h:30
char * data
String.
Definition: list.h:35
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
Duplicate the structure of an entire email.
Log at debug level 1.
Definition: logging.h:56
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:329
static int autoview_handler(struct Body *a, struct State *s)
Handler for autoviewable attachments - Implements handler_t.
Definition: handler.c:529
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:268
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
bool C_ImplicitAutoview
Config: Display MIME attachments inline if a &#39;copiousoutput&#39; mailcap entry exists.
Definition: handler.c:66
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
#define mutt_error(...)
Definition: logging.h:84
#define state_putc(str, state)
Definition: state.h:55
Binary.
Definition: mime.h:53
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
Quoted-printable text.
Definition: mime.h:51
static void convert_to_state(iconv_t cd, char *bufi, size_t *l, struct State *s)
Convert text and write it to a file.
Definition: handler.c:122
#define FREE(x)
Definition: memory.h:40
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
char * C_ShowMultipartAlternative
Config: How to display &#39;multipart/alternative&#39; MIME parts.
Definition: handler.c:71
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
char * mutt_ch_get_default_charset(void)
Get the default character set.
Definition: charset.c:434
char * language
content-language (RFC8255)
Definition: body.h:38
Keep track when processing files.
Definition: state.h:44
static int multipart_handler(struct Body *a, struct State *s)
Handler for multipart emails - Implements handler_t.
Definition: handler.c:1203
WHERE bool OptViewAttach
(pseudo) signals that we are viewing attachments
Definition: options.h:53
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:54
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1545
#define MUTT_FIRSTDONE
The first attachment has been done.
Definition: state.h:39
#define BUFO_SIZE
Definition: handler.c:74
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int rfc3676_handler(struct Body *a, struct State *s)
Body handler implementing RFC3676 for format=flowed - Implements handler_t.
Definition: rfc3676.c:316
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
static int external_body_handler(struct Body *b, struct State *s)
Handler for external-body emails - Implements handler_t.
Definition: handler.c:754
bool mailcap_lookup(struct Body *a, char *type, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition: mailcap.c:465
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:583
static int text_plain_handler(struct Body *b, struct State *s)
Handler for plain text - Implements handler_t.
Definition: handler.c:675
int mutt_wait_filter(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:228
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:81
A List node for strings.
Definition: list.h:33
Decide how to display email content.
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
#define WithCrypto
Definition: ncrypt.h:160
#define CH_PREFIX
Quote header using C_IndentString string?
Definition: copy.h:56
struct Slist * C_PreferredLanguages
Config: Preferred languages for multilingual MIME.
Definition: handler.c:69
Convenience wrapper for the library headers.
int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix)
Copy header from one file to another.
Definition: copy.c:76
bool C_IncludeOnlyfirst
Config: Only include the first attachment when replying.
Definition: handler.c:68
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
Type: &#39;application/*&#39;.
Definition: mime.h:33
UUEncoded text.
Definition: mime.h:54
bool C_Weed
Config: Filter headers when displaying/forwarding/printing/replying.
Definition: email_globals.c:40
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: ncrypt.h:135