NeoMutt  2025-12-11-435-g4ac674
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 ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1149 (b_email->encoding == ENC_UUENCODED))
1150 {
1151 mustfree = true;
1152 b = mutt_body_new();
1153 b->length = mutt_file_get_size_fp(state->fp_in);
1154 b->parts = mutt_parse_multipart(state->fp_in,
1155 mutt_param_get(&b_email->parameter, "boundary"),
1156 b->length,
1157 mutt_istr_equal("digest", b_email->subtype));
1158 }
1159 else
1160 {
1161 b = b_email;
1162 }
1163
1164 b_email = b;
1165
1166 if (b_email->parts)
1167 b = b_email->parts;
1168 else
1169 b = b_email;
1170
1171 struct Body *choice = NULL;
1172 struct Body *first_part = NULL;
1173 struct Body *zxx_part = NULL;
1174 struct ListNode *np = NULL;
1175
1176 while (b)
1177 {
1178 if (mutt_can_decode(b))
1179 {
1180 first_part = b;
1181 break;
1182 }
1183 b = b->next;
1184 }
1185
1186 const struct Slist *c_preferred_languages = cs_subset_slist(NeoMutt->sub, "preferred_languages");
1187 if (c_preferred_languages)
1188 {
1189 struct Buffer *langs = buf_pool_get();
1190 cs_subset_str_string_get(NeoMutt->sub, "preferred_languages", langs);
1191 mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1192 buf_string(langs));
1193 buf_pool_release(&langs);
1194
1195 STAILQ_FOREACH(np, &c_preferred_languages->head, entries)
1196 {
1197 while (b)
1198 {
1199 if (mutt_can_decode(b))
1200 {
1201 if (b->language && mutt_str_equal("zxx", b->language))
1202 zxx_part = b;
1203
1204 mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1205 np->data, b->language);
1206 if (b->language && mutt_str_equal(np->data, b->language))
1207 {
1208 mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1209 np->data, b->language);
1210 choice = b;
1211 break;
1212 }
1213 }
1214
1215 b = b->next;
1216 }
1217
1218 if (choice)
1219 break;
1220
1221 if (b_email->parts)
1222 b = b_email->parts;
1223 else
1224 b = b_email;
1225 }
1226 }
1227
1228 if (choice)
1229 {
1230 mutt_body_handler(choice, state);
1231 }
1232 else
1233 {
1234 if (zxx_part)
1235 mutt_body_handler(zxx_part, state);
1236 else if (first_part)
1237 mutt_body_handler(first_part, state);
1238 }
1239
1240 if (mustfree)
1241 mutt_body_free(&b_email);
1242
1243 return rc;
1244}
1245
1249static int multipart_handler(struct Body *b_email, struct State *state)
1250{
1251 struct Body *b = NULL, *p = NULL;
1252 int count;
1253 int rc = 0;
1254
1255 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1256 (b_email->encoding == ENC_UUENCODED))
1257 {
1258 b = mutt_body_new();
1259 b->length = mutt_file_get_size_fp(state->fp_in);
1260 b->parts = mutt_parse_multipart(state->fp_in,
1261 mutt_param_get(&b_email->parameter, "boundary"),
1262 b->length,
1263 mutt_istr_equal("digest", b_email->subtype));
1264 }
1265 else
1266 {
1267 b = b_email;
1268 }
1269
1270 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1271 const bool c_include_only_first = cs_subset_bool(NeoMutt->sub, "include_only_first");
1272
1273 for (p = b->parts, count = 1; p; p = p->next, count++)
1274 {
1275 if (state->flags & STATE_DISPLAY)
1276 {
1277 state_mark_attach(state);
1278 if (p->description || p->filename || p->form_name)
1279 {
1280 /* L10N: %s is the attachment description, filename or form_name. */
1281 state_printf(state, _("[-- Attachment #%d: %s --]\n"), count,
1282 p->description ? p->description :
1283 p->filename ? p->filename :
1284 p->form_name);
1285 }
1286 else
1287 {
1288 state_printf(state, _("[-- Attachment #%d --]\n"), count);
1289 }
1290 print_part_line(state, p, 0);
1291 if (c_weed)
1292 {
1293 state_putc(state, '\n');
1294 }
1295 else if (mutt_file_seek(state->fp_in, p->hdr_offset, SEEK_SET))
1296 {
1297 mutt_file_copy_bytes(state->fp_in, state->fp_out, p->offset - p->hdr_offset);
1298 }
1299 }
1300
1301 rc = mutt_body_handler(p, state);
1302 state_putc(state, '\n');
1303
1304 if (rc != 0)
1305 {
1306 mutt_error(_("One or more parts of this message could not be displayed"));
1307 mutt_debug(LL_DEBUG1, "Failed on attachment #%d, type %s/%s\n", count,
1308 BODY_TYPE(p), NONULL(p->subtype));
1309 }
1310
1311 if ((state->flags & STATE_REPLYING) && c_include_only_first && (state->flags & STATE_FIRSTDONE))
1312 {
1313 break;
1314 }
1315 }
1316
1317 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1318 (b_email->encoding == ENC_UUENCODED))
1319 {
1320 mutt_body_free(&b);
1321 }
1322
1323 /* make failure of a single part non-fatal */
1324 if (rc < 0)
1325 rc = 1;
1326 return rc;
1327}
1328
1338static int run_decode_and_handler(struct Body *b, struct State *state,
1339 handler_t handler, bool plaintext)
1340{
1341 const char *save_prefix = NULL;
1342 FILE *fp = NULL;
1343 size_t tmplength = 0;
1344 LOFF_T tmpoffset = 0;
1345 int decode = 0;
1346 int rc = 0;
1347#ifndef USE_FMEMOPEN
1348 struct Buffer *tempfile = NULL;
1349#endif
1350
1351 if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1352 {
1353 return -1;
1354 }
1355
1356#ifdef USE_FMEMOPEN
1357 char *temp = NULL;
1358 size_t tempsize = 0;
1359#endif
1360
1361 /* see if we need to decode this part before processing it */
1362 if ((b->encoding == ENC_BASE64) || (b->encoding == ENC_QUOTED_PRINTABLE) ||
1363 (b->encoding == ENC_UUENCODED) || (plaintext || mutt_is_text_part(b)))
1364 /* text subtypes may require character set conversion even with 8bit encoding */
1365 {
1366 const int orig_type = b->type;
1367 if (plaintext)
1368 {
1369 b->type = TYPE_TEXT;
1370 }
1371 else
1372 {
1373 /* decode to a tempfile, saving the original destination */
1374 fp = state->fp_out;
1375#ifdef USE_FMEMOPEN
1376 state->fp_out = open_memstream(&temp, &tempsize);
1377 if (!state->fp_out)
1378 {
1379 mutt_error(_("Unable to open 'memory stream'"));
1380 mutt_debug(LL_DEBUG1, "Can't open 'memory stream'\n");
1381 return -1;
1382 }
1383#else
1384 tempfile = buf_pool_get();
1385 buf_mktemp(tempfile);
1386 state->fp_out = mutt_file_fopen(buf_string(tempfile), "w");
1387 if (!state->fp_out)
1388 {
1389 mutt_error(_("Unable to open temporary file"));
1390 mutt_debug(LL_DEBUG1, "Can't open %s\n", buf_string(tempfile));
1391 buf_pool_release(&tempfile);
1392 return -1;
1393 }
1394#endif
1395 /* decoding the attachment changes the size and offset, so save a copy
1396 * of the "real" values now, and restore them after processing */
1397 tmplength = b->length;
1398 tmpoffset = b->offset;
1399
1400 /* if we are decoding binary bodies, we don't want to prefix each
1401 * line with the prefix or else the data will get corrupted. */
1402 save_prefix = state->prefix;
1403 state->prefix = NULL;
1404
1405 decode = 1;
1406 }
1407
1408 mutt_decode_attachment(b, state);
1409
1410 if (decode)
1411 {
1412 b->length = ftello(state->fp_out);
1413 b->offset = 0;
1414#ifdef USE_FMEMOPEN
1415 /* When running under torify, mutt_file_fclose(&state->fp_out) does not seem to
1416 * update tempsize. On the other hand, fflush does. See
1417 * https://github.com/neomutt/neomutt/issues/440 */
1418 fflush(state->fp_out);
1419#endif
1420 mutt_file_fclose(&state->fp_out);
1421
1422 /* restore final destination and substitute the tempfile for input */
1423 state->fp_out = fp;
1424 fp = state->fp_in;
1425#ifdef USE_FMEMOPEN
1426 if (tempsize)
1427 {
1428 state->fp_in = fmemopen(temp, tempsize, "r");
1429 }
1430 else
1431 { /* fmemopen can't handle zero-length buffers */
1432 state->fp_in = mutt_file_fopen("/dev/null", "r");
1433 }
1434 if (!state->fp_in)
1435 {
1436 mutt_perror(_("failed to re-open 'memory stream'"));
1437 FREE(&temp);
1438 state->fp_in = fp;
1439 state->prefix = save_prefix;
1440 b->length = tmplength;
1441 b->offset = tmpoffset;
1442 return -1;
1443 }
1444#else
1445 state->fp_in = mutt_file_fopen(buf_string(tempfile), "r");
1446 unlink(buf_string(tempfile));
1447 buf_pool_release(&tempfile);
1448 if (!state->fp_in)
1449 {
1450 mutt_perror(_("failed to re-open temporary file"));
1451 state->fp_in = fp;
1452 state->prefix = save_prefix;
1453 b->length = tmplength;
1454 b->offset = tmpoffset;
1455 return -1;
1456 }
1457#endif
1458 /* restore the prefix */
1459 state->prefix = save_prefix;
1460 }
1461
1462 b->type = orig_type;
1463 }
1464
1465 /* process the (decoded) body part */
1466 if (handler)
1467 {
1468 rc = handler(b, state);
1469 if (rc != 0)
1470 {
1471 mutt_debug(LL_DEBUG1, "Failed on attachment of type %s/%s\n",
1472 BODY_TYPE(b), NONULL(b->subtype));
1473 }
1474
1475 if (decode)
1476 {
1477 b->length = tmplength;
1478 b->offset = tmpoffset;
1479
1480 /* restore the original source stream */
1481 mutt_file_fclose(&state->fp_in);
1482 state->fp_in = fp;
1483 }
1484 }
1485 state->flags |= STATE_FIRSTDONE;
1486#ifdef USE_FMEMOPEN
1487 FREE(&temp);
1488#endif
1489
1490 return rc;
1491}
1492
1496static int valid_pgp_encrypted_handler(struct Body *b_email, struct State *state)
1497{
1498 struct Body *octetstream = b_email->parts->next;
1499
1500 /* clear out any mime headers before the handler, so they can't be spoofed. */
1501 mutt_env_free(&b_email->mime_headers);
1502 mutt_env_free(&octetstream->mime_headers);
1503
1504 int rc;
1505 /* Some clients improperly encode the octetstream part. */
1506 if (octetstream->encoding != ENC_7BIT)
1507 rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, 0);
1508 else
1509 rc = crypt_pgp_encrypted_handler(octetstream, state);
1510 b_email->goodsig |= octetstream->goodsig;
1511
1512 /* Relocate protected headers onto the multipart/encrypted part */
1513 if (!rc && octetstream->mime_headers)
1514 {
1515 b_email->mime_headers = octetstream->mime_headers;
1516 octetstream->mime_headers = NULL;
1517 }
1518
1519 return rc;
1520}
1521
1525static int malformed_pgp_encrypted_handler(struct Body *b_email, struct State *state)
1526{
1527 if (!b_email->parts || !b_email->parts->next || !b_email->parts->next->next)
1528 return -1;
1529
1530 struct Body *octetstream = b_email->parts->next->next;
1531
1532 /* clear out any mime headers before the handler, so they can't be spoofed. */
1533 mutt_env_free(&b_email->mime_headers);
1534 mutt_env_free(&octetstream->mime_headers);
1535
1536 /* exchange encodes the octet-stream, so re-run it through the decoder */
1537 int rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, false);
1538 b_email->goodsig |= octetstream->goodsig;
1539#ifdef USE_AUTOCRYPT
1540 b_email->is_autocrypt |= octetstream->is_autocrypt;
1541#endif
1542
1543 /* Relocate protected headers onto the multipart/encrypted part */
1544 if (!rc && octetstream->mime_headers)
1545 {
1546 b_email->mime_headers = octetstream->mime_headers;
1547 octetstream->mime_headers = NULL;
1548 }
1549
1550 return rc;
1551}
1552
1560void mutt_decode_base64(struct State *state, size_t len, bool istext, iconv_t cd)
1561{
1562 char buf[5] = { 0 };
1563 int ch, i;
1564 bool cr = false;
1565 char bufi[BUFI_SIZE] = { 0 };
1566 size_t l = 0;
1567
1568 buf[4] = '\0';
1569
1570 if (istext)
1571 state_set_prefix(state);
1572
1573 while (len > 0)
1574 {
1575 for (i = 0; (i < 4) && (len > 0); len--)
1576 {
1577 ch = fgetc(state->fp_in);
1578 if (ch == EOF)
1579 break;
1580 if ((ch >= 0) && (ch < 128) && ((base64val(ch) != -1) || (ch == '=')))
1581 buf[i++] = ch;
1582 }
1583 if (i != 4)
1584 {
1585 /* "i" may be zero if there is trailing whitespace, which is not an error */
1586 if (i != 0)
1587 mutt_debug(LL_DEBUG2, "didn't get a multiple of 4 chars\n");
1588 break;
1589 }
1590
1591 const int c1 = base64val(buf[0]);
1592 const int c2 = base64val(buf[1]);
1593
1594 /* first char */
1595 ch = (c1 << 2) | (c2 >> 4);
1596
1597 if (cr && (ch != '\n'))
1598 bufi[l++] = '\r';
1599
1600 cr = false;
1601
1602 if (istext && (ch == '\r'))
1603 cr = true;
1604 else
1605 bufi[l++] = ch;
1606
1607 /* second char */
1608 if (buf[2] == '=')
1609 break;
1610 const int c3 = base64val(buf[2]);
1611 ch = ((c2 & 0xf) << 4) | (c3 >> 2);
1612
1613 if (cr && (ch != '\n'))
1614 bufi[l++] = '\r';
1615
1616 cr = false;
1617
1618 if (istext && (ch == '\r'))
1619 cr = true;
1620 else
1621 bufi[l++] = ch;
1622
1623 /* third char */
1624 if (buf[3] == '=')
1625 break;
1626 const int c4 = base64val(buf[3]);
1627 ch = ((c3 & 0x3) << 6) | c4;
1628
1629 if (cr && (ch != '\n'))
1630 bufi[l++] = '\r';
1631
1632 cr = false;
1633
1634 if (istext && (ch == '\r'))
1635 cr = true;
1636 else
1637 bufi[l++] = ch;
1638
1639 if ((l + 8) >= sizeof(bufi))
1640 convert_to_state(cd, bufi, &l, state);
1641 }
1642
1643 if (cr)
1644 bufi[l++] = '\r';
1645
1646 convert_to_state(cd, bufi, &l, state);
1647 convert_to_state(cd, 0, 0, state);
1648
1649 state_reset_prefix(state);
1650}
1651
1659int mutt_body_handler(struct Body *b, struct State *state)
1660{
1661 if (!b || !state)
1662 return -1;
1663
1664 bool plaintext = false;
1665 handler_t handler = NULL;
1666 handler_t encrypted_handler = NULL;
1667 int rc = 0;
1668 static unsigned short recurse_level = 0;
1669
1670 const int oflags = state->flags;
1671 const bool is_attachment_display = (oflags & STATE_DISPLAY_ATTACH);
1672
1673 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1674 {
1675 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up\n");
1676 return 1;
1677 }
1678 recurse_level++;
1679
1680 /* first determine which handler to use to process this part */
1681
1682 if (is_autoview(b))
1683 {
1684 handler = autoview_handler;
1685 state->flags &= ~STATE_CHARCONV;
1686 }
1687 else if (b->type == TYPE_TEXT)
1688 {
1689 if (mutt_istr_equal("plain", b->subtype))
1690 {
1691 const bool c_reflow_text = cs_subset_bool(NeoMutt->sub, "reflow_text");
1692 /* avoid copying this part twice since removing the transfer-encoding is
1693 * the only operation needed. */
1695 {
1696 encrypted_handler = crypt_pgp_application_handler;
1697 handler = encrypted_handler;
1698 }
1699 else if (c_reflow_text &&
1700 mutt_istr_equal("flowed", mutt_param_get(&b->parameter, "format")))
1701 {
1702 handler = rfc3676_handler;
1703 }
1704 else
1705 {
1706 handler = text_plain_handler;
1707 }
1708 }
1709 else if (mutt_istr_equal("enriched", b->subtype))
1710 {
1711 handler = text_enriched_handler;
1712 }
1713 else /* text body type without a handler */
1714 {
1715 plaintext = false;
1716 }
1717 }
1718 else if (b->type == TYPE_MESSAGE)
1719 {
1720 if (mutt_is_message_type(b->type, b->subtype))
1721 handler = message_handler;
1722 else if (mutt_istr_equal("delivery-status", b->subtype))
1723 plaintext = true;
1724 else if (mutt_istr_equal("external-body", b->subtype))
1725 handler = external_body_handler;
1726 }
1727 else if (b->type == TYPE_MULTIPART)
1728 {
1729 const char *const c_show_multipart_alternative = cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1730 if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1731 mutt_istr_equal("alternative", b->subtype))
1732 {
1733 handler = alternative_handler;
1734 }
1735 else if (!mutt_str_equal("inline", c_show_multipart_alternative) &&
1736 mutt_istr_equal("multilingual", b->subtype))
1737 {
1738 handler = multilingual_handler;
1739 }
1740 else if ((WithCrypto != 0) && mutt_istr_equal("signed", b->subtype))
1741 {
1742 if (!mutt_param_get(&b->parameter, "protocol"))
1743 mutt_error(_("Error: multipart/signed has no protocol"));
1744 else if (state->flags & STATE_VERIFY)
1745 handler = mutt_signed_handler;
1746 }
1748 {
1749 encrypted_handler = valid_pgp_encrypted_handler;
1750 handler = encrypted_handler;
1751 }
1753 {
1754 encrypted_handler = malformed_pgp_encrypted_handler;
1755 handler = encrypted_handler;
1756 }
1757
1758 if (!handler)
1759 handler = multipart_handler;
1760
1761 if ((b->encoding != ENC_7BIT) && (b->encoding != ENC_8BIT) && (b->encoding != ENC_BINARY))
1762 {
1763 mutt_debug(LL_DEBUG1, "Bad encoding type %d for multipart entity, assuming 7 bit\n",
1764 b->encoding);
1765 b->encoding = ENC_7BIT;
1766 }
1767 }
1768 else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1769 {
1770 if (OptDontHandlePgpKeys && mutt_istr_equal("pgp-keys", b->subtype))
1771 {
1772 /* pass raw part through for key extraction */
1773 plaintext = true;
1774 }
1775 else if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b))
1776 {
1777 encrypted_handler = crypt_pgp_application_handler;
1778 handler = encrypted_handler;
1779 }
1780 else if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(b))
1781 {
1782 encrypted_handler = crypt_smime_application_handler;
1783 handler = encrypted_handler;
1784 }
1785 }
1786
1787 if ((plaintext || handler) && (is_attachment_display || !mutt_prefer_as_attachment(b)))
1788 {
1789 /* only respect disposition == attachment if we're not
1790 * displaying from the attachment menu (i.e. pager) */
1791 /* Prevent encrypted attachments from being included in replies
1792 * unless $include_encrypted is set. */
1793 const bool c_include_encrypted = cs_subset_bool(NeoMutt->sub, "include_encrypted");
1794 if ((state->flags & STATE_REPLYING) && (state->flags & STATE_FIRSTDONE) &&
1795 encrypted_handler && !c_include_encrypted)
1796 {
1797 goto cleanup;
1798 }
1799
1800 rc = run_decode_and_handler(b, state, handler, plaintext);
1801 }
1802 else if (state->flags & STATE_DISPLAY)
1803 {
1804 /* print hint to use attachment menu for disposition == attachment
1805 * if we're not already being called from there */
1806 const bool c_honor_disposition = cs_subset_bool(NeoMutt->sub, "honor_disposition");
1807 struct Buffer *msg = buf_pool_get();
1808
1809 if (is_attachment_display)
1810 {
1811 if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1812 {
1813 buf_strcpy(msg, _("[-- This is an attachment --]\n"));
1814 }
1815 else
1816 {
1817 /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1818 buf_printf(msg, _("[-- %s/%s is unsupported --]\n"), BODY_TYPE(b), b->subtype);
1819 }
1820 }
1821 else
1822 {
1823 struct Buffer *keystroke = buf_pool_get();
1824 if (keymap_expand_key(km_find_func(MdPager, OP_VIEW_ATTACHMENTS), keystroke))
1825 {
1826 if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1827 {
1828 /* L10N: %s expands to a keystroke/key binding, e.g. 'v'. */
1829 buf_printf(msg, _("[-- This is an attachment (use '%s' to view this part) --]\n"),
1830 buf_string(keystroke));
1831 }
1832 else
1833 {
1834 /* L10N: %s/%s is a MIME type, e.g. "text/plain".
1835 The last %s expands to a keystroke/key binding, e.g. 'v'. */
1836 buf_printf(msg, _("[-- %s/%s is unsupported (use '%s' to view this part) --]\n"),
1837 BODY_TYPE(b), b->subtype, buf_string(keystroke));
1838 }
1839 }
1840 else
1841 {
1842 if (c_honor_disposition && (b->disposition == DISP_ATTACH))
1843 {
1844 buf_strcpy(msg, _("[-- This is an attachment (need 'view-attachments' bound to key) --]\n"));
1845 }
1846 else
1847 {
1848 /* L10N: %s/%s is a MIME type, e.g. "text/plain". */
1849 buf_printf(msg, _("[-- %s/%s is unsupported (need 'view-attachments' bound to key) --]\n"),
1850 BODY_TYPE(b), b->subtype);
1851 }
1852 }
1853 buf_pool_release(&keystroke);
1854 }
1855 state_mark_attach(state);
1856 state_printf(state, "%s", buf_string(msg));
1857 buf_pool_release(&msg);
1858 }
1859
1860cleanup:
1861 recurse_level--;
1862 state->flags = oflags | (state->flags & STATE_FIRSTDONE);
1863 if (rc != 0)
1864 {
1865 mutt_debug(LL_DEBUG1, "Bailing on attachment of type %s/%s\n", BODY_TYPE(b),
1866 NONULL(b->subtype));
1867 }
1868
1869 return rc;
1870}
1871
1878{
1879 if (!mutt_can_decode(b))
1880 return true;
1881
1882 if (b->disposition != DISP_ATTACH)
1883 return false;
1884
1885 return cs_subset_bool(NeoMutt->sub, "honor_disposition");
1886}
1887
1893bool mutt_can_decode(struct Body *b)
1894{
1895 if (is_autoview(b))
1896 return true;
1897 if (b->type == TYPE_TEXT)
1898 return true;
1899 if (b->type == TYPE_MESSAGE)
1900 return true;
1901 if (b->type == TYPE_MULTIPART)
1902 {
1903 if (WithCrypto)
1904 {
1905 if (mutt_istr_equal(b->subtype, "signed") || mutt_istr_equal(b->subtype, "encrypted"))
1906 {
1907 return true;
1908 }
1909 }
1910
1911 for (struct Body *part = b->parts; part; part = part->next)
1912 {
1913 if (mutt_can_decode(part))
1914 return true;
1915 }
1916 }
1917 else if ((WithCrypto != 0) && (b->type == TYPE_APPLICATION))
1918 {
1920 return true;
1922 return true;
1923 }
1924
1925 return false;
1926}
1927
1933void mutt_decode_attachment(const struct Body *b, struct State *state)
1934{
1935 int istext = mutt_is_text_part(b) && (b->disposition == DISP_INLINE);
1936 iconv_t cd = ICONV_T_INVALID;
1937
1938 if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1939 {
1940 return;
1941 }
1942
1943 if (istext && (b->charset || (state->flags & STATE_CHARCONV)))
1944 {
1945 const char *charset = b->charset;
1946 if (!charset)
1947 {
1948 charset = mutt_param_get(&b->parameter, "charset");
1949 if (!charset && !slist_is_empty(cc_assumed_charset()))
1951 }
1952 if (charset && cc_charset())
1954 }
1955
1956 switch (b->encoding)
1957 {
1959 decode_quoted(state, b->length,
1960 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1962 cd);
1963 break;
1964 case ENC_BASE64:
1965 mutt_decode_base64(state, b->length,
1966 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1968 cd);
1969 break;
1970 case ENC_UUENCODED:
1971 decode_uuencoded(state, b->length,
1972 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1974 cd);
1975 break;
1976 default:
1977 decode_xbit(state, b->length,
1978 istext || (((WithCrypto & APPLICATION_PGP) != 0) &&
1980 cd);
1981 break;
1982 }
1983}
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:222
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:682
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:1427
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition file.c:586
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
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:469
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:1525
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:1496
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:1249
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:1241
#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:1877
#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:1893
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1659
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:1560
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:1338
#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:1933
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: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:674
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:662
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition string.c:728
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:500
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:583
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:934
bool mutt_is_text_part(const struct Body *b)
Is this part of an email in plain text?
Definition muttlib.c:395
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