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