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