NeoMutt  2021-10-22-8-g9cb437
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 <time.h>
38 #include <unistd.h>
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "mutt.h"
44 #include "handler.h"
45 #include "attach/lib.h"
46 #include "menu/lib.h"
47 #include "ncrypt/lib.h"
48 #include "pager/lib.h"
49 #include "copy.h"
50 #include "enriched.h"
51 #include "keymap.h"
52 #include "mailcap.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 #ifdef ENABLE_NLS
60 #include <libintl.h>
61 #endif
62 
63 #define BUFI_SIZE 1000
64 #define BUFO_SIZE 2000
65 
66 #define TXT_HTML 1
67 #define TXT_PLAIN 2
68 #define TXT_ENRICHED 3
69 
80 typedef int (*handler_t)(struct Body *b, struct State *s);
81 
88 static void print_part_line(struct State *s, struct Body *b, int n)
89 {
90  char length[5];
91  mutt_str_pretty_size(length, sizeof(length), b->length);
93  char *charset = mutt_param_get(&b->parameter, "charset");
94  if (n == 0)
95  {
96  state_printf(s, _("[-- Type: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
97  TYPE(b), b->subtype, charset ? "; charset=" : "",
98  charset ? charset : "", ENCODING(b->encoding), length);
99  }
100  else
101  {
102  state_printf(s, _("[-- Alternative Type #%d: %s/%s%s%s, Encoding: %s, Size: %s --]\n"),
103  n, TYPE(b), b->subtype, charset ? "; charset=" : "",
104  charset ? charset : "", ENCODING(b->encoding), length);
105  }
106 }
107 
115 static void convert_to_state(iconv_t cd, char *bufi, size_t *l, struct State *s)
116 {
117  char bufo[BUFO_SIZE];
118  const char *ib = NULL;
119  char *ob = NULL;
120  size_t ibl, obl;
121 
122  if (!bufi)
123  {
124  if (cd != (iconv_t) (-1))
125  {
126  ob = bufo;
127  obl = sizeof(bufo);
128  iconv(cd, NULL, NULL, &ob, &obl);
129  if (ob != bufo)
130  state_prefix_put(s, bufo, ob - bufo);
131  }
132  return;
133  }
134 
135  if (cd == (iconv_t) (-1))
136  {
137  state_prefix_put(s, bufi, *l);
138  *l = 0;
139  return;
140  }
141 
142  ib = bufi;
143  ibl = *l;
144  while (true)
145  {
146  ob = bufo;
147  obl = sizeof(bufo);
148  mutt_ch_iconv(cd, &ib, &ibl, &ob, &obl, 0, "?", NULL);
149  if (ob == bufo)
150  break;
151  state_prefix_put(s, bufo, ob - bufo);
152  }
153  memmove(bufi, ib, ibl);
154  *l = ibl;
155 }
156 
164 static void decode_xbit(struct State *s, long len, bool istext, iconv_t cd)
165 {
166  if (!istext)
167  {
168  mutt_file_copy_bytes(s->fp_in, s->fp_out, len);
169  return;
170  }
171 
172  state_set_prefix(s);
173 
174  int c;
175  char bufi[BUFI_SIZE];
176  size_t l = 0;
177  while (((c = fgetc(s->fp_in)) != EOF) && len--)
178  {
179  if ((c == '\r') && len)
180  {
181  const int ch = fgetc(s->fp_in);
182  if (ch == '\n')
183  {
184  c = ch;
185  len--;
186  }
187  else
188  ungetc(ch, s->fp_in);
189  }
190 
191  bufi[l++] = c;
192  if (l == sizeof(bufi))
193  convert_to_state(cd, bufi, &l, s);
194  }
195 
196  convert_to_state(cd, bufi, &l, s);
197  convert_to_state(cd, 0, 0, s);
198 
200 }
201 
209 static int qp_decode_triple(char *s, char *d)
210 {
211  /* soft line break */
212  if ((s[0] == '=') && (s[1] == '\0'))
213  return 1;
214 
215  /* quoted-printable triple */
216  if ((s[0] == '=') && isxdigit((unsigned char) s[1]) && isxdigit((unsigned char) s[2]))
217  {
218  *d = (hexval(s[1]) << 4) | hexval(s[2]);
219  return 0;
220  }
221 
222  /* something else */
223  return -1;
224 }
225 
233 static void qp_decode_line(char *dest, char *src, size_t *l, int last)
234 {
235  char *d = NULL, *s = NULL;
236  char c = 0;
237 
238  int kind = -1;
239  bool soft = false;
240 
241  /* decode the line */
242 
243  for (d = dest, s = src; *s;)
244  {
245  switch ((kind = qp_decode_triple(s, &c)))
246  {
247  case 0:
248  *d++ = c;
249  s += 3;
250  break; /* qp triple */
251  case -1:
252  *d++ = *s++;
253  break; /* single character */
254  case 1:
255  soft = true;
256  s++;
257  break; /* soft line break */
258  }
259  }
260 
261  if (!soft && (last == '\n'))
262  {
263  /* neither \r nor \n as part of line-terminating CRLF
264  * may be qp-encoded, so remove \r and \n-terminate;
265  * see RFC2045, sect. 6.7, (1): General 8bit representation */
266  if ((kind == 0) && (c == '\r'))
267  *(d - 1) = '\n';
268  else
269  *d++ = '\n';
270  }
271 
272  *d = '\0';
273  *l = d - dest;
274 }
275 
301 static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd)
302 {
303  char line[256];
304  char decline[512];
305  size_t l = 0;
306  size_t l3;
307 
308  if (istext)
309  state_set_prefix(s);
310 
311  while (len > 0)
312  {
313  /* It's ok to use a fixed size buffer for input, even if the line turns
314  * out to be longer than this. Just process the line in chunks. This
315  * really shouldn't happen according the MIME spec, since Q-P encoded
316  * lines are at most 76 characters, but we should be liberal about what
317  * we accept. */
318  if (!fgets(line, MIN((ssize_t) sizeof(line), len + 1), s->fp_in))
319  break;
320 
321  size_t linelen = strlen(line);
322  len -= linelen;
323 
324  /* inspect the last character we read so we can tell if we got the
325  * entire line. */
326  const int last = (linelen != 0) ? line[linelen - 1] : 0;
327 
328  /* chop trailing whitespace if we got the full line */
329  if (last == '\n')
330  {
331  while ((linelen > 0) && IS_SPACE(line[linelen - 1]))
332  linelen--;
333  line[linelen] = '\0';
334  }
335 
336  /* decode and do character set conversion */
337  qp_decode_line(decline + l, line, &l3, last);
338  l += l3;
339  convert_to_state(cd, decline, &l, s);
340  }
341 
342  convert_to_state(cd, 0, 0, s);
344 }
345 
351 static unsigned char decode_byte(char ch)
352 {
353  if ((ch < 32) || (ch > 95))
354  return 0;
355  return ch - 32;
356 }
357 
365 static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
366 {
367  char tmps[128];
368  char *pt = NULL;
369  char bufi[BUFI_SIZE];
370  size_t k = 0;
371 
372  if (istext)
373  state_set_prefix(s);
374 
375  while (len > 0)
376  {
377  if (!fgets(tmps, sizeof(tmps), s->fp_in))
378  return;
379  len -= mutt_str_len(tmps);
380  if (mutt_str_startswith(tmps, "begin "))
381  break;
382  }
383  while (len > 0)
384  {
385  if (!fgets(tmps, sizeof(tmps), s->fp_in))
386  return;
387  len -= mutt_str_len(tmps);
388  if (mutt_str_startswith(tmps, "end"))
389  break;
390  pt = tmps;
391  const unsigned char linelen = decode_byte(*pt);
392  pt++;
393  for (unsigned char c = 0; c < linelen;)
394  {
395  for (char l = 2; l <= 6; l += 2)
396  {
397  char out = decode_byte(*pt) << l;
398  pt++;
399  out |= (decode_byte(*pt) >> (6 - l));
400  bufi[k++] = out;
401  c++;
402  if (c == linelen)
403  break;
404  }
405  convert_to_state(cd, bufi, &k, s);
406  pt++;
407  }
408  }
409 
410  convert_to_state(cd, bufi, &k, s);
411  convert_to_state(cd, 0, 0, s);
412 
414 }
415 
426 static bool is_mmnoask(const char *buf)
427 {
428  const char *val = mutt_str_getenv("MM_NOASK");
429  if (!val)
430  return false;
431 
432  char *p = NULL;
433  char tmp[1024];
434  char *q = NULL;
435 
436  if (mutt_str_equal(val, "1"))
437  return true;
438 
439  mutt_str_copy(tmp, val, sizeof(tmp));
440  p = tmp;
441 
442  while ((p = strtok(p, ",")))
443  {
444  q = strrchr(p, '/');
445  if (q)
446  {
447  if (q[1] == '*')
448  {
449  if (mutt_istrn_equal(buf, p, q - p))
450  return true;
451  }
452  else
453  {
454  if (mutt_istr_equal(buf, p))
455  return true;
456  }
457  }
458  else
459  {
460  const size_t plen = mutt_istr_startswith(buf, p);
461  if ((plen != 0) && (buf[plen] == '/'))
462  return true;
463  }
464 
465  p = NULL;
466  }
467 
468  return false;
469 }
470 
477 static bool is_autoview(struct Body *b)
478 {
479  char type[256];
480  bool is_av = false;
481 
482  snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
483 
484  const bool c_implicit_autoview =
485  cs_subset_bool(NeoMutt->sub, "implicit_autoview");
486  if (c_implicit_autoview)
487  {
488  /* $implicit_autoview is essentially the same as "auto_view *" */
489  is_av = true;
490  }
491  else
492  {
493  /* determine if this type is on the user's auto_view list */
494  mutt_check_lookup_list(b, type, sizeof(type));
495  struct ListNode *np = NULL;
496  STAILQ_FOREACH(np, &AutoViewList, entries)
497  {
498  int i = mutt_str_len(np->data) - 1;
499  if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
500  mutt_istrn_equal(type, np->data, i)) ||
501  mutt_istr_equal(type, np->data))
502  {
503  is_av = true;
504  break;
505  }
506  }
507 
508  if (is_mmnoask(type))
509  is_av = true;
510  }
511 
512  /* determine if there is a mailcap entry suitable for auto_view
513  *
514  * @warning type is altered by this call as a result of 'mime_lookup' support */
515  if (is_av)
516  return mailcap_lookup(b, type, sizeof(type), NULL, MUTT_MC_AUTOVIEW);
517 
518  return false;
519 }
520 
524 static int autoview_handler(struct Body *a, struct State *s)
525 {
526  struct MailcapEntry *entry = mailcap_entry_new();
527  char buf[1024];
528  char type[256];
529  struct Buffer *cmd = mutt_buffer_pool_get();
530  struct Buffer *tempfile = mutt_buffer_pool_get();
531  char *fname = NULL;
532  FILE *fp_in = NULL;
533  FILE *fp_out = NULL;
534  FILE *fp_err = NULL;
535  pid_t pid;
536  int rc = 0;
537 
538  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
539  mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_AUTOVIEW);
540 
541  fname = mutt_str_dup(a->filename);
542  mutt_file_sanitize_filename(fname, true);
543  mailcap_expand_filename(entry->nametemplate, fname, tempfile);
544  FREE(&fname);
545 
546  if (entry->command)
547  {
548  mutt_buffer_strcpy(cmd, entry->command);
549 
550  /* mailcap_expand_command returns 0 if the file is required */
551  bool piped = mailcap_expand_command(a, mutt_buffer_string(tempfile), type, cmd);
552 
553  if (s->flags & MUTT_DISPLAY)
554  {
556  state_printf(s, _("[-- Autoview using %s --]\n"), mutt_buffer_string(cmd));
557  mutt_message(_("Invoking autoview command: %s"), mutt_buffer_string(cmd));
558  }
559 
560  fp_in = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
561  if (!fp_in)
562  {
563  mutt_perror("fopen");
564  mailcap_entry_free(&entry);
565  rc = -1;
566  goto cleanup;
567  }
568 
569  mutt_file_copy_bytes(s->fp_in, fp_in, a->length);
570 
571  if (piped)
572  {
573  unlink(mutt_buffer_string(tempfile));
574  fflush(fp_in);
575  rewind(fp_in);
576  pid = filter_create_fd(mutt_buffer_string(cmd), NULL, &fp_out, &fp_err,
577  fileno(fp_in), -1, -1);
578  }
579  else
580  {
581  mutt_file_fclose(&fp_in);
582  pid = filter_create(mutt_buffer_string(cmd), NULL, &fp_out, &fp_err);
583  }
584 
585  if (pid < 0)
586  {
587  mutt_perror(_("Can't create filter"));
588  if (s->flags & MUTT_DISPLAY)
589  {
591  state_printf(s, _("[-- Can't run %s. --]\n"), mutt_buffer_string(cmd));
592  }
593  rc = -1;
594  goto bail;
595  }
596 
597  if (s->prefix)
598  {
599  /* Remove ansi and formatting from autoview output in replies only. The
600  * user may want to see the formatting in the pager, but it shouldn't be
601  * in their quoted reply text too. */
602  struct Buffer *stripped = mutt_buffer_pool_get();
603  while (fgets(buf, sizeof(buf), fp_out))
604  {
605  mutt_buffer_strip_formatting(stripped, buf, false);
606  state_puts(s, s->prefix);
607  state_puts(s, mutt_buffer_string(stripped));
608  }
609  mutt_buffer_pool_release(&stripped);
610 
611  /* check for data on stderr */
612  if (fgets(buf, sizeof(buf), fp_err))
613  {
614  if (s->flags & MUTT_DISPLAY)
615  {
617  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_buffer_string(cmd));
618  }
619 
620  state_puts(s, s->prefix);
621  state_puts(s, buf);
622  while (fgets(buf, sizeof(buf), fp_err))
623  {
624  state_puts(s, s->prefix);
625  state_puts(s, buf);
626  }
627  }
628  }
629  else
630  {
631  mutt_file_copy_stream(fp_out, s->fp_out);
632  /* Check for stderr messages */
633  if (fgets(buf, sizeof(buf), fp_err))
634  {
635  if (s->flags & MUTT_DISPLAY)
636  {
638  state_printf(s, _("[-- Autoview stderr of %s --]\n"), mutt_buffer_string(cmd));
639  }
640 
641  state_puts(s, buf);
642  mutt_file_copy_stream(fp_err, s->fp_out);
643  }
644  }
645 
646  bail:
647  mutt_file_fclose(&fp_out);
648  mutt_file_fclose(&fp_err);
649 
650  filter_wait(pid);
651  if (piped)
652  mutt_file_fclose(&fp_in);
653  else
655 
656  if (s->flags & MUTT_DISPLAY)
658  }
659 
660 cleanup:
661  mailcap_entry_free(&entry);
662 
664  mutt_buffer_pool_release(&tempfile);
665 
666  return rc;
667 }
668 
677 static int text_plain_handler(struct Body *b, struct State *s)
678 {
679  char *buf = NULL;
680  size_t sz = 0;
681 
682  while ((buf = mutt_file_read_line(buf, &sz, s->fp_in, NULL, MUTT_RL_NO_FLAGS)))
683  {
684  const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
685  if (!mutt_str_equal(buf, "-- ") && c_text_flowed)
686  {
687  size_t len = mutt_str_len(buf);
688  while ((len > 0) && (buf[len - 1] == ' '))
689  buf[--len] = '\0';
690  }
691  if (s->prefix)
692  state_puts(s, s->prefix);
693  state_puts(s, buf);
694  state_putc(s, '\n');
695  }
696 
697  FREE(&buf);
698  return 0;
699 }
700 
704 static int message_handler(struct Body *a, struct State *s)
705 {
706  struct Body *b = NULL;
707  LOFF_T off_start;
708  int rc = 0;
709 
710  off_start = ftello(s->fp_in);
711  if (off_start < 0)
712  return -1;
713 
714  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
715  (a->encoding == ENC_UUENCODED))
716  {
717  b = mutt_body_new();
720  }
721  else
722  b = a;
723 
724  if (b->parts)
725  {
726  CopyHeaderFlags chflags = CH_DECODE | CH_FROM;
727  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
728  if ((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY | MUTT_PRINTING)) && c_weed))
729  chflags |= CH_WEED | CH_REORDER;
730  if (s->prefix)
731  chflags |= CH_PREFIX;
732  if (s->flags & MUTT_DISPLAY)
733  chflags |= CH_DISPLAY;
734 
735  mutt_copy_hdr(s->fp_in, s->fp_out, off_start, b->parts->offset, chflags, s->prefix, 0);
736 
737  if (s->prefix)
738  state_puts(s, s->prefix);
739  state_putc(s, '\n');
740 
741  rc = mutt_body_handler(b->parts, s);
742  }
743 
744  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
745  (a->encoding == ENC_UUENCODED))
746  {
747  mutt_body_free(&b);
748  }
749 
750  return rc;
751 }
752 
756 static int external_body_handler(struct Body *b, struct State *s)
757 {
758  const char *str = NULL;
759  char strbuf[1024];
760 
761  const char *access_type = mutt_param_get(&b->parameter, "access-type");
762  if (!access_type)
763  {
764  if (s->flags & MUTT_DISPLAY)
765  {
767  state_puts(s, _("[-- Error: message/external-body has no access-type "
768  "parameter --]\n"));
769  return 0;
770  }
771  else
772  return -1;
773  }
774 
775  const char *expiration = mutt_param_get(&b->parameter, "expiration");
776  time_t expire;
777  if (expiration)
778  expire = mutt_date_parse_date(expiration, NULL);
779  else
780  expire = -1;
781 
782  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
783  if (mutt_istr_equal(access_type, "x-mutt-deleted"))
784  {
785  if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING))
786  {
787  char pretty_size[10];
788  char *length = mutt_param_get(&b->parameter, "length");
789  if (length)
790  {
791  long size = strtol(length, NULL, 10);
792  mutt_str_pretty_size(pretty_size, sizeof(pretty_size), size);
793  if (expire != -1)
794  {
795  str = ngettext(
796  /* L10N: If the translation of this string is a multi line string, then
797  each line should start with "[-- " and end with " --]".
798  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
799  expands to a date as returned by `mutt_date_parse_date()`.
800 
801  Note: The size argument printed is not the actual number as passed
802  to gettext but the prettified version, e.g. size = 2048 will be
803  printed as 2K. Your language might be sensitive to that: For
804  example although '1K' and '1024' represent the same number your
805  language might inflect the noun 'byte' differently.
806 
807  Sadly, we can't do anything about that at the moment besides
808  passing the precise size in bytes. If you are interested the
809  function responsible for the prettification is
810  mutt_str_pretty_size() in mutt/string.c. */
811  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n"
812  "[-- on %s --]\n",
813  "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n"
814  "[-- on %s --]\n",
815  size);
816  }
817  else
818  {
819  str = ngettext(
820  /* L10N: If the translation of this string is a multi line string, then
821  each line should start with "[-- " and end with " --]".
822  The first "%s/%s" is a MIME type, e.g. "text/plain".
823 
824  Note: The size argument printed is not the actual number as passed
825  to gettext but the prettified version, e.g. size = 2048 will be
826  printed as 2K. Your language might be sensitive to that: For
827  example although '1K' and '1024' represent the same number your
828  language might inflect the noun 'byte' differently.
829 
830  Sadly, we can't do anything about that at the moment besides
831  passing the precise size in bytes. If you are interested the
832  function responsible for the prettification is
833  mutt_str_pretty_size() in mutt/string.c. */
834  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n",
835  "[-- This %s/%s attachment (size %s bytes) has been deleted "
836  "--]\n",
837  size);
838  }
839  }
840  else
841  {
842  pretty_size[0] = '\0';
843  if (expire != -1)
844  {
845  /* L10N: If the translation of this string is a multi line string, then
846  each line should start with "[-- " and end with " --]".
847  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
848  expands to a date as returned by `mutt_date_parse_date()`.
849 
850  Caution: Argument three %3$ is also defined but should not be used
851  in this translation! */
852  str = _("[-- This %s/%s attachment has been deleted --]\n[-- on %4$s "
853  "--]\n");
854  }
855  else
856  {
857  /* L10N: If the translation of this string is a multi line string, then
858  each line should start with "[-- " and end with " --]".
859  The first "%s/%s" is a MIME type, e.g. "text/plain". */
860  str = _("[-- This %s/%s attachment has been deleted --]\n");
861  }
862  }
863 
864  snprintf(strbuf, sizeof(strbuf), str, TYPE(b->parts), b->parts->subtype,
865  pretty_size, expiration);
866  state_attach_puts(s, strbuf);
867  if (b->parts->filename)
868  {
870  state_printf(s, _("[-- name: %s --]\n"), b->parts->filename);
871  }
872 
873  CopyHeaderFlags chflags = CH_DECODE;
874  if (c_weed)
875  chflags |= CH_WEED | CH_REORDER;
876 
877  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
878  chflags, NULL, 0);
879  }
880  }
881  else if (expiration && (expire < mutt_date_epoch()))
882  {
883  if (s->flags & MUTT_DISPLAY)
884  {
885  /* L10N: If the translation of this string is a multi line string, then
886  each line should start with "[-- " and end with " --]".
887  The "%s/%s" is a MIME type, e.g. "text/plain". */
888  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated external source has --]\n[-- expired. --]\n"),
889  TYPE(b->parts), b->parts->subtype);
890  state_attach_puts(s, strbuf);
891 
893  if (c_weed)
894  chflags |= CH_WEED | CH_REORDER;
895 
896  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
897  chflags, NULL, 0);
898  }
899  }
900  else
901  {
902  if (s->flags & MUTT_DISPLAY)
903  {
904  /* L10N: If the translation of this string is a multi line string, then
905  each line should start with "[-- " and end with " --]".
906  The "%s/%s" is a MIME type, e.g. "text/plain". The %s after
907  access-type is an access-type as defined by the MIME RFCs, e.g. "FTP",
908  "LOCAL-FILE", "MAIL-SERVER". */
909  snprintf(strbuf, sizeof(strbuf), _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated access-type %s is unsupported --]\n"),
910  TYPE(b->parts), b->parts->subtype, access_type);
911  state_attach_puts(s, strbuf);
912 
914  if (c_weed)
915  chflags |= CH_WEED | CH_REORDER;
916 
917  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
918  chflags, NULL, 0);
919  }
920  }
921 
922  return 0;
923 }
924 
928 static int alternative_handler(struct Body *a, struct State *s)
929 {
930  struct Body *const head = a;
931  struct Body *choice = NULL;
932  struct Body *b = NULL;
933  bool mustfree = false;
934  int rc = 0;
935 
936  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
937  (a->encoding == ENC_UUENCODED))
938  {
939  mustfree = true;
940  b = mutt_body_new();
942  b->parts =
943  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
944  b->length, mutt_istr_equal("digest", a->subtype));
945  }
946  else
947  b = a;
948 
949  a = b;
950 
951  /* First, search list of preferred types */
952  struct ListNode *np = NULL;
953  STAILQ_FOREACH(np, &AlternativeOrderList, entries)
954  {
955  int btlen; /* length of basetype */
956  bool wild; /* do we have a wildcard to match all subtypes? */
957 
958  char *c = strchr(np->data, '/');
959  if (c)
960  {
961  wild = ((c[1] == '*') && (c[2] == '\0'));
962  btlen = c - np->data;
963  }
964  else
965  {
966  wild = true;
967  btlen = mutt_str_len(np->data);
968  }
969 
970  if (a->parts)
971  b = a->parts;
972  else
973  b = a;
974  while (b)
975  {
976  const char *bt = TYPE(b);
977  if (mutt_istrn_equal(bt, np->data, btlen) && (bt[btlen] == 0))
978  {
979  /* the basetype matches */
980  if (wild || mutt_istr_equal(np->data + btlen + 1, b->subtype))
981  {
982  choice = b;
983  }
984  }
985  b = b->next;
986  }
987 
988  if (choice)
989  break;
990  }
991 
992  /* Next, look for an autoviewable type */
993  if (!choice)
994  {
995  if (a->parts)
996  b = a->parts;
997  else
998  b = a;
999  while (b)
1000  {
1001  if (is_autoview(b))
1002  choice = b;
1003  b = b->next;
1004  }
1005  }
1006 
1007  /* Then, look for a text entry */
1008  if (!choice)
1009  {
1010  if (a->parts)
1011  b = a->parts;
1012  else
1013  b = a;
1014  int type = 0;
1015  while (b)
1016  {
1017  if (b->type == TYPE_TEXT)
1018  {
1019  if (mutt_istr_equal("plain", b->subtype) && (type <= TXT_PLAIN))
1020  {
1021  choice = b;
1022  type = TXT_PLAIN;
1023  }
1024  else if (mutt_istr_equal("enriched", b->subtype) && (type <= TXT_ENRICHED))
1025  {
1026  choice = b;
1027  type = TXT_ENRICHED;
1028  }
1029  else if (mutt_istr_equal("html", b->subtype) && (type <= TXT_HTML))
1030  {
1031  choice = b;
1032  type = TXT_HTML;
1033  }
1034  }
1035  b = b->next;
1036  }
1037  }
1038 
1039  /* Finally, look for other possibilities */
1040  if (!choice)
1041  {
1042  if (a->parts)
1043  b = a->parts;
1044  else
1045  b = a;
1046  while (b)
1047  {
1048  if (mutt_can_decode(b))
1049  choice = b;
1050  b = b->next;
1051  }
1052  }
1053 
1054  if (choice)
1055  {
1056  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1057  if (s->flags & MUTT_DISPLAY && !c_weed &&
1058  (fseeko(s->fp_in, choice->hdr_offset, SEEK_SET) == 0))
1059  {
1060  mutt_file_copy_bytes(s->fp_in, s->fp_out, choice->offset - choice->hdr_offset);
1061  }
1062 
1063  const char *const c_show_multipart_alternative =
1064  cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1065  if (mutt_str_equal("info", c_show_multipart_alternative))
1066  {
1067  print_part_line(s, choice, 0);
1068  }
1069  mutt_body_handler(choice, s);
1070 
1071  /* Let it flow back to the main part */
1072  head->nowrap = choice->nowrap;
1073  choice->nowrap = false;
1074 
1075  if (mutt_str_equal("info", c_show_multipart_alternative))
1076  {
1077  if (a->parts)
1078  b = a->parts;
1079  else
1080  b = a;
1081  int count = 0;
1082  while (b)
1083  {
1084  if (choice != b)
1085  {
1086  count += 1;
1087  if (count == 1)
1088  state_putc(s, '\n');
1089 
1090  print_part_line(s, b, count);
1091  }
1092  b = b->next;
1093  }
1094  }
1095  }
1096  else if (s->flags & MUTT_DISPLAY)
1097  {
1098  /* didn't find anything that we could display! */
1099  state_mark_attach(s);
1100  state_puts(s, _("[-- Error: Could not display any parts of "
1101  "Multipart/Alternative --]\n"));
1102  rc = -1;
1103  }
1104 
1105  if (mustfree)
1106  mutt_body_free(&a);
1107 
1108  return rc;
1109 }
1110 
1115 static int multilingual_handler(struct Body *a, struct State *s)
1116 {
1117  struct Body *b = NULL;
1118  bool mustfree = false;
1119  int rc = 0;
1120 
1122  "RFC8255 >> entering in handler multilingual handler\n");
1123  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1124  (a->encoding == ENC_UUENCODED))
1125  {
1126  mustfree = true;
1127  b = mutt_body_new();
1129  b->parts =
1130  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1131  b->length, 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 
1148  const struct Slist *c_preferred_languages =
1149  cs_subset_slist(NeoMutt->sub, "preferred_languages");
1150  if (c_preferred_languages)
1151  {
1152  struct Buffer *langs = mutt_buffer_pool_get();
1153  cs_subset_str_string_get(NeoMutt->sub, "preferred_languages", langs);
1154  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1155  mutt_buffer_string(langs));
1156  mutt_buffer_pool_release(&langs);
1157 
1158  STAILQ_FOREACH(np, &c_preferred_languages->head, entries)
1159  {
1160  while (b)
1161  {
1162  if (mutt_can_decode(b))
1163  {
1164  if (!first_part)
1165  first_part = b;
1166 
1167  if (b->language && mutt_str_equal("zxx", b->language))
1168  zxx_part = b;
1169 
1170  mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1171  np->data, b->language);
1172  if (b->language && mutt_str_equal(np->data, b->language))
1173  {
1174  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1175  np->data, b->language);
1176  choice = b;
1177  break;
1178  }
1179  }
1180 
1181  b = b->next;
1182  }
1183 
1184  if (choice)
1185  break;
1186 
1187  if (a->parts)
1188  b = a->parts;
1189  else
1190  b = a;
1191  }
1192  }
1193 
1194  if (choice)
1195  mutt_body_handler(choice, s);
1196  else
1197  {
1198  if (zxx_part)
1199  mutt_body_handler(zxx_part, s);
1200  else
1201  mutt_body_handler(first_part, s);
1202  }
1203 
1204  if (mustfree)
1205  mutt_body_free(&a);
1206 
1207  return rc;
1208 }
1209 
1213 static int multipart_handler(struct Body *a, struct State *s)
1214 {
1215  struct Body *b = NULL, *p = NULL;
1216  int count;
1217  int rc = 0;
1218 
1219  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1220  (a->encoding == ENC_UUENCODED))
1221  {
1222  b = mutt_body_new();
1224  b->parts =
1225  mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1226  b->length, 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  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1248  if (c_weed)
1249  {
1250  state_putc(s, '\n');
1251  }
1252  else if (fseeko(s->fp_in, p->hdr_offset, SEEK_SET) == 0)
1253  {
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  const bool c_include_only_first =
1269  cs_subset_bool(NeoMutt->sub, "include_only_first");
1270  if ((s->flags & MUTT_REPLYING) && c_include_only_first && (s->flags & MUTT_FIRSTDONE))
1271  {
1272  break;
1273  }
1274  }
1275 
1276  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1277  (a->encoding == ENC_UUENCODED))
1278  {
1279  mutt_body_free(&b);
1280  }
1281 
1282  /* make failure of a single part non-fatal */
1283  if (rc < 0)
1284  rc = 1;
1285  return rc;
1286 }
1287 
1297 static int run_decode_and_handler(struct Body *b, struct State *s,
1298  handler_t handler, bool plaintext)
1299 {
1300  char *save_prefix = NULL;
1301  FILE *fp = NULL;
1302  size_t tmplength = 0;
1303  LOFF_T tmpoffset = 0;
1304  int decode = 0;
1305  int rc = 0;
1306 #ifndef USE_FMEMOPEN
1307  struct Buffer *tempfile = NULL;
1308 #endif
1309 
1310  (void) fseeko(s->fp_in, b->offset, SEEK_SET);
1311 
1312 #ifdef USE_FMEMOPEN
1313  char *temp = NULL;
1314  size_t tempsize = 0;
1315 #endif
1316 
1317  /* see if we need to decode this part before processing it */
1318  if ((b->encoding == ENC_BASE64) || (b->encoding == ENC_QUOTED_PRINTABLE) ||
1319  (b->encoding == ENC_UUENCODED) || (plaintext || mutt_is_text_part(b)))
1320  /* text subtypes may require character set conversion even with 8bit encoding */
1321  {
1322  const int orig_type = b->type;
1323  if (!plaintext)
1324  {
1325  /* decode to a tempfile, saving the original destination */
1326  fp = s->fp_out;
1327 #ifdef USE_FMEMOPEN
1328  s->fp_out = open_memstream(&temp, &tempsize);
1329  if (!s->fp_out)
1330  {
1331  mutt_error(_("Unable to open 'memory stream'"));
1332  mutt_debug(LL_DEBUG1, "Can't open 'memory stream'\n");
1333  return -1;
1334  }
1335 #else
1336  tempfile = mutt_buffer_pool_get();
1337  mutt_buffer_mktemp(tempfile);
1338  s->fp_out = mutt_file_fopen(mutt_buffer_string(tempfile), "w");
1339  if (!s->fp_out)
1340  {
1341  mutt_error(_("Unable to open temporary file"));
1342  mutt_debug(LL_DEBUG1, "Can't open %s\n", mutt_buffer_string(tempfile));
1343  mutt_buffer_pool_release(&tempfile);
1344  return -1;
1345  }
1346 #endif
1347  /* decoding the attachment changes the size and offset, so save a copy
1348  * of the "real" values now, and restore them after processing */
1349  tmplength = b->length;
1350  tmpoffset = b->offset;
1351 
1352  /* if we are decoding binary bodies, we don't want to prefix each
1353  * line with the prefix or else the data will get corrupted. */
1354  save_prefix = s->prefix;
1355  s->prefix = NULL;
1356 
1357  decode = 1;
1358  }
1359  else
1360  b->type = TYPE_TEXT;
1361 
1362  mutt_decode_attachment(b, s);
1363 
1364  if (decode)
1365  {
1366  b->length = ftello(s->fp_out);
1367  b->offset = 0;
1368 #ifdef USE_FMEMOPEN
1369  /* When running under torify, mutt_file_fclose(&s->fp_out) does not seem to
1370  * update tempsize. On the other hand, fflush does. See
1371  * https://github.com/neomutt/neomutt/issues/440 */
1372  fflush(s->fp_out);
1373 #endif
1374  mutt_file_fclose(&s->fp_out);
1375 
1376  /* restore final destination and substitute the tempfile for input */
1377  s->fp_out = fp;
1378  fp = s->fp_in;
1379 #ifdef USE_FMEMOPEN
1380  if (tempsize)
1381  {
1382  s->fp_in = fmemopen(temp, tempsize, "r");
1383  }
1384  else
1385  { /* fmemopen can't handle zero-length buffers */
1386  s->fp_in = mutt_file_fopen("/dev/null", "r");
1387  }
1388  if (!s->fp_in)
1389  {
1390  mutt_perror(_("failed to re-open 'memory stream'"));
1391  FREE(&temp);
1392  return -1;
1393  }
1394 #else
1395  s->fp_in = fopen(mutt_buffer_string(tempfile), "r");
1396  unlink(mutt_buffer_string(tempfile));
1397  mutt_buffer_pool_release(&tempfile);
1398 #endif
1399  /* restore the prefix */
1400  s->prefix = save_prefix;
1401  }
1402 
1403  b->type = orig_type;
1404  }
1405 
1406  /* process the (decoded) body part */
1407  if (handler)
1408  {
1409  rc = handler(b, s);
1410  if (rc != 0)
1411  {
1412  mutt_debug(LL_DEBUG1, "Failed on attachment of type %s/%s\n", TYPE(b),
1413  NONULL(b->subtype));
1414  }
1415 
1416  if (decode)
1417  {
1418  b->length = tmplength;
1419  b->offset = tmpoffset;
1420 
1421  /* restore the original source stream */
1422  mutt_file_fclose(&s->fp_in);
1423  s->fp_in = fp;
1424  }
1425  }
1426  s->flags |= MUTT_FIRSTDONE;
1427 #ifdef USE_FMEMOPEN
1428  FREE(&temp);
1429 #endif
1430 
1431  return rc;
1432 }
1433 
1437 static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
1438 {
1439  struct Body *octetstream = b->parts->next;
1440 
1441  /* clear out any mime headers before the handler, so they can't be spoofed. */
1443  mutt_env_free(&octetstream->mime_headers);
1444 
1445  int rc;
1446  /* Some clients improperly encode the octetstream part. */
1447  if (octetstream->encoding != ENC_7BIT)
1448  rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, 0);
1449  else
1450  rc = crypt_pgp_encrypted_handler(octetstream, s);
1451  b->goodsig |= octetstream->goodsig;
1452 
1453  /* Relocate protected headers onto the multipart/encrypted part */
1454  if (!rc && octetstream->mime_headers)
1455  {
1456  b->mime_headers = octetstream->mime_headers;
1457  octetstream->mime_headers = NULL;
1458  }
1459 
1460  return rc;
1461 }
1462 
1466 static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
1467 {
1468  struct Body *octetstream = b->parts->next->next;
1469 
1470  /* clear out any mime headers before the handler, so they can't be spoofed. */
1472  mutt_env_free(&octetstream->mime_headers);
1473 
1474  /* exchange encodes the octet-stream, so re-run it through the decoder */
1475  int rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, false);
1476  b->goodsig |= octetstream->goodsig;
1477 #ifdef USE_AUTOCRYPT
1478  b->is_autocrypt |= octetstream->is_autocrypt;
1479 #endif
1480 
1481  /* Relocate protected headers onto the multipart/encrypted part */
1482  if (!rc && octetstream->mime_headers)
1483  {
1484  b->mime_headers = octetstream->mime_headers;
1485  octetstream->mime_headers = NULL;
1486  }
1487 
1488  return rc;
1489 }
1490 
1498 void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
1499 {
1500  char buf[5];
1501  int ch, i;
1502  bool cr = false;
1503  char bufi[BUFI_SIZE];
1504  size_t l = 0;
1505 
1506  buf[4] = '\0';
1507 
1508  if (istext)
1509  state_set_prefix(s);
1510 
1511  while (len > 0)
1512  {
1513  for (i = 0; (i < 4) && (len > 0); len--)
1514  {
1515  ch = fgetc(s->fp_in);
1516  if (ch == EOF)
1517  break;
1518  if ((ch >= 0) && (ch < 128) && ((base64val(ch) != -1) || (ch == '=')))
1519  buf[i++] = ch;
1520  }
1521  if (i != 4)
1522  {
1523  /* "i" may be zero if there is trailing whitespace, which is not an error */
1524  if (i != 0)
1525  mutt_debug(LL_DEBUG2, "didn't get a multiple of 4 chars\n");
1526  break;
1527  }
1528 
1529  const int c1 = base64val(buf[0]);
1530  const int c2 = base64val(buf[1]);
1531 
1532  /* first char */
1533  ch = (c1 << 2) | (c2 >> 4);
1534 
1535  if (cr && (ch != '\n'))
1536  bufi[l++] = '\r';
1537 
1538  cr = false;
1539 
1540  if (istext && (ch == '\r'))
1541  cr = true;
1542  else
1543  bufi[l++] = ch;
1544 
1545  /* second char */
1546  if (buf[2] == '=')
1547  break;
1548  const int c3 = base64val(buf[2]);
1549  ch = ((c2 & 0xf) << 4) | (c3 >> 2);
1550 
1551  if (cr && (ch != '\n'))
1552  bufi[l++] = '\r';
1553 
1554  cr = false;
1555 
1556  if (istext && (ch == '\r'))
1557  cr = true;
1558  else
1559  bufi[l++] = ch;
1560 
1561  /* third char */
1562  if (buf[3] == '=')
1563  break;
1564  const int c4 = base64val(buf[3]);
1565  ch = ((c3 & 0x3) << 6) | c4;
1566 
1567  if (cr && (ch != '\n'))
1568  bufi[l++] = '\r';
1569 
1570  cr = false;
1571 
1572  if (istext && (ch == '\r'))
1573  cr = true;
1574  else
1575  bufi[l++] = ch;
1576 
1577  if ((l + 8) >= sizeof(bufi))
1578  convert_to_state(cd, bufi, &l, s);
1579  }
1580 
1581  if (cr)
1582  bufi[l++] = '\r';
1583 
1584  convert_to_state(cd, bufi, &l, s);
1585  convert_to_state(cd, 0, 0, s);
1586 
1587  state_reset_prefix(s);
1588 }
1589 
1597 int mutt_body_handler(struct Body *b, struct State *s)
1598 {
1599  if (!b || !s)
1600  return -1;
1601 
1602  bool plaintext = false;
1603  handler_t handler = NULL;
1604  handler_t encrypted_handler = NULL;
1605  int rc = 0;
1606  static unsigned short recurse_level = 0;
1607 
1608  int oflags = s->flags;
1609 
1610  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1611  {
1612  mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1613  return 1;
1614  }
1615  recurse_level++;
1616 
1617  /* first determine which handler to use to process this part */
1618 
1619  if (is_autoview(b))
1620  {
1621  handler = autoview_handler;
1622  s->flags &= ~MUTT_CHARCONV;
1623  }
1624  else if (b->type == TYPE_TEXT)
1625  {
1626  if (mutt_istr_equal("plain", b->subtype))
1627  {
1628  const bool c_reflow_text = cs_subset_bool(NeoMutt->sub, "reflow_text");
1629  /* avoid copying this part twice since removing the transfer-encoding is
1630  * the only operation needed. */
1631  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1632  {
1633  encrypted_handler = crypt_pgp_application_handler;
1634  handler = encrypted_handler;
1635  }
1636  else if (c_reflow_text && mutt_istr_equal("flowed", mutt_param_get(&b->parameter, "format")))
1637  {
1638  handler = rfc3676_handler;
1639  }
1640  else
1641  {
1642  handler = text_plain_handler;
1643  }
1644  }
1645  else if (mutt_istr_equal("enriched", b->subtype))
1646  handler = text_enriched_handler;
1647  else /* text body type without a handler */
1648  plaintext = false;
1649  }
1650  else if (b->type == TYPE_MESSAGE)
1651  {
1652  if (mutt_is_message_type(b->type, b->subtype))
1653  handler = message_handler;
1654  else if (mutt_istr_equal("delivery-status", b->subtype))
1655  plaintext = true;
1656  else if (mutt_istr_equal("external-body", b->subtype))
1657  handler = external_body_handler;
1658  }
1659  else if (b->type == TYPE_MULTIPART)
1660  {
1661  const char *const c_show_multipart_alternative =
1662  cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1663  if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1664  mutt_istr_equal("alternative", b->subtype))
1665  {
1666  handler = alternative_handler;
1667  }
1668  else if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1669  mutt_istr_equal("multilingual", b->subtype))
1670  {
1671  handler = multilingual_handler;
1672  }
1673  else if ((WithCrypto != 0) && mutt_istr_equal("signed", b->subtype))
1674  {
1675  if (!mutt_param_get(&b->parameter, "protocol"))
1676  mutt_error(_("Error: multipart/signed has no protocol"));
1677  else if (s->flags & MUTT_VERIFY)
1678  handler = mutt_signed_handler;
1679  }
1681  {
1682  encrypted_handler = valid_pgp_encrypted_handler;
1683  handler = encrypted_handler;
1684  }
1686  {
1687  encrypted_handler = malformed_pgp_encrypted_handler;
1688  handler = encrypted_handler;
1689  }
1690 
1691  if (!handler)
1692  handler = multipart_handler;
1693 
1694  if ((b->encoding != ENC_7BIT) && (b->encoding != ENC_8BIT) && (b->encoding != ENC_BINARY))
1695  {
1696  mutt_debug(LL_DEBUG1, "Bad encoding type %d for multipart entity, assuming 7 bit\n",
1697  b->encoding);
1698  b->encoding = ENC_7BIT;
1699  }
1700  }
1701  else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1702  {
1703  if (OptDontHandlePgpKeys && mutt_istr_equal("pgp-keys", b->subtype))
1704  {
1705  /* pass raw part through for key extraction */
1706  plaintext = true;
1707  }
1708  else if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1709  {
1710  encrypted_handler = crypt_pgp_application_handler;
1711  handler = encrypted_handler;
1712  }
1713  else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(b))
1714  {
1715  encrypted_handler = crypt_smime_application_handler;
1716  handler = encrypted_handler;
1717  }
1718  }
1719 
1720  const bool c_honor_disposition =
1721  cs_subset_bool(NeoMutt->sub, "honor_disposition");
1722  /* only respect disposition == attachment if we're not
1723  * displaying from the attachment menu (i.e. pager) */
1724  if ((!c_honor_disposition || ((b->disposition != DISP_ATTACH) || OptViewAttach)) &&
1725  (plaintext || handler))
1726  {
1727  /* Prevent encrypted attachments from being included in replies
1728  * unless $include_encrypted is set. */
1729  const bool c_include_encrypted =
1730  cs_subset_bool(NeoMutt->sub, "include_encrypted");
1731  if ((s->flags & MUTT_REPLYING) && (s->flags & MUTT_FIRSTDONE) &&
1732  encrypted_handler && !c_include_encrypted)
1733  {
1734  goto cleanup;
1735  }
1736 
1737  rc = run_decode_and_handler(b, s, handler, plaintext);
1738  }
1739  /* print hint to use attachment menu for disposition == attachment
1740  * if we're not already being called from there */
1741  else if (s->flags & MUTT_DISPLAY)
1742  {
1743  struct Buffer msg = mutt_buffer_make(256);
1744 
1745  if (!OptViewAttach)
1746  {
1747  char keystroke[128] = { 0 };
1748  if (km_expand_key(keystroke, sizeof(keystroke),
1749  km_find_func(MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1750  {
1751  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1752  {
1753  /* L10N: %s expands to a keystroke/key binding, e.g. 'v'. */
1754  mutt_buffer_printf(&msg, _("[-- This is an attachment (use '%s' to view this part) --]\n"),
1755  keystroke);
1756  }
1757  else
1758  {
1759  /* L10N: %s/%s is a MIME type, e.g. "text/plain".
1760  The last %s expands to a keystroke/key binding, e.g. 'v'. */
1761  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (use '%s' to view this part) --]\n"),
1762  TYPE(b), b->subtype, keystroke);
1763  }
1764  }
1765  else
1766  {
1767  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1768  {
1769  mutt_buffer_strcpy(&msg, _("[-- This is an attachment (need "
1770  "'view-attachments' bound to key) --]\n"));
1771  }
1772  else
1773  {
1774  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1775  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (need 'view-attachments' bound to key) --]\n"),
1776  TYPE(b), b->subtype);
1777  }
1778  }
1779  }
1780  else
1781  {
1782  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1783  {
1784  mutt_buffer_strcpy(&msg, _("[-- This is an attachment --]\n"));
1785  }
1786  else
1787  {
1788  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1789  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported --]\n"), TYPE(b), b->subtype);
1790  }
1791  }
1792  state_mark_attach(s);
1793  state_printf(s, "%s", mutt_buffer_string(&msg));
1794  mutt_buffer_dealloc(&msg);
1795  }
1796 
1797 cleanup:
1798  recurse_level--;
1799  s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
1800  if (rc != 0)
1801  {
1802  mutt_debug(LL_DEBUG1, "Bailing on attachment of type %s/%s\n", TYPE(b),
1803  NONULL(b->subtype));
1804  }
1805 
1806  return rc;
1807 }
1808 
1814 bool mutt_can_decode(struct Body *a)
1815 {
1816  if (is_autoview(a))
1817  return true;
1818  if (a->type == TYPE_TEXT)
1819  return true;
1820  if (a->type == TYPE_MESSAGE)
1821  return true;
1822  if (a->type == TYPE_MULTIPART)
1823  {
1824  if (WithCrypto)
1825  {
1826  if (mutt_istr_equal(a->subtype, "signed") || mutt_istr_equal(a->subtype, "encrypted"))
1827  {
1828  return true;
1829  }
1830  }
1831 
1832  for (struct Body *b = a->parts; b; b = b->next)
1833  {
1834  if (mutt_can_decode(b))
1835  return true;
1836  }
1837  }
1838  else if ((WithCrypto != 0) && (a->type == TYPE_APPLICATION))
1839  {
1840  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(a))
1841  return true;
1843  return true;
1844  }
1845 
1846  return false;
1847 }
1848 
1854 void mutt_decode_attachment(struct Body *b, struct State *s)
1855 {
1856  int istext = mutt_is_text_part(b) && (b->disposition == DISP_INLINE);
1857  iconv_t cd = (iconv_t) (-1);
1858 
1859  if (istext && (b->charset || (s->flags & MUTT_CHARCONV)))
1860  {
1861  const char *charset = b->charset;
1862  if (!charset)
1863  {
1864  const char *const c_assumed_charset =
1865  cs_subset_string(NeoMutt->sub, "assumed_charset");
1866  charset = mutt_param_get(&b->parameter, "charset");
1867  if (!charset && c_assumed_charset)
1868  charset = mutt_ch_get_default_charset();
1869  }
1870  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
1871  if (charset && c_charset)
1872  cd = mutt_ch_iconv_open(c_charset, charset, MUTT_ICONV_HOOK_FROM);
1873  }
1874 
1875  (void) fseeko(s->fp_in, b->offset, SEEK_SET);
1876  switch (b->encoding)
1877  {
1878  case ENC_QUOTED_PRINTABLE:
1879  decode_quoted(s, b->length,
1880  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1882  cd);
1883  break;
1884  case ENC_BASE64:
1885  mutt_decode_base64(s, b->length,
1886  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1888  cd);
1889  break;
1890  case ENC_UUENCODED:
1891  decode_uuencoded(s, b->length,
1892  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1894  cd);
1895  break;
1896  default:
1897  decode_xbit(s, b->length,
1898  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1900  cd);
1901  break;
1902  }
1903 
1904  if (cd != (iconv_t) (-1))
1905  iconv_close(cd);
1906 }
GUI display the mailboxes in a side panel.
#define base64val(ch)
Definition: base64.h:30
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Convenience wrapper for the config headers.
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:104
Duplicate the structure of an entire email.
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:54
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:57
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:56
#define CH_WEED
Weed the headers?
Definition: copy.h:53
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:59
#define CH_DISPLAY
Display result to user.
Definition: copy.h:70
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:50
Convenience wrapper for the core headers.
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:559
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:617
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
Is this a valid multi-part encrypted message?
Definition: crypt.c:478
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:515
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:456
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:1103
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
Structs that make up an email.
Rich text handler.
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
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
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
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:671
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1520
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:622
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
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
static int multipart_handler(struct Body *a, struct State *s)
Handler for multipart emails - Implements handler_t -.
Definition: handler.c:1213
static int message_handler(struct Body *a, struct State *s)
Handler for message/rfc822 body parts - Implements handler_t -.
Definition: handler.c:704
static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for invalid pgp-encrypted emails - Implements handler_t -.
Definition: handler.c:1466
int rfc3676_handler(struct Body *a, struct State *s)
Body handler implementing RFC3676 for format=flowed - Implements handler_t -.
Definition: rfc3676.c:320
int mutt_signed_handler(struct Body *b, struct State *s)
Verify a "multipart/signed" body - Implements handler_t -.
Definition: crypt.c:1152
static int autoview_handler(struct Body *a, struct State *s)
Handler for autoviewable attachments - Implements handler_t -.
Definition: handler.c:524
static int text_plain_handler(struct Body *b, struct State *s)
Handler for plain text - Implements handler_t -.
Definition: handler.c:677
int text_enriched_handler(struct Body *a, struct State *s)
Handler for enriched text - Implements handler_t -.
Definition: enriched.c:459
static int multilingual_handler(struct Body *a, struct State *s)
Handler for multi-lingual emails - Implements handler_t -.
Definition: handler.c:1115
static int alternative_handler(struct Body *a, struct State *s)
Handler for multipart alternative emails - Implements handler_t -.
Definition: handler.c:928
static int valid_pgp_encrypted_handler(struct Body *b, struct State *s)
Handler for valid pgp-encrypted emails - Implements handler_t -.
Definition: handler.c:1437
static int external_body_handler(struct Body *b, struct State *s)
Handler for external-body emails - Implements handler_t -.
Definition: handler.c:756
int crypt_pgp_encrypted_handler(struct Body *b, struct State *s)
Wrapper for CryptModuleSpecs::encrypted_handler() - Implements handler_t -.
Definition: cryptglue.c:249
int crypt_smime_application_handler(struct Body *b, struct State *s)
Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
Definition: cryptglue.c:445
int crypt_pgp_application_handler(struct Body *b, struct State *s)
Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
Definition: cryptglue.c:238
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
int(* handler_t)(struct Body *b, struct State *s)
Definition: handler.c:80
static bool is_autoview(struct Body *b)
Should email body be filtered by mailcap.
Definition: handler.c:477
#define BUFI_SIZE
Definition: handler.c:63
static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
Decode uuencoded text.
Definition: handler.c:365
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:115
static void print_part_line(struct State *s, struct Body *b, int n)
Print a separator for the Mime part.
Definition: handler.c:88
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:1297
#define TXT_PLAIN
Definition: handler.c:67
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email's attachment.
Definition: handler.c:1854
static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd)
Decode an attachment encoded with quoted-printable.
Definition: handler.c:301
#define TXT_HTML
Definition: handler.c:66
#define TXT_ENRICHED
Definition: handler.c:68
#define BUFO_SIZE
Definition: handler.c:64
static void decode_xbit(struct State *s, long len, bool istext, iconv_t cd)
Decode xbit-encoded text.
Definition: handler.c:164
static unsigned char decode_byte(char ch)
Decode a uuencoded byte.
Definition: handler.c:351
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1814
void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
Decode base64-encoded text.
Definition: handler.c:1498
static void qp_decode_line(char *dest, char *src, size_t *l, int last)
Decode a line of quoted-printable text.
Definition: handler.c:233
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1597
static bool is_mmnoask(const char *buf)
Metamail compatibility: should the attachment be autoviewed?
Definition: handler.c:426
static int qp_decode_triple(char *s, char *d)
Decode a quoted-printable triplet.
Definition: handler.c:209
Decide how to display email content.
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
struct Keymap * km_find_func(enum MenuType mtype, int func)
Find a function's mapping in a Menu.
Definition: keymap.c:938
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:910
Manage keymappings.
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:437
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:475
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:446
int mailcap_expand_command(struct Body *a, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition: mailcap.c:67
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:545
RFC1524 Mailcap routines.
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition: mailcap.h:60
#define FREE(x)
Definition: memory.h:40
#define MIN(a, b)
Definition: memory.h:31
GUI present the user with a selectable list.
@ ENC_7BIT
7-bit text
Definition: mime.h:49
@ ENC_UUENCODED
UUEncoded text.
Definition: mime.h:54
@ ENC_BINARY
Binary.
Definition: mime.h:53
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_8BIT
8-bit text
Definition: mime.h:50
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
#define MUTT_MIME_MAX_DEPTH
Definition: mime.h:69
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
@ DISP_ATTACH
Content is attached.
Definition: mime.h:63
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
#define ENCODING(x)
Definition: mime.h:92
#define TYPE(body)
Definition: mime.h:89
#define hexval(ch)
Definition: mime.h:80
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:629
char * mutt_ch_get_default_charset(void)
Get the default character set.
Definition: charset.c:443
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:576
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1024
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:749
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
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
Many unsorted constants and some structs.
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:335
Hundreds of global variables to back the user variables.
struct ListHead AlternativeOrderList
List of preferred mime types to display.
Definition: mutt_globals.h:64
struct ListHead AutoViewList
List of mime types to auto view.
Definition: mutt_globals.h:65
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
NeoMutt Logging.
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1679
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:434
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:88
#define WithCrypto
Definition: lib.h:113
All user-callable functions.
Handling of global boolean variables.
bool OptDontHandlePgpKeys
(pseudo) used to extract PGP keys
Definition: options.h:41
bool OptViewAttach
(pseudo) signals that we are viewing attachments
Definition: options.h:60
GUI display a file/email/help in a viewport with paging.
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
Parse a Message/RFC822 body.
Definition: parse.c:1736
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition: parse.c:1752
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1428
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
RFC3676 Format Flowed routines.
int state_printf(struct State *s, const char *fmt,...)
Write a formatted string to the State.
Definition: state.c:185
void state_prefix_put(struct State *s, const char *buf, size_t buflen)
Write a prefixed fixed-string to the State.
Definition: state.c:203
void state_mark_attach(struct State *s)
Write a unique marker around content.
Definition: state.c:71
void state_attach_puts(struct State *s, const char *t)
Write a string to the state.
Definition: state.c:102
#define MUTT_FIRSTDONE
The first attachment has been done.
Definition: state.h:39
#define state_puts(STATE, STR)
Definition: state.h:55
#define state_set_prefix(state)
Definition: state.h:53
#define MUTT_WEED
Weed headers even when not in display mode.
Definition: state.h:35
#define MUTT_VERIFY
Perform signature verification.
Definition: state.h:33
#define state_reset_prefix(state)
Definition: state.h:54
#define state_putc(STATE, STR)
Definition: state.h:56
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
The body of an email.
Definition: body.h:35
char * language
content-language (RFC8255)
Definition: body.h:76
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:71
LOFF_T offset
offset where the actual data begins
Definition: body.h:51
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:74
bool is_autocrypt
Flag autocrypt-decrypted messages for replying.
Definition: body.h:49
LOFF_T length
length (in bytes) of attachment
Definition: body.h:52
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:77
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:61
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:41
bool nowrap
Do not wrap the output in the pager.
Definition: body.h:88
struct Body * next
next attachment in the list
Definition: body.h:70
char * subtype
content-type subtype
Definition: body.h:59
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:40
bool goodsig
Good cryptographic signature.
Definition: body.h:44
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:79
unsigned int type
content-type primary type, ContentType
Definition: body.h:39
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:57
String manipulation buffer.
Definition: buffer.h:34
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
A mailcap entry.
Definition: mailcap.h:36
char * nametemplate
Definition: mailcap.h:43
char * command
Definition: mailcap.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
String list.
Definition: slist.h:47
struct ListHead head
List containing values.
Definition: slist.h:48
Keep track when processing files.
Definition: state.h:45
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
FILE * fp_out
File to write to.
Definition: state.h:47
char * prefix
String to add to the beginning of each output line.
Definition: state.h:48
FILE * fp_in
File to read from.
Definition: state.h:46
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:370
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:54