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