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