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