NeoMutt  2022-04-29-247-gc6aae8
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
80typedef int (*handler_t)(struct Body *b, struct State *s);
81
88static void print_part_line(struct State *s, struct Body *b, int n)
89{
90 char length[5] = { 0 };
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
115static void convert_to_state(iconv_t cd, char *bufi, size_t *l, struct State *s)
116{
117 char bufo[BUFO_SIZE] = { 0 };
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
164static 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
173
174 int c;
175 char bufi[BUFI_SIZE] = { 0 };
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
209static 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
233static 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
301static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd)
302{
303 char line[256] = { 0 };
304 char decline[512] = { 0 };
305 size_t l = 0;
306 size_t l3;
307
308 if (istext)
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
351static unsigned char decode_byte(char ch)
352{
353 if ((ch < 32) || (ch > 95))
354 return 0;
355 return ch - 32;
356}
357
365static void decode_uuencoded(struct State *s, long len, bool istext, iconv_t cd)
366{
367 char tmps[128] = { 0 };
368 char *pt = NULL;
369 char bufi[BUFI_SIZE] = { 0 };
370 size_t k = 0;
371
372 if (istext)
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
410cleanup:
411 convert_to_state(cd, bufi, &k, s);
412 convert_to_state(cd, 0, 0, s);
413
415}
416
427static 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] = { 0 };
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
478static bool is_autoview(struct Body *b)
479{
480 char type[256] = { 0 };
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
524static int autoview_handler(struct Body *a, struct State *s)
525{
526 struct MailcapEntry *entry = mailcap_entry_new();
527 char buf[1024] = { 0 };
528 char type[256] = { 0 };
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
660cleanup:
661 mailcap_entry_free(&entry);
662
664 mutt_buffer_pool_release(&tempfile);
665
666 return rc;
667}
668
677static 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
704static 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 {
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
756static int external_body_handler(struct Body *b, struct State *s)
757{
758 const char *str = NULL;
759 char strbuf[1024] = { 0 };
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] = { 0 };
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
926static 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;
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! */
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
1110static 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));
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
1212static 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 {
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
1294static 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
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
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 */
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
1437static 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
1466static 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
1498void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd)
1499{
1500 char buf[5] = { 0 };
1501 int ch, i;
1502 bool cr = false;
1503 char bufi[BUFI_SIZE] = { 0 };
1504 size_t l = 0;
1505
1506 buf[4] = '\0';
1507
1508 if (istext)
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
1588}
1589
1597int 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. */
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 }
1790 state_printf(s, "%s", mutt_buffer_string(&msg));
1791 mutt_buffer_dealloc(&msg);
1792 }
1793
1794cleanup:
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
1827bool 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 {
1854 return true;
1856 return true;
1857 }
1858
1859 return false;
1860}
1861
1867void 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 {
1895 decode_quoted(s, b->length,
1896 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1898 cd);
1899 break;
1900 case ENC_BASE64:
1902 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1904 cd);
1905 break;
1906 case ENC_UUENCODED:
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:67
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:309
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
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
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:428
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:457
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:674
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
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
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
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
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
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:956
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:928
Manage keymappings.
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
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
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:435
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:819
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:807
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:927
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:567
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:652
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:524
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:336
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_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition: parse.c:1767
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
Parse a Message/RFC822 body.
Definition: parse.c:1751
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1441
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