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