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