NeoMutt  2021-02-05-89-gabe350
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/lib.h"
41 #include "config/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "mutt.h"
45 #include "handler.h"
46 #include "ncrypt/lib.h"
47 #include "pager/lib.h"
48 #include "copy.h"
49 #include "enriched.h"
50 #include "keymap.h"
51 #include "mailcap.h"
52 #include "mutt_attach.h"
53 #include "mutt_globals.h"
54 #include "mutt_logging.h"
55 #include "muttlib.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, _("[-- Type: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
104  TYPE(b), b->subtype, charset ? "; charset=" : "",
105  charset ? charset : "", ENCODING(b->encoding), length);
106  }
107  else
108  {
109  state_printf(s, _("[-- Alternative Type #%d: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
110  n, 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(s, bufo, ob - bufo);
138  }
139  return;
140  }
141 
142  if (cd == (iconv_t)(-1))
143  {
144  state_prefix_put(s, bufi, *l);
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(s, bufo, ob - bufo);
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_len(tmps);
387  if (mutt_str_startswith(tmps, "begin "))
388  break;
389  }
390  while (len > 0)
391  {
392  if (!fgets(tmps, sizeof(tmps), s->fp_in))
393  return;
394  len -= mutt_str_len(tmps);
395  if (mutt_str_startswith(tmps, "end"))
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_equal(val, "1"))
444  return true;
445 
446  mutt_str_copy(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_istrn_equal(buf, p, q - p))
457  return true;
458  }
459  else
460  {
461  if (mutt_istr_equal(buf, p))
462  return true;
463  }
464  }
465  else
466  {
467  const size_t plen = mutt_istr_startswith(buf, p);
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[256];
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_len(np->data) - 1;
504  if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
505  mutt_istrn_equal(type, np->data, i)) ||
506  mutt_istr_equal(type, np->data))
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, sizeof(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, sizeof(type), entry, MUTT_MC_AUTOVIEW);
545 
546  fname = mutt_str_dup(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_buffer_string(tempfile), type, cmd);
557 
558  if (s->flags & MUTT_DISPLAY)
559  {
561  state_printf(s, _("[-- Autoview using %s --]\n"), mutt_buffer_string(cmd));
562  mutt_message(_("Invoking autoview command: %s"), mutt_buffer_string(cmd));
563  }
564 
565  fp_in = mutt_file_fopen(mutt_buffer_string(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  unlink(mutt_buffer_string(tempfile));
579  fflush(fp_in);
580  rewind(fp_in);
581  pid = filter_create_fd(mutt_buffer_string(cmd), NULL, &fp_out, &fp_err,
582  fileno(fp_in), -1, -1);
583  }
584  else
585  {
586  mutt_file_fclose(&fp_in);
587  pid = filter_create(mutt_buffer_string(cmd), NULL, &fp_out, &fp_err);
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_buffer_string(cmd));
597  }
598  rc = -1;
599  goto bail;
600  }
601 
602  if (s->prefix)
603  {
604  /* Remove ansi and formatting from autoview output in replies only. The
605  * user may want to see the formatting in the pager, but it shouldn't be
606  * in their quoted reply text too. */
607  struct Buffer *stripped = mutt_buffer_pool_get();
608  while (fgets(buf, sizeof(buf), fp_out))
609  {
610  mutt_buffer_strip_formatting(stripped, buf, false);
611  state_puts(s, s->prefix);
612  state_puts(s, mutt_buffer_string(stripped));
613  }
614  mutt_buffer_pool_release(&stripped);
615 
616  /* check for data on stderr */
617  if (fgets(buf, sizeof(buf), fp_err))
618  {
619  if (s->flags & MUTT_DISPLAY)
620  {
622  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_buffer_string(cmd));
623  }
624 
625  state_puts(s, s->prefix);
626  state_puts(s, buf);
627  while (fgets(buf, sizeof(buf), fp_err))
628  {
629  state_puts(s, s->prefix);
630  state_puts(s, buf);
631  }
632  }
633  }
634  else
635  {
636  mutt_file_copy_stream(fp_out, s->fp_out);
637  /* Check for stderr messages */
638  if (fgets(buf, sizeof(buf), fp_err))
639  {
640  if (s->flags & MUTT_DISPLAY)
641  {
643  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_buffer_string(cmd));
644  }
645 
646  state_puts(s, buf);
647  mutt_file_copy_stream(fp_err, s->fp_out);
648  }
649  }
650 
651  bail:
652  mutt_file_fclose(&fp_out);
653  mutt_file_fclose(&fp_err);
654 
655  filter_wait(pid);
656  if (piped)
657  mutt_file_fclose(&fp_in);
658  else
660 
661  if (s->flags & MUTT_DISPLAY)
663  }
664 
665 cleanup:
666  mailcap_entry_free(&entry);
667 
669  mutt_buffer_pool_release(&tempfile);
670 
671  return rc;
672 }
673 
682 static int text_plain_handler(struct Body *b, struct State *s)
683 {
684  char *buf = NULL;
685  size_t sz = 0;
686 
687  while ((buf = mutt_file_read_line(buf, &sz, s->fp_in, NULL, MUTT_RL_NO_FLAGS)))
688  {
689  if (!mutt_str_equal(buf, "-- ") && C_TextFlowed)
690  {
691  size_t len = mutt_str_len(buf);
692  while ((len > 0) && (buf[len - 1] == ' '))
693  buf[--len] = '\0';
694  }
695  if (s->prefix)
696  state_puts(s, s->prefix);
697  state_puts(s, buf);
698  state_putc(s, '\n');
699  }
700 
701  FREE(&buf);
702  return 0;
703 }
704 
708 static int message_handler(struct Body *a, struct State *s)
709 {
710  struct stat st;
711  struct Body *b = NULL;
712  LOFF_T off_start;
713  int rc = 0;
714 
715  off_start = ftello(s->fp_in);
716  if (off_start < 0)
717  return -1;
718 
719  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
720  (a->encoding == ENC_UUENCODED))
721  {
722  fstat(fileno(s->fp_in), &st);
723  b = mutt_body_new();
724  b->length = (LOFF_T) st.st_size;
726  }
727  else
728  b = a;
729 
730  if (b->parts)
731  {
732  CopyHeaderFlags chflags = CH_DECODE | CH_FROM;
733  if ((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY | MUTT_PRINTING)) && C_Weed))
734  chflags |= CH_WEED | CH_REORDER;
735  if (s->prefix)
736  chflags |= CH_PREFIX;
737  if (s->flags & MUTT_DISPLAY)
738  chflags |= CH_DISPLAY;
739 
740  mutt_copy_hdr(s->fp_in, s->fp_out, off_start, b->parts->offset, chflags, s->prefix, 0);
741 
742  if (s->prefix)
743  state_puts(s, s->prefix);
744  state_putc(s, '\n');
745 
746  rc = mutt_body_handler(b->parts, s);
747  }
748 
749  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
750  (a->encoding == ENC_UUENCODED))
751  {
752  mutt_body_free(&b);
753  }
754 
755  return rc;
756 }
757 
761 static int external_body_handler(struct Body *b, struct State *s)
762 {
763  const char *str = NULL;
764  char strbuf[1024];
765 
766  const char *access_type = mutt_param_get(&b->parameter, "access-type");
767  if (!access_type)
768  {
769  if (s->flags & MUTT_DISPLAY)
770  {
772  state_puts(s, _("[-- Error: message/external-body has no access-type "
773  "parameter --]\n"));
774  return 0;
775  }
776  else
777  return -1;
778  }
779 
780  const char *expiration = mutt_param_get(&b->parameter, "expiration");
781  time_t expire;
782  if (expiration)
783  expire = mutt_date_parse_date(expiration, NULL);
784  else
785  expire = -1;
786 
787  if (mutt_istr_equal(access_type, "x-mutt-deleted"))
788  {
789  if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING))
790  {
791  char pretty_size[10];
792  char *length = mutt_param_get(&b->parameter, "length");
793  if (length)
794  {
795  long size = strtol(length, NULL, 10);
796  mutt_str_pretty_size(pretty_size, sizeof(pretty_size), size);
797  if (expire != -1)
798  {
799  str = ngettext(
800  /* L10N: If the translation of this string is a multi line string, then
801  each line should start with "[-- " and end with " --]".
802  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
803  expands to a date as returned by `mutt_date_parse_date()`.
804 
805  Note: The size argument printed is not the actual number as passed
806  to gettext but the prettified version, e.g. size = 2048 will be
807  printed as 2K. Your language might be sensitive to that: For
808  example although '1K' and '1024' represent the same number your
809  language might inflect the noun 'byte' differently.
810 
811  Sadly, we can't do anything about that at the moment besides
812  passing the precise size in bytes. If you are interested the
813  function responsible for the prettification is
814  mutt_str_pretty_size() in mutt/string.c. */
815  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n"
816  "[-- on %s --]\n",
817  "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n"
818  "[-- on %s --]\n",
819  size);
820  }
821  else
822  {
823  str = ngettext(
824  /* L10N: If the translation of this string is a multi line string, then
825  each line should start with "[-- " and end with " --]".
826  The first "%s/%s" is a MIME type, e.g. "text/plain".
827 
828  Note: The size argument printed is not the actual number as passed
829  to gettext but the prettified version, e.g. size = 2048 will be
830  printed as 2K. Your language might be sensitive to that: For
831  example although '1K' and '1024' represent the same number your
832  language might inflect the noun 'byte' differently.
833 
834  Sadly, we can't do anything about that at the moment besides
835  passing the precise size in bytes. If you are interested the
836  function responsible for the prettification is
837  mutt_str_pretty_size() in mutt/string.c. */
838  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n",
839  "[-- This %s/%s attachment (size %s bytes) has been deleted "
840  "--]\n",
841  size);
842  }
843  }
844  else
845  {
846  pretty_size[0] = '\0';
847  if (expire != -1)
848  {
849  /* L10N: If the translation of this string is a multi line string, then
850  each line should start with "[-- " and end with " --]".
851  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
852  expands to a date as returned by `mutt_date_parse_date()`.
853 
854  Caution: Argument three %3$ is also defined but should not be used
855  in this translation! */
856  str = _("[-- This %s/%s attachment has been deleted --]\n[-- on %4$s "
857  "--]\n");
858  }
859  else
860  {
861  /* L10N: If the translation of this string is a multi line string, then
862  each line should start with "[-- " and end with " --]".
863  The first "%s/%s" is a MIME type, e.g. "text/plain". */
864  str = _("[-- This %s/%s attachment has been deleted --]\n");
865  }
866  }
867 
868  snprintf(strbuf, sizeof(strbuf), str, TYPE(b->parts), b->parts->subtype,
869  pretty_size, expiration);
870  state_attach_puts(s, strbuf);
871  if (b->parts->filename)
872  {
874  state_printf(s, _("[-- name: %s --]\n"), b->parts->filename);
875  }
876 
877  CopyHeaderFlags chflags = CH_DECODE;
878  if (C_Weed)
879  chflags |= CH_WEED | CH_REORDER;
880 
881  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
882  chflags, NULL, 0);
883  }
884  }
885  else if (expiration && (expire < mutt_date_epoch()))
886  {
887  if (s->flags & MUTT_DISPLAY)
888  {
889  /* L10N: If the translation of this string is a multi line string, then
890  each line should start with "[-- " and end with " --]".
891  The "%s/%s" is a MIME type, e.g. "text/plain". */
892  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated external source has --]\n[-- expired. --]\n"),
893  TYPE(b->parts), b->parts->subtype);
894  state_attach_puts(s, strbuf);
895 
897  if (C_Weed)
898  chflags |= CH_WEED | CH_REORDER;
899 
900  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
901  chflags, NULL, 0);
902  }
903  }
904  else
905  {
906  if (s->flags & MUTT_DISPLAY)
907  {
908  /* L10N: If the translation of this string is a multi line string, then
909  each line should start with "[-- " and end with " --]".
910  The "%s/%s" is a MIME type, e.g. "text/plain". The %s after
911  access-type is an access-type as defined by the MIME RFCs, e.g. "FTP",
912  "LOCAL-FILE", "MAIL-SERVER". */
913  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated access-type %s is unsupported --]\n"),
914  TYPE(b->parts), b->parts->subtype, access_type);
915  state_attach_puts(s, strbuf);
916 
918  if (C_Weed)
919  chflags |= CH_WEED | CH_REORDER;
920 
921  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
922  chflags, NULL, 0);
923  }
924  }
925 
926  return 0;
927 }
928 
932 static int alternative_handler(struct Body *a, struct State *s)
933 {
934  struct Body *choice = NULL;
935  struct Body *b = NULL;
936  bool mustfree = false;
937  int rc = 0;
938 
939  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
940  (a->encoding == ENC_UUENCODED))
941  {
942  struct stat st;
943  mustfree = true;
944  fstat(fileno(s->fp_in), &st);
945  b = mutt_body_new();
946  b->length = (long) st.st_size;
947  b->parts =
948  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
949  (long) st.st_size, mutt_istr_equal("digest", a->subtype));
950  }
951  else
952  b = a;
953 
954  a = b;
955 
956  /* First, search list of preferred types */
957  struct ListNode *np = NULL;
958  STAILQ_FOREACH(np, &AlternativeOrderList, entries)
959  {
960  int btlen; /* length of basetype */
961  bool wild; /* do we have a wildcard to match all subtypes? */
962 
963  char *c = strchr(np->data, '/');
964  if (c)
965  {
966  wild = ((c[1] == '*') && (c[2] == '\0'));
967  btlen = c - np->data;
968  }
969  else
970  {
971  wild = true;
972  btlen = mutt_str_len(np->data);
973  }
974 
975  if (a->parts)
976  b = a->parts;
977  else
978  b = a;
979  while (b)
980  {
981  const char *bt = TYPE(b);
982  if (mutt_istrn_equal(bt, np->data, btlen) && (bt[btlen] == 0))
983  {
984  /* the basetype matches */
985  if (wild || mutt_istr_equal(np->data + btlen + 1, b->subtype))
986  {
987  choice = b;
988  }
989  }
990  b = b->next;
991  }
992 
993  if (choice)
994  break;
995  }
996 
997  /* Next, look for an autoviewable type */
998  if (!choice)
999  {
1000  if (a->parts)
1001  b = a->parts;
1002  else
1003  b = a;
1004  while (b)
1005  {
1006  if (is_autoview(b))
1007  choice = b;
1008  b = b->next;
1009  }
1010  }
1011 
1012  /* Then, look for a text entry */
1013  if (!choice)
1014  {
1015  if (a->parts)
1016  b = a->parts;
1017  else
1018  b = a;
1019  int type = 0;
1020  while (b)
1021  {
1022  if (b->type == TYPE_TEXT)
1023  {
1024  if (mutt_istr_equal("plain", b->subtype) && (type <= TXT_PLAIN))
1025  {
1026  choice = b;
1027  type = TXT_PLAIN;
1028  }
1029  else if (mutt_istr_equal("enriched", b->subtype) && (type <= TXT_ENRICHED))
1030  {
1031  choice = b;
1032  type = TXT_ENRICHED;
1033  }
1034  else if (mutt_istr_equal("html", b->subtype) && (type <= TXT_HTML))
1035  {
1036  choice = b;
1037  type = TXT_HTML;
1038  }
1039  }
1040  b = b->next;
1041  }
1042  }
1043 
1044  /* Finally, look for other possibilities */
1045  if (!choice)
1046  {
1047  if (a->parts)
1048  b = a->parts;
1049  else
1050  b = a;
1051  while (b)
1052  {
1053  if (mutt_can_decode(b))
1054  choice = b;
1055  b = b->next;
1056  }
1057  }
1058 
1059  if (choice)
1060  {
1061  if (s->flags & MUTT_DISPLAY && !C_Weed)
1062  {
1063  fseeko(s->fp_in, choice->hdr_offset, SEEK_SET);
1064  mutt_file_copy_bytes(s->fp_in, s->fp_out, choice->offset - choice->hdr_offset);
1065  }
1066 
1068  {
1069  print_part_line(s, choice, 0);
1070  }
1071  mutt_body_handler(choice, s);
1072 
1074  {
1075  if (a->parts)
1076  b = a->parts;
1077  else
1078  b = a;
1079  int count = 0;
1080  while (b)
1081  {
1082  if (choice != b)
1083  {
1084  count += 1;
1085  if (count == 1)
1086  state_putc(s, '\n');
1087 
1088  print_part_line(s, b, count);
1089  }
1090  b = b->next;
1091  }
1092  }
1093  }
1094  else if (s->flags & MUTT_DISPLAY)
1095  {
1096  /* didn't find anything that we could display! */
1097  state_mark_attach(s);
1098  state_puts(s, _("[-- Error: Could not display any parts of "
1099  "Multipart/Alternative --]\n"));
1100  rc = -1;
1101  }
1102 
1103  if (mustfree)
1104  mutt_body_free(&a);
1105 
1106  return rc;
1107 }
1108 
1113 static int multilingual_handler(struct Body *a, struct State *s)
1114 {
1115  struct Body *b = NULL;
1116  bool mustfree = false;
1117  int rc = 0;
1118 
1120  "RFC8255 >> entering in handler multilingual handler\n");
1121  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1122  (a->encoding == ENC_UUENCODED))
1123  {
1124  struct stat st;
1125  mustfree = true;
1126  fstat(fileno(s->fp_in), &st);
1127  b = mutt_body_new();
1128  b->length = (long) st.st_size;
1129  b->parts =
1130  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1131  (long) st.st_size, mutt_istr_equal("digest", a->subtype));
1132  }
1133  else
1134  b = a;
1135 
1136  a = b;
1137 
1138  if (a->parts)
1139  b = a->parts;
1140  else
1141  b = a;
1142 
1143  struct Body *choice = NULL;
1144  struct Body *first_part = NULL;
1145  struct Body *zxx_part = NULL;
1146  struct ListNode *np = NULL;
1147 
1149  {
1150  struct Buffer *langs = mutt_buffer_pool_get();
1151  cs_subset_str_string_get(NeoMutt->sub, "preferred_languages", langs);
1152  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1153  mutt_buffer_string(langs));
1154  mutt_buffer_pool_release(&langs);
1155 
1156  STAILQ_FOREACH(np, &C_PreferredLanguages->head, entries)
1157  {
1158  while (b)
1159  {
1160  if (mutt_can_decode(b))
1161  {
1162  if (!first_part)
1163  first_part = b;
1164 
1165  if (b->language && mutt_str_equal("zxx", b->language))
1166  zxx_part = b;
1167 
1168  mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1169  np->data, b->language);
1170  if (b->language && mutt_str_equal(np->data, b->language))
1171  {
1172  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1173  np->data, b->language);
1174  choice = b;
1175  break;
1176  }
1177  }
1178 
1179  b = b->next;
1180  }
1181 
1182  if (choice)
1183  break;
1184 
1185  if (a->parts)
1186  b = a->parts;
1187  else
1188  b = a;
1189  }
1190  }
1191 
1192  if (choice)
1193  mutt_body_handler(choice, s);
1194  else
1195  {
1196  if (zxx_part)
1197  mutt_body_handler(zxx_part, s);
1198  else
1199  mutt_body_handler(first_part, s);
1200  }
1201 
1202  if (mustfree)
1203  mutt_body_free(&a);
1204 
1205  return rc;
1206 }
1207 
1211 static int multipart_handler(struct Body *a, struct State *s)
1212 {
1213  struct Body *b = NULL, *p = NULL;
1214  struct stat st;
1215  int count;
1216  int rc = 0;
1217 
1218  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1219  (a->encoding == ENC_UUENCODED))
1220  {
1221  fstat(fileno(s->fp_in), &st);
1222  b = mutt_body_new();
1223  b->length = (long) st.st_size;
1224  b->parts =
1225  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1226  (long) st.st_size, mutt_istr_equal("digest", a->subtype));
1227  }
1228  else
1229  b = a;
1230 
1231  for (p = b->parts, count = 1; p; p = p->next, count++)
1232  {
1233  if (s->flags & MUTT_DISPLAY)
1234  {
1235  state_mark_attach(s);
1236  if (p->description || p->filename || p->form_name)
1237  {
1238  /* L10N: %s is the attachment description, filename or form_name. */
1239  state_printf(s, _("[-- Attachment #%d: %s --]\n"), count,
1240  p->description ? p->description :
1241  p->filename ? p->filename :
1242  p->form_name);
1243  }
1244  else
1245  state_printf(s, _("[-- Attachment #%d --]\n"), count);
1246  print_part_line(s, p, 0);
1247  if (C_Weed)
1248  {
1249  state_putc(s, '\n');
1250  }
1251  else
1252  {
1253  fseeko(s->fp_in, p->hdr_offset, SEEK_SET);
1254  mutt_file_copy_bytes(s->fp_in, s->fp_out, p->offset - p->hdr_offset);
1255  }
1256  }
1257 
1258  rc = mutt_body_handler(p, s);
1259  state_putc(s, '\n');
1260 
1261  if (rc != 0)
1262  {
1263  mutt_error(_("One or more parts of this message could not be displayed"));
1264  mutt_debug(LL_DEBUG1, "Failed on attachment #%d, type %s/%s\n", count,
1265  TYPE(p), NONULL(p->subtype));
1266  }
1267 
1268  if ((s->flags & MUTT_REPLYING) && C_IncludeOnlyFirst && (s->flags & MUTT_FIRSTDONE))
1269  {
1270  break;
1271  }
1272  }
1273 
1274  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1275  (a->encoding == ENC_UUENCODED))
1276  {
1277  mutt_body_free(&b);
1278  }
1279 
1280  /* make failure of a single part non-fatal */
1281  if (rc < 0)
1282  rc = 1;
1283  return rc;
1284 }
1285 
1295 static int run_decode_and_handler(struct Body *b, struct State *s,
1296  handler_t handler, bool plaintext)
1297 {
1298  char *save_prefix = NULL;
1299  FILE *fp = NULL;
1300  size_t tmplength = 0;
1301  LOFF_T tmpoffset = 0;
1302  int decode = 0;
1303  int rc = 0;
1304 #ifndef USE_FMEMOPEN
1305  struct Buffer *tempfile = NULL;
1306 #endif
1307 
1308  fseeko(s->fp_in, b->offset, SEEK_SET);
1309 
1310 #ifdef USE_FMEMOPEN
1311  char *temp = NULL;
1312  size_t tempsize = 0;
1313 #endif
1314 
1315  /* see if we need to decode this part before processing it */
1316  if ((b->encoding == ENC_BASE64) || (b->encoding == ENC_QUOTED_PRINTABLE) ||
1317  (b->encoding == ENC_UUENCODED) || (plaintext || mutt_is_text_part(b)))
1318  /* text subtypes may require character set conversion even with 8bit encoding */
1319  {
1320  const int orig_type = b->type;
1321  if (!plaintext)
1322  {
1323  /* decode to a tempfile, saving the original destination */
1324  fp = s->fp_out;
1325 #ifdef USE_FMEMOPEN
1326  s->fp_out = open_memstream(&temp, &tempsize);
1327  if (!s->fp_out)
1328  {
1329  mutt_error(_("Unable to open 'memory stream'"));
1330  mutt_debug(LL_DEBUG1, "Can't open 'memory stream'\n");
1331  return -1;
1332  }
1333 #else
1334  tempfile = mutt_buffer_pool_get();
1335  mutt_buffer_mktemp(tempfile);
1336  s->fp_out = mutt_file_fopen(mutt_buffer_string(tempfile), "w");
1337  if (!s->fp_out)
1338  {
1339  mutt_error(_("Unable to open temporary file"));
1340  mutt_debug(LL_DEBUG1, "Can't open %s\n", mutt_buffer_string(tempfile));
1341  mutt_buffer_pool_release(&tempfile);
1342  return -1;
1343  }
1344 #endif
1345  /* decoding the attachment changes the size and offset, so save a copy
1346  * of the "real" values now, and restore them after processing */
1347  tmplength = b->length;
1348  tmpoffset = b->offset;
1349 
1350  /* if we are decoding binary bodies, we don't want to prefix each
1351  * line with the prefix or else the data will get corrupted. */
1352  save_prefix = s->prefix;
1353  s->prefix = NULL;
1354 
1355  decode = 1;
1356  }
1357  else
1358  b->type = TYPE_TEXT;
1359 
1360  mutt_decode_attachment(b, s);
1361 
1362  if (decode)
1363  {
1364  b->length = ftello(s->fp_out);
1365  b->offset = 0;
1366 #ifdef USE_FMEMOPEN
1367  /* When running under torify, mutt_file_fclose(&s->fp_out) does not seem to
1368  * update tempsize. On the other hand, fflush does. See
1369  * https://github.com/neomutt/neomutt/issues/440 */
1370  fflush(s->fp_out);
1371 #endif
1372  mutt_file_fclose(&s->fp_out);
1373 
1374  /* restore final destination and substitute the tempfile for input */
1375  s->fp_out = fp;
1376  fp = s->fp_in;
1377 #ifdef USE_FMEMOPEN
1378  if (tempsize)
1379  {
1380  s->fp_in = fmemopen(temp, tempsize, "r");
1381  }
1382  else
1383  { /* fmemopen can't handle zero-length buffers */
1384  s->fp_in = mutt_file_fopen("/dev/null", "r");
1385  }
1386  if (!s->fp_in)
1387  {
1388  mutt_perror(_("failed to re-open 'memory stream'"));
1389  FREE(&temp);
1390  return -1;
1391  }
1392 #else
1393  s->fp_in = fopen(mutt_buffer_string(tempfile), "r");
1394  unlink(mutt_buffer_string(tempfile));
1395  mutt_buffer_pool_release(&tempfile);
1396 #endif
1397  /* restore the prefix */
1398  s->prefix = save_prefix;
1399  }
1400 
1401  b->type = orig_type;
1402  }
1403 
1404  /* process the (decoded) body part */
1405  if (handler)
1406  {
1407  rc = handler(b, s);
1408  if (rc != 0)
1409  {
1410  mutt_debug(LL_DEBUG1, "Failed on attachment of type %s/%s\n", TYPE(b),
1411  NONULL(b->subtype));
1412  }
1413 
1414  if (decode)
1415  {
1416  b->length = tmplength;
1417  b->offset = tmpoffset;
1418 
1419  /* restore the original source stream */
1420  mutt_file_fclose(&s->fp_in);
1421  s->fp_in = fp;
1422  }
1423  }
1424  s->flags |= MUTT_FIRSTDONE;
1425 #ifdef USE_FMEMOPEN
1426  FREE(&temp);
1427 #endif
1428 
1429  return rc;
1430 }
1431 
1435 static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
1436 {
1437  struct Body *octetstream = b->parts->next;
1438 
1439  /* clear out any mime headers before the handler, so they can't be spoofed. */
1441  mutt_env_free(&octetstream->mime_headers);
1442 
1443  int rc;
1444  /* Some clients improperly encode the octetstream part. */
1445  if (octetstream->encoding != ENC_7BIT)
1446  rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, 0);
1447  else
1448  rc = crypt_pgp_encrypted_handler(octetstream, s);
1449  b->goodsig |= octetstream->goodsig;
1450 
1451  /* Relocate protected headers onto the multipart/encrypted part */
1452  if (!rc && octetstream->mime_headers)
1453  {
1454  b->mime_headers = octetstream->mime_headers;
1455  octetstream->mime_headers = NULL;
1456  }
1457 
1458  return rc;
1459 }
1460 
1464 static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
1465 {
1466  struct Body *octetstream = b->parts->next->next;
1467 
1468  /* clear out any mime headers before the handler, so they can't be spoofed. */
1470  mutt_env_free(&octetstream->mime_headers);
1471 
1472  /* exchange encodes the octet-stream, so re-run it through the decoder */
1473  int rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, false);
1474  b->goodsig |= octetstream->goodsig;
1475 #ifdef USE_AUTOCRYPT
1476  b->is_autocrypt |= octetstream->is_autocrypt;
1477 #endif
1478 
1479  /* Relocate protected headers onto the multipart/encrypted part */
1480  if (!rc && octetstream->mime_headers)
1481  {
1482  b->mime_headers = octetstream->mime_headers;
1483  octetstream->mime_headers = NULL;
1484  }
1485 
1486  return rc;
1487 }
1488 
1496 void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
1497 {
1498  char buf[5];
1499  int ch, i;
1500  bool cr = false;
1501  char bufi[BUFI_SIZE];
1502  size_t l = 0;
1503 
1504  buf[4] = '\0';
1505 
1506  if (istext)
1507  state_set_prefix(s);
1508 
1509  while (len > 0)
1510  {
1511  for (i = 0; (i < 4) && (len > 0); len--)
1512  {
1513  ch = fgetc(s->fp_in);
1514  if (ch == EOF)
1515  break;
1516  if ((ch >= 0) && (ch < 128) && ((base64val(ch) != -1) || (ch == '=')))
1517  buf[i++] = ch;
1518  }
1519  if (i != 4)
1520  {
1521  /* "i" may be zero if there is trailing whitespace, which is not an error */
1522  if (i != 0)
1523  mutt_debug(LL_DEBUG2, "didn't get a multiple of 4 chars\n");
1524  break;
1525  }
1526 
1527  const int c1 = base64val(buf[0]);
1528  const int c2 = base64val(buf[1]);
1529 
1530  /* first char */
1531  ch = (c1 << 2) | (c2 >> 4);
1532 
1533  if (cr && (ch != '\n'))
1534  bufi[l++] = '\r';
1535 
1536  cr = false;
1537 
1538  if (istext && (ch == '\r'))
1539  cr = true;
1540  else
1541  bufi[l++] = ch;
1542 
1543  /* second char */
1544  if (buf[2] == '=')
1545  break;
1546  const int c3 = base64val(buf[2]);
1547  ch = ((c2 & 0xf) << 4) | (c3 >> 2);
1548 
1549  if (cr && (ch != '\n'))
1550  bufi[l++] = '\r';
1551 
1552  cr = false;
1553 
1554  if (istext && (ch == '\r'))
1555  cr = true;
1556  else
1557  bufi[l++] = ch;
1558 
1559  /* third char */
1560  if (buf[3] == '=')
1561  break;
1562  const int c4 = base64val(buf[3]);
1563  ch = ((c3 & 0x3) << 6) | c4;
1564 
1565  if (cr && (ch != '\n'))
1566  bufi[l++] = '\r';
1567 
1568  cr = false;
1569 
1570  if (istext && (ch == '\r'))
1571  cr = true;
1572  else
1573  bufi[l++] = ch;
1574 
1575  if ((l + 8) >= sizeof(bufi))
1576  convert_to_state(cd, bufi, &l, s);
1577  }
1578 
1579  if (cr)
1580  bufi[l++] = '\r';
1581 
1582  convert_to_state(cd, bufi, &l, s);
1583  convert_to_state(cd, 0, 0, s);
1584 
1585  state_reset_prefix(s);
1586 }
1587 
1595 int mutt_body_handler(struct Body *b, struct State *s)
1596 {
1597  if (!b || !s)
1598  return -1;
1599 
1600  bool plaintext = false;
1601  handler_t handler = NULL;
1602  handler_t encrypted_handler = NULL;
1603  int rc = 0;
1604  static unsigned short recurse_level = 0;
1605 
1606  int oflags = s->flags;
1607 
1608  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1609  {
1610  mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1611  return 1;
1612  }
1613  recurse_level++;
1614 
1615  /* first determine which handler to use to process this part */
1616 
1617  if (is_autoview(b))
1618  {
1619  handler = autoview_handler;
1620  s->flags &= ~MUTT_CHARCONV;
1621  }
1622  else if (b->type == TYPE_TEXT)
1623  {
1624  if (mutt_istr_equal("plain", b->subtype))
1625  {
1626  /* avoid copying this part twice since removing the transfer-encoding is
1627  * the only operation needed. */
1628  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1629  {
1630  encrypted_handler = crypt_pgp_application_handler;
1631  handler = encrypted_handler;
1632  }
1633  else if (C_ReflowText && mutt_istr_equal("flowed", mutt_param_get(&b->parameter, "format")))
1634  {
1635  handler = rfc3676_handler;
1636  }
1637  else
1638  {
1639  handler = text_plain_handler;
1640  }
1641  }
1642  else if (mutt_istr_equal("enriched", b->subtype))
1643  handler = text_enriched_handler;
1644  else /* text body type without a handler */
1645  plaintext = false;
1646  }
1647  else if (b->type == TYPE_MESSAGE)
1648  {
1649  if (mutt_is_message_type(b->type, b->subtype))
1650  handler = message_handler;
1651  else if (mutt_istr_equal("delivery-status", b->subtype))
1652  plaintext = true;
1653  else if (mutt_istr_equal("external-body", b->subtype))
1654  handler = external_body_handler;
1655  }
1656  else if (b->type == TYPE_MULTIPART)
1657  {
1658  if (!mutt_str_equal("inline", C_ShowMultipartAlternative) &&
1659  mutt_istr_equal("alternative", b->subtype))
1660  {
1661  handler = alternative_handler;
1662  }
1663  else if (!mutt_str_equal("inline", C_ShowMultipartAlternative) &&
1664  mutt_istr_equal("multilingual", b->subtype))
1665  {
1666  handler = multilingual_handler;
1667  }
1668  else if ((WithCrypto != 0) && mutt_istr_equal("signed", b->subtype))
1669  {
1670  if (!mutt_param_get(&b->parameter, "protocol"))
1671  mutt_error(_("Error: multipart/signed has no protocol"));
1672  else if (s->flags & MUTT_VERIFY)
1673  handler = mutt_signed_handler;
1674  }
1676  {
1677  encrypted_handler = valid_pgp_encrypted_handler;
1678  handler = encrypted_handler;
1679  }
1681  {
1682  encrypted_handler = malformed_pgp_encrypted_handler;
1683  handler = encrypted_handler;
1684  }
1685 
1686  if (!handler)
1687  handler = multipart_handler;
1688 
1689  if ((b->encoding != ENC_7BIT) && (b->encoding != ENC_8BIT) && (b->encoding != ENC_BINARY))
1690  {
1691  mutt_debug(LL_DEBUG1, "Bad encoding type %d for multipart entity, assuming 7 bit\n",
1692  b->encoding);
1693  b->encoding = ENC_7BIT;
1694  }
1695  }
1696  else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1697  {
1698  if (OptDontHandlePgpKeys && mutt_istr_equal("pgp-keys", b->subtype))
1699  {
1700  /* pass raw part through for key extraction */
1701  plaintext = true;
1702  }
1703  else if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1704  {
1705  encrypted_handler = crypt_pgp_application_handler;
1706  handler = encrypted_handler;
1707  }
1708  else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(b))
1709  {
1710  encrypted_handler = crypt_smime_application_handler;
1711  handler = encrypted_handler;
1712  }
1713  }
1714 
1715  /* only respect disposition == attachment if we're not
1716  * displaying from the attachment menu (i.e. pager) */
1717  if ((!C_HonorDisposition || ((b->disposition != DISP_ATTACH) || OptViewAttach)) &&
1718  (plaintext || handler))
1719  {
1720  /* Prevent encrypted attachments from being included in replies
1721  * unless $include_encrypted is set. */
1722  if ((s->flags & MUTT_REPLYING) && (s->flags & MUTT_FIRSTDONE) &&
1723  encrypted_handler && !C_IncludeEncrypted)
1724  {
1725  goto cleanup;
1726  }
1727 
1728  rc = run_decode_and_handler(b, s, handler, plaintext);
1729  }
1730  /* print hint to use attachment menu for disposition == attachment
1731  * if we're not already being called from there */
1732  else if (s->flags & MUTT_DISPLAY)
1733  {
1734  struct Buffer msg = mutt_buffer_make(256);
1735 
1736  if (!OptViewAttach)
1737  {
1738  char keystroke[128] = { 0 };
1739  if (km_expand_key(keystroke, sizeof(keystroke),
1740  km_find_func(MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1741  {
1743  {
1744  /* L10N: %s expands to a keystroke/key binding, e.g. 'v'. */
1745  mutt_buffer_printf(&msg, _("[-- This is an attachment (use '%s' to view this part) --]\n"),
1746  keystroke);
1747  }
1748  else
1749  {
1750  /* L10N: %s/%s is a MIME type, e.g. "text/plain".
1751  The last %s expands to a keystroke/key binding, e.g. 'v'. */
1752  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (use '%s' to view this part) --]\n"),
1753  TYPE(b), b->subtype, keystroke);
1754  }
1755  }
1756  else
1757  {
1759  {
1760  mutt_buffer_strcpy(&msg, _("[-- This is an attachment (need "
1761  "'view-attachments' bound to key) --]\n"));
1762  }
1763  else
1764  {
1765  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1766  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (need 'view-attachments' bound to key) --]\n"),
1767  TYPE(b), b->subtype);
1768  }
1769  }
1770  }
1771  else
1772  {
1774  {
1775  mutt_buffer_strcpy(&msg, _("[-- This is an attachment --]\n"));
1776  }
1777  else
1778  {
1779  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1780  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported --]\n"), TYPE(b), b->subtype);
1781  }
1782  }
1783  state_mark_attach(s);
1784  state_printf(s, "%s", mutt_buffer_string(&msg));
1785  mutt_buffer_dealloc(&msg);
1786  }
1787 
1788 cleanup:
1789  recurse_level--;
1790  s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
1791  if (rc != 0)
1792  {
1793  mutt_debug(LL_DEBUG1, "Bailing on attachment of type %s/%s\n", TYPE(b),
1794  NONULL(b->subtype));
1795  }
1796 
1797  return rc;
1798 }
1799 
1805 bool mutt_can_decode(struct Body *a)
1806 {
1807  if (is_autoview(a))
1808  return true;
1809  if (a->type == TYPE_TEXT)
1810  return true;
1811  if (a->type == TYPE_MESSAGE)
1812  return true;
1813  if (a->type == TYPE_MULTIPART)
1814  {
1815  if (WithCrypto)
1816  {
1817  if (mutt_istr_equal(a->subtype, "signed") || mutt_istr_equal(a->subtype, "encrypted"))
1818  {
1819  return true;
1820  }
1821  }
1822 
1823  for (struct Body *b = a->parts; b; b = b->next)
1824  {
1825  if (mutt_can_decode(b))
1826  return true;
1827  }
1828  }
1829  else if ((WithCrypto != 0) && (a->type == TYPE_APPLICATION))
1830  {
1831  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(a))
1832  return true;
1834  return true;
1835  }
1836 
1837  return false;
1838 }
1839 
1845 void mutt_decode_attachment(struct Body *b, struct State *s)
1846 {
1847  int istext = mutt_is_text_part(b) && (b->disposition == DISP_INLINE);
1848  iconv_t cd = (iconv_t)(-1);
1849 
1850  if (istext && (b->charset || (s->flags & MUTT_CHARCONV)))
1851  {
1852  const char *charset = b->charset;
1853  if (!charset)
1854  {
1855  charset = mutt_param_get(&b->parameter, "charset");
1856  if (!charset && C_AssumedCharset)
1857  charset = mutt_ch_get_default_charset();
1858  }
1859  if (charset && C_Charset)
1861  }
1862 
1863  fseeko(s->fp_in, b->offset, SEEK_SET);
1864  switch (b->encoding)
1865  {
1866  case ENC_QUOTED_PRINTABLE:
1867  decode_quoted(s, b->length,
1868  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1870  cd);
1871  break;
1872  case ENC_BASE64:
1873  mutt_decode_base64(s, b->length,
1874  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1876  cd);
1877  break;
1878  case ENC_UUENCODED:
1879  decode_uuencoded(s, b->length,
1880  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1882  cd);
1883  break;
1884  default:
1885  decode_xbit(s, b->length,
1886  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1888  cd);
1889  break;
1890  }
1891 
1892  if (cd != (iconv_t)(-1))
1893  iconv_close(cd);
1894 }
Body::hdr_offset
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
mutt_file_copy_bytes
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:241
rfc3676.h
CH_FROM
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:55
CopyHeaderFlags
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
ENC_BINARY
@ ENC_BINARY
Binary.
Definition: mime.h:53
decode_uuencoded
static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
Decode uuencoded text.
Definition: handler.c:372
ENC_QUOTED_PRINTABLE
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
lib.h
State::fp_in
FILE * fp_in
File to read from.
Definition: state.h:46
CH_REORDER
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:58
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
TXT_HTML
#define TXT_HTML
Definition: handler.c:76
ENC_UUENCODED
@ ENC_UUENCODED
UUEncoded text.
Definition: mime.h:54
APPLICATION_SMIME
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:98
filter_create_fd
pid_t filter_create_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:61
MUTT_DISPLAY
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
crypt_pgp_encrypted_handler
int crypt_pgp_encrypted_handler(struct Body *a, struct State *s)
Wrapper for CryptModuleSpecs::encrypted_handler()
Definition: cryptglue.c:249
ListNode
A List node for strings.
Definition: list.h:34
C_TextFlowed
WHERE bool C_TextFlowed
Config: Generate 'format=flowed' messages.
Definition: mutt_globals.h:166
Buffer
String manipulation buffer.
Definition: buffer.h:33
mutt_file_sanitize_filename
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:618
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
decode_byte
static unsigned char decode_byte(char ch)
Decode a uuencoded byte.
Definition: handler.c:358
C_IncludeOnlyFirst
bool C_IncludeOnlyFirst
Config: Only include the first attachment when replying.
Definition: handler.c:68
mailcap_lookup
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:465
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
mutt_is_malformed_multipart_pgp_encrypted
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:510
Body::next
struct Body * next
next attachment in the list
Definition: body.h:53
mutt_buffer_dealloc
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
Body
The body of an email.
Definition: body.h:34
Body::is_autocrypt
bool is_autocrypt
Flag autocrypt-decrypted messages for replying.
Definition: body.h:79
mailcap_expand_command
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
Slist::head
struct ListHead head
Definition: slist.h:48
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
handler.h
CH_WEED
#define CH_WEED
Weed the headers?
Definition: copy.h:52
mutt_date_parse_date
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:445
mailcap.h
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
mutt_file_fopen
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
mutt_file_unlink
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
mutt_is_valid_multipart_pgp_encrypted
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
Is this a valid multi-part encrypted message?
Definition: crypt.c:473
state_prefix_put
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
FREE
#define FREE(x)
Definition: memory.h:40
options.h
state_putc
#define state_putc(STATE, STR)
Definition: state.h:56
Body::disposition
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
mutt_decode_base64
void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
Decode base64-encoded text.
Definition: handler.c:1496
handler_t
int(* handler_t)(struct Body *b, struct State *s)
Manage a PGP or S/MIME encrypted MIME part.
Definition: handler.c:87
TXT_PLAIN
#define TXT_PLAIN
Definition: handler.c:77
Body::encoding
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
DISP_ATTACH
@ DISP_ATTACH
Content is attached.
Definition: mime.h:63
copy.h
is_mmnoask
static bool is_mmnoask(const char *buf)
Metamail compatibility: should the attachment be autoviewed?
Definition: handler.c:433
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
mutt_ch_get_default_charset
char * mutt_ch_get_default_charset(void)
Get the default character set.
Definition: charset.c:440
mutt_body_free
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
decode_quoted
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
Body::subtype
char * subtype
content-type subtype
Definition: body.h:37
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
MailcapEntry
A mailcap entry.
Definition: mailcap.h:38
State::fp_out
FILE * fp_out
File to write to.
Definition: state.h:47
text_enriched_handler
int text_enriched_handler(struct Body *a, struct State *s)
Handler for enriched text - Implements handler_t.
Definition: enriched.c:460
state_attach_puts
void state_attach_puts(struct State *s, const char *t)
Write a string to the state.
Definition: state.c:70
TXT_ENRICHED
#define TXT_ENRICHED
Definition: handler.c:78
State::prefix
char * prefix
String to add to the beginning of each output line.
Definition: state.h:48
mutt_ch_iconv_open
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:565
mutt_istr_equal
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
MUTT_MC_AUTOVIEW
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition: mailcap.h:62
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
lib.h
MUTT_RL_NO_FLAGS
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:39
MUTT_ICONV_HOOK_FROM
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:74
filter_create
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
mutt_str_pretty_size
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1663
mutt_body_new
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
mutt_env_free
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
mutt_is_application_pgp
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:554
C_ReflowText
bool C_ReflowText
Config: Reformat paragraphs of 'format=flowed' text.
Definition: handler.c:70
qp_decode_line
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
mutt_can_decode
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1805
CH_DECODE
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:53
malformed_pgp_encrypted_handler
static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for invalid pgp-encrypted emails - Implements handler_t.
Definition: handler.c:1464
MailcapEntry::command
char * command
Definition: mailcap.h:40
Body::goodsig
bool goodsig
Good cryptographic signature.
Definition: body.h:75
keymap.h
km_find_func
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function's mapping in a Menu.
Definition: keymap.c:939
MUTT_CHARCONV
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
lib.h
mutt_file_copy_stream
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
muttlib.h
TYPE
#define TYPE(body)
Definition: mime.h:89
MUTT_VERIFY
#define MUTT_VERIFY
Perform signature verification.
Definition: state.h:33
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
mutt_check_lookup_list
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:335
ENC_8BIT
@ ENC_8BIT
8-bit text
Definition: mime.h:50
APPLICATION_PGP
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
lib.h
mutt_signed_handler
int mutt_signed_handler(struct Body *a, struct State *s)
Verify a "multipart/signed" body - Implements handler_t.
Definition: crypt.c:1116
OptDontHandlePgpKeys
WHERE bool OptDontHandlePgpKeys
(pseudo) used to extract PGP keys
Definition: options.h:36
MUTT_FIRSTDONE
#define MUTT_FIRSTDONE
The first attachment has been done.
Definition: state.h:39
mutt_clear_error
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
mailcap_expand_filename
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:533
qp_decode_triple
static int qp_decode_triple(char *s, char *d)
Decode a quoted-printable triplet.
Definition: handler.c:216
valid_pgp_encrypted_handler
static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for valid pgp-encrypted emails - Implements handler_t.
Definition: handler.c:1435
km_expand_key
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:911
mutt_copy_hdr
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:103
MUTT_REPLYING
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
mutt_is_message_type
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1368
CH_DISPLAY
#define CH_DISPLAY
Display result to user.
Definition: copy.h:69
Body::parts
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
TYPE_MESSAGE
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
Body::charset
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:49
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
state_printf
int state_printf(struct State *s, const char *fmt,...)
Write a formatted string to the State.
Definition: state.c:153
base64val
#define base64val(ch)
Definition: base64.h:30
mutt_str_len
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
mutt_str_getenv
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:991
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
message_handler
static int message_handler(struct Body *a, struct State *s)
Handler for message/rfc822 body parts - Implements handler_t.
Definition: handler.c:708
mutt_date_epoch
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:416
crypt_pgp_application_handler
int crypt_pgp_application_handler(struct Body *m, struct State *s)
Wrapper for CryptModuleSpecs::application_handler()
Definition: cryptglue.c:236
mutt_parse_multipart
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition: parse.c:1690
filter_wait
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
print_part_line
static void print_part_line(struct State *s, struct Body *b, int n)
Print a separator for the Mime part.
Definition: handler.c:95
WithCrypto
#define WithCrypto
Definition: lib.h:123
mutt_logging.h
BUFI_SIZE
#define BUFI_SIZE
Definition: handler.c:73
mutt_param_get
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
lib.h
lib.h
CH_PREFIX
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:56
C_Weed
bool C_Weed
Config: Filter headers when displaying/forwarding/printing/replying.
Definition: globals.c:40
ENCODING
#define ENCODING(x)
Definition: mime.h:92
mutt_ch_iconv
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:618
multilingual_handler
static int multilingual_handler(struct Body *a, struct State *s)
Handler for multi-lingual emails - Implements handler_t.
Definition: handler.c:1113
TYPE_APPLICATION
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
State
Keep track when processing files.
Definition: state.h:44
mailcap_entry_free
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:436
ENC_BASE64
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
TYPE_TEXT
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
run_decode_and_handler
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:1295
MUTT_WEED
#define MUTT_WEED
Weed headers even when not in display mode.
Definition: state.h:35
NeoMutt
Container for Accounts, Notifications.
Definition: neomutt.h:36
mutt.h
DISP_INLINE
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
mutt_istr_startswith
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
C_AssumedCharset
char * C_AssumedCharset
Config: If a message is missing a character set, assume this character set.
Definition: charset.c:52
C_ShowMultipartAlternative
char * C_ShowMultipartAlternative
Config: How to display 'multipart/alternative' MIME parts.
Definition: handler.c:71
Body::type
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
multipart_handler
static int multipart_handler(struct Body *a, struct State *s)
Handler for multipart emails - Implements handler_t.
Definition: handler.c:1211
OptViewAttach
WHERE bool OptViewAttach
(pseudo) signals that we are viewing attachments
Definition: options.h:55
cs_subset_str_string_get
int cs_subset_str_string_get(const struct ConfigSubset *sub, const char *name, struct Buffer *result)
Get a config item as a string.
Definition: subset.c:357
MUTT_MIME_MAX_DEPTH
#define MUTT_MIME_MAX_DEPTH
Definition: mime.h:69
mutt_body_handler
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1595
C_IncludeEncrypted
bool C_IncludeEncrypted
Config: Whether to include encrypted content when replying.
Definition: handler.c:67
ListNode::data
char * data
String.
Definition: list.h:36
hexval
#define hexval(ch)
Definition: mime.h:80
C_ImplicitAutoview
bool C_ImplicitAutoview
Config: Display MIME attachments inline if a 'copiousoutput' mailcap entry exists.
Definition: handler.c:66
NeoMutt::sub
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
state_set_prefix
#define state_set_prefix(state)
Definition: state.h:53
mutt_buffer_strip_formatting
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: pager.c:1471
external_body_handler
static int external_body_handler(struct Body *b, struct State *s)
Handler for external-body emails - Implements handler_t.
Definition: handler.c:761
mutt_is_text_part
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
plen
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
rfc3676_handler
int rfc3676_handler(struct Body *a, struct State *s)
Body handler implementing RFC3676 for format=flowed - Implements handler_t.
Definition: rfc3676.c:316
mutt_istrn_equal
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
C_HonorDisposition
bool C_HonorDisposition
Config: Don't display MIME parts inline if they have a disposition of 'attachment'.
Definition: handler.c:65
opcodes.h
Body::parameter
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
mutt_decode_attachment
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email's attachment.
Definition: handler.c:1845
state.h
State::flags
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
C_Charset
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53
IS_SPACE
#define IS_SPACE(ch)
Definition: string2.h:38
TYPE_MULTIPART
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
decode_xbit
static void decode_xbit(struct State *s, long len, bool istext, iconv_t cd)
Decode xbit-encoded text.
Definition: handler.c:171
mutt_str_startswith
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
mutt_message
#define mutt_message(...)
Definition: logging.h:83
BUFO_SIZE
#define BUFO_SIZE
Definition: handler.c:74
mutt_attach.h
autoview_handler
static int autoview_handler(struct Body *a, struct State *s)
Handler for autoviewable attachments - Implements handler_t.
Definition: handler.c:529
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
is_autoview
static bool is_autoview(struct Body *b)
Should email body be filtered by mailcap.
Definition: handler.c:484
mutt_buffer_make
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
MUTT_PRINTING
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
state_reset_prefix
#define state_reset_prefix(state)
Definition: state.h:54
alternative_handler
static int alternative_handler(struct Body *a, struct State *s)
Handler for multipart alternative emails - Implements handler_t.
Definition: handler.c:932
text_plain_handler
static int text_plain_handler(struct Body *b, struct State *s)
Handler for plain text - Implements handler_t.
Definition: handler.c:682
MENU_PAGER
@ MENU_PAGER
Pager pager (email viewer)
Definition: keymap.h:81
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
convert_to_state
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
MailcapEntry::nametemplate
char * nametemplate
Definition: mailcap.h:46
LL_DEBUG2
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
mailcap_entry_new
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:427
mutt_is_application_smime
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:612
ENC_7BIT
@ ENC_7BIT
7-bit text
Definition: mime.h:49
Body::language
char * language
content-language (RFC8255)
Definition: body.h:38
state_puts
#define state_puts(STATE, STR)
Definition: state.h:55
mutt_rfc822_parse_message
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
parse a Message/RFC822 body
Definition: parse.c:1674
Body::mime_headers
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:63
C_PreferredLanguages
struct Slist * C_PreferredLanguages
Config: Preferred languages for multilingual MIME.
Definition: handler.c:69
Body::filename
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
crypt_smime_application_handler
int crypt_smime_application_handler(struct Body *m, struct State *s)
Wrapper for CryptModuleSpecs::application_handler()
Definition: cryptglue.c:447
enriched.h
MIN
#define MIN(a, b)
Definition: memory.h:31
mutt_error
#define mutt_error(...)
Definition: logging.h:84
Slist
String list.
Definition: slist.h:46
mutt_file_read_line
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:667
state_mark_attach
void state_mark_attach(struct State *s)
Write a unique marker around content.
Definition: state.c:41
mutt_str_copy
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716