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