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