NeoMutt  2022-04-29-145-g9b6a0e
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  goto cleanup;
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  goto cleanup;
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) && *pt;)
394  {
395  for (char l = 2; (l <= 6) && pt[0] && pt[1]; 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 cleanup:
411  convert_to_state(cd, bufi, &k, s);
412  convert_to_state(cd, 0, 0, s);
413 
415 }
416 
427 static bool is_mmnoask(const char *buf)
428 {
429  const char *val = mutt_str_getenv("MM_NOASK");
430  if (!val)
431  return false;
432 
433  char *p = NULL;
434  char tmp[1024];
435  char *q = NULL;
436 
437  if (mutt_str_equal(val, "1"))
438  return true;
439 
440  mutt_str_copy(tmp, val, sizeof(tmp));
441  p = tmp;
442 
443  while ((p = strtok(p, ",")))
444  {
445  q = strrchr(p, '/');
446  if (q)
447  {
448  if (q[1] == '*')
449  {
450  if (mutt_istrn_equal(buf, p, q - p))
451  return true;
452  }
453  else
454  {
455  if (mutt_istr_equal(buf, p))
456  return true;
457  }
458  }
459  else
460  {
461  const size_t plen = mutt_istr_startswith(buf, p);
462  if ((plen != 0) && (buf[plen] == '/'))
463  return true;
464  }
465 
466  p = NULL;
467  }
468 
469  return false;
470 }
471 
478 static bool is_autoview(struct Body *b)
479 {
480  char type[256];
481  bool is_av = false;
482 
483  snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
484 
485  const bool c_implicit_autoview = 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 parameter --]\n"));
768  return 0;
769  }
770  else
771  return -1;
772  }
773 
774  const char *expiration = mutt_param_get(&b->parameter, "expiration");
775  time_t expire;
776  if (expiration)
777  expire = mutt_date_parse_date(expiration, NULL);
778  else
779  expire = -1;
780 
781  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
782  if (mutt_istr_equal(access_type, "x-mutt-deleted"))
783  {
784  if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING))
785  {
786  char pretty_size[10];
787  char *length = mutt_param_get(&b->parameter, "length");
788  if (length)
789  {
790  long size = strtol(length, NULL, 10);
791  mutt_str_pretty_size(pretty_size, sizeof(pretty_size), size);
792  if (expire != -1)
793  {
794  str = ngettext(
795  /* L10N: If the translation of this string is a multi line string, then
796  each line should start with "[-- " and end with " --]".
797  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
798  expands to a date as returned by `mutt_date_parse_date()`.
799 
800  Note: The size argument printed is not the actual number as passed
801  to gettext but the prettified version, e.g. size = 2048 will be
802  printed as 2K. Your language might be sensitive to that: For
803  example although '1K' and '1024' represent the same number your
804  language might inflect the noun 'byte' differently.
805 
806  Sadly, we can't do anything about that at the moment besides
807  passing the precise size in bytes. If you are interested the
808  function responsible for the prettification is
809  mutt_str_pretty_size() in mutt/string.c. */
810  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n"
811  "[-- on %s --]\n",
812  "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n"
813  "[-- on %s --]\n",
814  size);
815  }
816  else
817  {
818  str = ngettext(
819  /* L10N: If the translation of this string is a multi line string, then
820  each line should start with "[-- " and end with " --]".
821  The first "%s/%s" is a MIME type, e.g. "text/plain".
822 
823  Note: The size argument printed is not the actual number as passed
824  to gettext but the prettified version, e.g. size = 2048 will be
825  printed as 2K. Your language might be sensitive to that: For
826  example although '1K' and '1024' represent the same number your
827  language might inflect the noun 'byte' differently.
828 
829  Sadly, we can't do anything about that at the moment besides
830  passing the precise size in bytes. If you are interested the
831  function responsible for the prettification is
832  mutt_str_pretty_size() in mutt/string.c. */
833  "[-- This %s/%s attachment (size %s byte) has been deleted --]\n",
834  "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n", size);
835  }
836  }
837  else
838  {
839  pretty_size[0] = '\0';
840  if (expire != -1)
841  {
842  /* L10N: If the translation of this string is a multi line string, then
843  each line should start with "[-- " and end with " --]".
844  The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
845  expands to a date as returned by `mutt_date_parse_date()`.
846 
847  Caution: Argument three %3$ is also defined but should not be used
848  in this translation! */
849  str = _("[-- This %s/%s attachment has been deleted --]\n[-- on %4$s --]\n");
850  }
851  else
852  {
853  /* L10N: If the translation of this string is a multi line string, then
854  each line should start with "[-- " and end with " --]".
855  The first "%s/%s" is a MIME type, e.g. "text/plain". */
856  str = _("[-- This %s/%s attachment has been deleted --]\n");
857  }
858  }
859 
860  snprintf(strbuf, sizeof(strbuf), str, TYPE(b->parts), b->parts->subtype,
861  pretty_size, expiration);
862  state_attach_puts(s, strbuf);
863  if (b->parts->filename)
864  {
866  state_printf(s, _("[-- name: %s --]\n"), b->parts->filename);
867  }
868 
869  CopyHeaderFlags chflags = CH_DECODE;
870  if (c_weed)
871  chflags |= CH_WEED | CH_REORDER;
872 
873  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
874  chflags, NULL, 0);
875  }
876  }
877  else if (expiration && (expire < mutt_date_epoch()))
878  {
879  if (s->flags & MUTT_DISPLAY)
880  {
881  /* L10N: If the translation of this string is a multi line string, then
882  each line should start with "[-- " and end with " --]".
883  The "%s/%s" is a MIME type, e.g. "text/plain". */
884  snprintf(strbuf, sizeof(strbuf),
885  _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated external source has --]\n[-- expired. --]\n"),
886  TYPE(b->parts), b->parts->subtype);
887  state_attach_puts(s, strbuf);
888 
890  if (c_weed)
891  chflags |= CH_WEED | CH_REORDER;
892 
893  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
894  chflags, NULL, 0);
895  }
896  }
897  else
898  {
899  if (s->flags & MUTT_DISPLAY)
900  {
901  /* L10N: If the translation of this string is a multi line string, then
902  each line should start with "[-- " and end with " --]".
903  The "%s/%s" is a MIME type, e.g. "text/plain". The %s after
904  access-type is an access-type as defined by the MIME RFCs, e.g. "FTP",
905  "LOCAL-FILE", "MAIL-SERVER". */
906  snprintf(strbuf, sizeof(strbuf),
907  _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated access-type %s is unsupported --]\n"),
908  TYPE(b->parts), b->parts->subtype, access_type);
909  state_attach_puts(s, strbuf);
910 
912  if (c_weed)
913  chflags |= CH_WEED | CH_REORDER;
914 
915  mutt_copy_hdr(s->fp_in, s->fp_out, ftello(s->fp_in), b->parts->offset,
916  chflags, NULL, 0);
917  }
918  }
919 
920  return 0;
921 }
922 
926 static int alternative_handler(struct Body *a, struct State *s)
927 {
928  struct Body *const head = a;
929  struct Body *choice = NULL;
930  struct Body *b = NULL;
931  bool mustfree = false;
932  int rc = 0;
933 
934  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
935  (a->encoding == ENC_UUENCODED))
936  {
937  mustfree = true;
938  b = mutt_body_new();
940  b->parts = mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
941  b->length, mutt_istr_equal("digest", a->subtype));
942  }
943  else
944  b = a;
945 
946  a = b;
947 
948  /* First, search list of preferred types */
949  struct ListNode *np = NULL;
950  STAILQ_FOREACH(np, &AlternativeOrderList, entries)
951  {
952  int btlen; /* length of basetype */
953  bool wild; /* do we have a wildcard to match all subtypes? */
954 
955  char *c = strchr(np->data, '/');
956  if (c)
957  {
958  wild = ((c[1] == '*') && (c[2] == '\0'));
959  btlen = c - np->data;
960  }
961  else
962  {
963  wild = true;
964  btlen = mutt_str_len(np->data);
965  }
966 
967  if (a->parts)
968  b = a->parts;
969  else
970  b = a;
971  while (b)
972  {
973  const char *bt = TYPE(b);
974  if (mutt_istrn_equal(bt, np->data, btlen) && (bt[btlen] == 0))
975  {
976  /* the basetype matches */
977  if (wild || mutt_istr_equal(np->data + btlen + 1, b->subtype))
978  {
979  choice = b;
980  }
981  }
982  b = b->next;
983  }
984 
985  if (choice)
986  break;
987  }
988 
989  /* Next, look for an autoviewable type */
990  if (!choice)
991  {
992  if (a->parts)
993  b = a->parts;
994  else
995  b = a;
996  while (b)
997  {
998  if (is_autoview(b))
999  choice = b;
1000  b = b->next;
1001  }
1002  }
1003 
1004  /* Then, look for a text entry */
1005  if (!choice)
1006  {
1007  if (a->parts)
1008  b = a->parts;
1009  else
1010  b = a;
1011  int type = 0;
1012  while (b)
1013  {
1014  if (b->type == TYPE_TEXT)
1015  {
1016  if (mutt_istr_equal("plain", b->subtype) && (type <= TXT_PLAIN))
1017  {
1018  choice = b;
1019  type = TXT_PLAIN;
1020  }
1021  else if (mutt_istr_equal("enriched", b->subtype) && (type <= TXT_ENRICHED))
1022  {
1023  choice = b;
1024  type = TXT_ENRICHED;
1025  }
1026  else if (mutt_istr_equal("html", b->subtype) && (type <= TXT_HTML))
1027  {
1028  choice = b;
1029  type = TXT_HTML;
1030  }
1031  }
1032  b = b->next;
1033  }
1034  }
1035 
1036  /* Finally, look for other possibilities */
1037  if (!choice)
1038  {
1039  if (a->parts)
1040  b = a->parts;
1041  else
1042  b = a;
1043  while (b)
1044  {
1045  if (mutt_can_decode(b))
1046  choice = b;
1047  b = b->next;
1048  }
1049  }
1050 
1051  if (choice)
1052  {
1053  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1054  if (s->flags & MUTT_DISPLAY && !c_weed &&
1055  mutt_file_seek(s->fp_in, choice->hdr_offset, SEEK_SET))
1056  {
1057  mutt_file_copy_bytes(s->fp_in, s->fp_out, choice->offset - choice->hdr_offset);
1058  }
1059 
1060  const char *const c_show_multipart_alternative = cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1061  if (mutt_str_equal("info", c_show_multipart_alternative))
1062  {
1063  print_part_line(s, choice, 0);
1064  }
1065  mutt_body_handler(choice, s);
1066 
1067  /* Let it flow back to the main part */
1068  head->nowrap = choice->nowrap;
1069  choice->nowrap = false;
1070 
1071  if (mutt_str_equal("info", c_show_multipart_alternative))
1072  {
1073  if (a->parts)
1074  b = a->parts;
1075  else
1076  b = a;
1077  int count = 0;
1078  while (b)
1079  {
1080  if (choice != b)
1081  {
1082  count += 1;
1083  if (count == 1)
1084  state_putc(s, '\n');
1085 
1086  print_part_line(s, b, count);
1087  }
1088  b = b->next;
1089  }
1090  }
1091  }
1092  else if (s->flags & MUTT_DISPLAY)
1093  {
1094  /* didn't find anything that we could display! */
1095  state_mark_attach(s);
1096  state_puts(s, _("[-- Error: Could not display any parts of Multipart/Alternative --]\n"));
1097  rc = -1;
1098  }
1099 
1100  if (mustfree)
1101  mutt_body_free(&a);
1102 
1103  return rc;
1104 }
1105 
1110 static int multilingual_handler(struct Body *a, struct State *s)
1111 {
1112  struct Body *b = NULL;
1113  bool mustfree = false;
1114  int rc = 0;
1115 
1116  mutt_debug(LL_DEBUG2, "RFC8255 >> entering in handler multilingual handler\n");
1117  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1118  (a->encoding == ENC_UUENCODED))
1119  {
1120  mustfree = true;
1121  b = mutt_body_new();
1123  b->parts = mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1124  b->length, mutt_istr_equal("digest", a->subtype));
1125  }
1126  else
1127  b = a;
1128 
1129  a = b;
1130 
1131  if (a->parts)
1132  b = a->parts;
1133  else
1134  b = a;
1135 
1136  struct Body *choice = NULL;
1137  struct Body *first_part = NULL;
1138  struct Body *zxx_part = NULL;
1139  struct ListNode *np = NULL;
1140 
1141  while (b)
1142  {
1143  if (mutt_can_decode(b))
1144  {
1145  first_part = b;
1146  break;
1147  }
1148  b = b->next;
1149  }
1150 
1151  const struct Slist *c_preferred_languages = cs_subset_slist(NeoMutt->sub, "preferred_languages");
1152  if (c_preferred_languages)
1153  {
1154  struct Buffer *langs = mutt_buffer_pool_get();
1155  cs_subset_str_string_get(NeoMutt->sub, "preferred_languages", langs);
1156  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1157  mutt_buffer_string(langs));
1158  mutt_buffer_pool_release(&langs);
1159 
1160  STAILQ_FOREACH(np, &c_preferred_languages->head, entries)
1161  {
1162  while (b)
1163  {
1164  if (mutt_can_decode(b))
1165  {
1166  if (b->language && mutt_str_equal("zxx", b->language))
1167  zxx_part = b;
1168 
1169  mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1170  np->data, b->language);
1171  if (b->language && mutt_str_equal(np->data, b->language))
1172  {
1173  mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1174  np->data, b->language);
1175  choice = b;
1176  break;
1177  }
1178  }
1179 
1180  b = b->next;
1181  }
1182 
1183  if (choice)
1184  break;
1185 
1186  if (a->parts)
1187  b = a->parts;
1188  else
1189  b = a;
1190  }
1191  }
1192 
1193  if (choice)
1194  mutt_body_handler(choice, s);
1195  else
1196  {
1197  if (zxx_part)
1198  mutt_body_handler(zxx_part, s);
1199  else
1200  mutt_body_handler(first_part, s);
1201  }
1202 
1203  if (mustfree)
1204  mutt_body_free(&a);
1205 
1206  return rc;
1207 }
1208 
1212 static int multipart_handler(struct Body *a, struct State *s)
1213 {
1214  struct Body *b = NULL, *p = NULL;
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  b = mutt_body_new();
1223  b->parts = mutt_parse_multipart(s->fp_in, mutt_param_get(&a->parameter, "boundary"),
1224  b->length, mutt_istr_equal("digest", a->subtype));
1225  }
1226  else
1227  b = a;
1228 
1229  for (p = b->parts, count = 1; p; p = p->next, count++)
1230  {
1231  if (s->flags & MUTT_DISPLAY)
1232  {
1233  state_mark_attach(s);
1234  if (p->description || p->filename || p->form_name)
1235  {
1236  /* L10N: %s is the attachment description, filename or form_name. */
1237  state_printf(s, _("[-- Attachment #%d: %s --]\n"), count,
1238  p->description ? p->description :
1239  p->filename ? p->filename :
1240  p->form_name);
1241  }
1242  else
1243  state_printf(s, _("[-- Attachment #%d --]\n"), count);
1244  print_part_line(s, p, 0);
1245  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1246  if (c_weed)
1247  {
1248  state_putc(s, '\n');
1249  }
1250  else if (mutt_file_seek(s->fp_in, p->hdr_offset, SEEK_SET))
1251  {
1252  mutt_file_copy_bytes(s->fp_in, s->fp_out, p->offset - p->hdr_offset);
1253  }
1254  }
1255 
1256  rc = mutt_body_handler(p, s);
1257  state_putc(s, '\n');
1258 
1259  if (rc != 0)
1260  {
1261  mutt_error(_("One or more parts of this message could not be displayed"));
1262  mutt_debug(LL_DEBUG1, "Failed on attachment #%d, type %s/%s\n", count,
1263  TYPE(p), NONULL(p->subtype));
1264  }
1265 
1266  const bool c_include_only_first = cs_subset_bool(NeoMutt->sub, "include_only_first");
1267  if ((s->flags & MUTT_REPLYING) && c_include_only_first && (s->flags & MUTT_FIRSTDONE))
1268  {
1269  break;
1270  }
1271  }
1272 
1273  if ((a->encoding == ENC_BASE64) || (a->encoding == ENC_QUOTED_PRINTABLE) ||
1274  (a->encoding == ENC_UUENCODED))
1275  {
1276  mutt_body_free(&b);
1277  }
1278 
1279  /* make failure of a single part non-fatal */
1280  if (rc < 0)
1281  rc = 1;
1282  return rc;
1283 }
1284 
1294 static int run_decode_and_handler(struct Body *b, struct State *s,
1295  handler_t handler, bool plaintext)
1296 {
1297  char *save_prefix = NULL;
1298  FILE *fp = NULL;
1299  size_t tmplength = 0;
1300  LOFF_T tmpoffset = 0;
1301  int decode = 0;
1302  int rc = 0;
1303 #ifndef USE_FMEMOPEN
1304  struct Buffer *tempfile = NULL;
1305 #endif
1306 
1307  if (!mutt_file_seek(s->fp_in, b->offset, SEEK_SET))
1308  {
1309  return -1;
1310  }
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  const int oflags = s->flags;
1609  const bool is_attachment_display = (oflags & MUTT_DISPLAY_ATTACH);
1610 
1611  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1612  {
1613  mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1614  return 1;
1615  }
1616  recurse_level++;
1617 
1618  /* first determine which handler to use to process this part */
1619 
1620  if (is_autoview(b))
1621  {
1622  handler = autoview_handler;
1623  s->flags &= ~MUTT_CHARCONV;
1624  }
1625  else if (b->type == TYPE_TEXT)
1626  {
1627  if (mutt_istr_equal("plain", b->subtype))
1628  {
1629  const bool c_reflow_text = cs_subset_bool(NeoMutt->sub, "reflow_text");
1630  /* avoid copying this part twice since removing the transfer-encoding is
1631  * the only operation needed. */
1632  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1633  {
1634  encrypted_handler = crypt_pgp_application_handler;
1635  handler = encrypted_handler;
1636  }
1637  else if (c_reflow_text &&
1638  mutt_istr_equal("flowed", mutt_param_get(&b->parameter, "format")))
1639  {
1640  handler = rfc3676_handler;
1641  }
1642  else
1643  {
1644  handler = text_plain_handler;
1645  }
1646  }
1647  else if (mutt_istr_equal("enriched", b->subtype))
1648  handler = text_enriched_handler;
1649  else /* text body type without a handler */
1650  plaintext = false;
1651  }
1652  else if (b->type == TYPE_MESSAGE)
1653  {
1654  if (mutt_is_message_type(b->type, b->subtype))
1655  handler = message_handler;
1656  else if (mutt_istr_equal("delivery-status", b->subtype))
1657  plaintext = true;
1658  else if (mutt_istr_equal("external-body", b->subtype))
1659  handler = external_body_handler;
1660  }
1661  else if (b->type == TYPE_MULTIPART)
1662  {
1663  const char *const c_show_multipart_alternative = cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1664  if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1665  mutt_istr_equal("alternative", b->subtype))
1666  {
1667  handler = alternative_handler;
1668  }
1669  else if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1670  mutt_istr_equal("multilingual", b->subtype))
1671  {
1672  handler = multilingual_handler;
1673  }
1674  else if ((WithCrypto != 0) && mutt_istr_equal("signed", b->subtype))
1675  {
1676  if (!mutt_param_get(&b->parameter, "protocol"))
1677  mutt_error(_("Error: multipart/signed has no protocol"));
1678  else if (s->flags & MUTT_VERIFY)
1679  handler = mutt_signed_handler;
1680  }
1682  {
1683  encrypted_handler = valid_pgp_encrypted_handler;
1684  handler = encrypted_handler;
1685  }
1687  {
1688  encrypted_handler = malformed_pgp_encrypted_handler;
1689  handler = encrypted_handler;
1690  }
1691 
1692  if (!handler)
1693  handler = multipart_handler;
1694 
1695  if ((b->encoding != ENC_7BIT) && (b->encoding != ENC_8BIT) && (b->encoding != ENC_BINARY))
1696  {
1697  mutt_debug(LL_DEBUG1, "Bad encoding type %d for multipart entity, assuming 7 bit\n",
1698  b->encoding);
1699  b->encoding = ENC_7BIT;
1700  }
1701  }
1702  else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1703  {
1704  if (OptDontHandlePgpKeys && mutt_istr_equal("pgp-keys", b->subtype))
1705  {
1706  /* pass raw part through for key extraction */
1707  plaintext = true;
1708  }
1709  else if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1710  {
1711  encrypted_handler = crypt_pgp_application_handler;
1712  handler = encrypted_handler;
1713  }
1714  else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(b))
1715  {
1716  encrypted_handler = crypt_smime_application_handler;
1717  handler = encrypted_handler;
1718  }
1719  }
1720 
1721  /* only respect disposition == attachment if we're not
1722  * displaying from the attachment menu (i.e. pager) */
1723  if ((plaintext || handler) && (is_attachment_display || !mutt_prefer_as_attachment(b)))
1724  {
1725  /* Prevent encrypted attachments from being included in replies
1726  * unless $include_encrypted is set. */
1727  const bool c_include_encrypted = cs_subset_bool(NeoMutt->sub, "include_encrypted");
1728  if ((s->flags & MUTT_REPLYING) && (s->flags & MUTT_FIRSTDONE) &&
1729  encrypted_handler && !c_include_encrypted)
1730  {
1731  goto cleanup;
1732  }
1733 
1734  rc = run_decode_and_handler(b, s, handler, plaintext);
1735  }
1736  /* print hint to use attachment menu for disposition == attachment
1737  * if we're not already being called from there */
1738  else if (s->flags & MUTT_DISPLAY)
1739  {
1740  const bool c_honor_disposition = cs_subset_bool(NeoMutt->sub, "honor_disposition");
1741  struct Buffer msg = mutt_buffer_make(256);
1742 
1743  if (!is_attachment_display)
1744  {
1745  char keystroke[128] = { 0 };
1746  if (km_expand_key(keystroke, sizeof(keystroke),
1747  km_find_func(MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1748  {
1749  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1750  {
1751  /* L10N: %s expands to a keystroke/key binding, e.g. 'v'. */
1752  mutt_buffer_printf(&msg, _("[-- This is an attachment (use '%s' to view this part) --]\n"),
1753  keystroke);
1754  }
1755  else
1756  {
1757  /* L10N: %s/%s is a MIME type, e.g. "text/plain".
1758  The last %s expands to a keystroke/key binding, e.g. 'v'. */
1759  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (use '%s' to view this part) --]\n"),
1760  TYPE(b), b->subtype, keystroke);
1761  }
1762  }
1763  else
1764  {
1765  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1766  {
1767  mutt_buffer_strcpy(&msg, _("[-- This is an attachment (need 'view-attachments' bound to key) --]\n"));
1768  }
1769  else
1770  {
1771  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1772  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported (need 'view-attachments' bound to key) --]\n"),
1773  TYPE(b), b->subtype);
1774  }
1775  }
1776  }
1777  else
1778  {
1779  if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1780  {
1781  mutt_buffer_strcpy(&msg, _("[-- This is an attachment --]\n"));
1782  }
1783  else
1784  {
1785  /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1786  mutt_buffer_printf(&msg, _("[-- %s/%s is unsupported --]\n"), TYPE(b), b->subtype);
1787  }
1788  }
1789  state_mark_attach(s);
1790  state_printf(s, "%s", mutt_buffer_string(&msg));
1791  mutt_buffer_dealloc(&msg);
1792  }
1793 
1794 cleanup:
1795  recurse_level--;
1796  s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
1797  if (rc != 0)
1798  {
1799  mutt_debug(LL_DEBUG1, "Bailing on attachment of type %s/%s\n", TYPE(b),
1800  NONULL(b->subtype));
1801  }
1802 
1803  return rc;
1804 }
1805 
1812 {
1813  if (!mutt_can_decode(b))
1814  return true;
1815 
1816  if (b->disposition != DISP_ATTACH)
1817  return false;
1818 
1819  return cs_subset_bool(NeoMutt->sub, "honor_disposition");
1820 }
1821 
1827 bool mutt_can_decode(struct Body *b)
1828 {
1829  if (is_autoview(b))
1830  return true;
1831  if (b->type == TYPE_TEXT)
1832  return true;
1833  if (b->type == TYPE_MESSAGE)
1834  return true;
1835  if (b->type == TYPE_MULTIPART)
1836  {
1837  if (WithCrypto)
1838  {
1839  if (mutt_istr_equal(b->subtype, "signed") || mutt_istr_equal(b->subtype, "encrypted"))
1840  {
1841  return true;
1842  }
1843  }
1844 
1845  for (struct Body *part = b->parts; part; part = part->next)
1846  {
1847  if (mutt_can_decode(part))
1848  return true;
1849  }
1850  }
1851  else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1852  {
1853  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1854  return true;
1856  return true;
1857  }
1858 
1859  return false;
1860 }
1861 
1867 void mutt_decode_attachment(struct Body *b, struct State *s)
1868 {
1869  int istext = mutt_is_text_part(b) && (b->disposition == DISP_INLINE);
1870  iconv_t cd = (iconv_t) (-1);
1871 
1872  if (!mutt_file_seek(s->fp_in, b->offset, SEEK_SET))
1873  {
1874  return;
1875  }
1876 
1877  if (istext && (b->charset || (s->flags & MUTT_CHARCONV)))
1878  {
1879  const char *charset = b->charset;
1880  if (!charset)
1881  {
1882  const struct Slist *const c_assumed_charset = cs_subset_slist(NeoMutt->sub, "assumed_charset");
1883  charset = mutt_param_get(&b->parameter, "charset");
1884  if (!charset && c_assumed_charset)
1885  charset = mutt_ch_get_default_charset();
1886  }
1887  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
1888  if (charset && c_charset)
1889  cd = mutt_ch_iconv_open(c_charset, charset, MUTT_ICONV_HOOK_FROM);
1890  }
1891 
1892  switch (b->encoding)
1893  {
1894  case ENC_QUOTED_PRINTABLE:
1895  decode_quoted(s, b->length,
1896  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1898  cd);
1899  break;
1900  case ENC_BASE64:
1901  mutt_decode_base64(s, b->length,
1902  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1904  cd);
1905  break;
1906  case ENC_UUENCODED:
1907  decode_uuencoded(s, b->length,
1908  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1910  cd);
1911  break;
1912  default:
1913  decode_xbit(s, b->length,
1914  istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1916  cd);
1917  break;
1918  }
1919 
1920  if (cd != (iconv_t) (-1))
1921  iconv_close(cd);
1922 }
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:63
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:292
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:158
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
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
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:544
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:601
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
Is this a valid multi-part encrypted message?
Definition: crypt.c:463
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:500
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:667
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
Structs that make up an email.
Rich text handler.
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
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:230
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
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:720
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1569
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:647
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
#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:1212
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:318
int mutt_signed_handler(struct Body *b, struct State *s)
Verify a "multipart/signed" body - Implements handler_t -.
Definition: crypt.c:1124
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:1110
static int alternative_handler(struct Body *a, struct State *s)
Handler for multipart alternative emails - Implements handler_t -.
Definition: handler.c:926
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:248
int crypt_smime_application_handler(struct Body *b, struct State *s)
Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
Definition: cryptglue.c:444
int crypt_pgp_application_handler(struct Body *b, struct State *s)
Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
Definition: cryptglue.c:237
#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:478
bool mutt_prefer_as_attachment(struct Body *b)
Do we want this part as an attachment?
Definition: handler.c:1811
#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
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition: handler.c:1827
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:1294
#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:1867
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
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:427
static int qp_decode_triple(char *s, char *d)
Decode a quoted-printable triplet.
Definition: handler.c:209
Decide how to display email content.
struct Keymap * km_find_func(enum MenuType mtype, int func)
Find a function's mapping in a Menu.
Definition: keymap.c:954
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:926
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:435
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:473
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:444
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:542
RFC1524 Mailcap routines.
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition: mailcap.h:60
#define FREE(x)
Definition: memory.h:43
#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:617
char * mutt_ch_get_default_charset(void)
Get the default character set.
Definition: charset.c:439
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:564
#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
int state_printf(struct State *s, const char *fmt,...)
Write a formatted string to the State.
Definition: state.c:183
void state_prefix_put(struct State *s, const char *buf, size_t buflen)
Write a prefixed fixed-string to the State.
Definition: state.c:201
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:100
#define MUTT_FIRSTDONE
The first attachment has been done.
Definition: state.h:39
#define state_puts(STATE, STR)
Definition: state.h:56
#define state_set_prefix(state)
Definition: state.h:54
#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:55
#define state_putc(STATE, STR)
Definition: state.h:57
#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_DISPLAY_ATTACH
We are displaying an attachment.
Definition: state.h:40
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:904
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:629
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
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:501
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:337
Hundreds of global variables to back the user variables.
struct ListHead AlternativeOrderList
List of preferred mime types to display.
Definition: mutt_globals.h:60
struct ListHead AutoViewList
List of mime types to auto view.
Definition: mutt_globals.h:61
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
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:1673
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
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:90
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define WithCrypto
Definition: lib.h:116
All user-callable functions.
Handling of global boolean variables.
bool OptDontHandlePgpKeys
(pseudo) used to extract PGP keys
Definition: options.h:41
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:1752
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition: parse.c:1768
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1442
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.
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
The body of an email.
Definition: body.h:36
char * language
content-language (RFC8255)
Definition: body.h:77
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
bool is_autocrypt
Flag autocrypt-decrypted messages for replying.
Definition: body.h:50
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:78
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:42
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:71
char * subtype
content-type subtype
Definition: body.h:60
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
bool goodsig
Good cryptographic signature.
Definition: body.h:45
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:80
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
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:46
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:50
FILE * fp_out
File to write to.
Definition: state.h:48
char * prefix
String to add to the beginning of each output line.
Definition: state.h:49
FILE * fp_in
File to read from.
Definition: state.h:47
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