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