NeoMutt  2025-12-11-769-g906513
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
Mime Handler API

Prototype for a function to handle MIME parts. More...

Functions

int text_enriched_handler (struct Body *b_email, struct State *state)
 Handler for enriched text - Implements handler_t -.
 
static int autoview_handler (struct Body *b_email, struct State *state)
 Handler for autoviewable attachments - Implements handler_t -.
 
static int text_plain_handler (struct Body *b_email, struct State *state)
 Handler for plain text - Implements handler_t -.
 
static int message_handler (struct Body *b_email, struct State *state)
 Handler for message/rfc822 body parts - Implements handler_t -.
 
static int external_body_handler (struct Body *b_email, struct State *state)
 Handler for external-body emails - Implements handler_t -.
 
static int alternative_handler (struct Body *b_email, struct State *state)
 Handler for multipart alternative emails - Implements handler_t -.
 
static int multilingual_handler (struct Body *b_email, struct State *state)
 Handler for multi-lingual emails - Implements handler_t -.
 
static int multipart_handler (struct Body *b_email, struct State *state)
 Handler for multipart emails - Implements handler_t -.
 
static int valid_pgp_encrypted_handler (struct Body *b_email, struct State *state)
 Handler for valid pgp-encrypted emails - Implements handler_t -.
 
static int malformed_pgp_encrypted_handler (struct Body *b_email, struct State *state)
 Handler for invalid pgp-encrypted emails - Implements handler_t -.
 
int rfc3676_handler (struct Body *b_email, struct State *state)
 Handler for format=flowed - Implements handler_t -.
 
int mutt_protected_headers_handler (struct Body *b_email, struct State *state)
 Handler for protected headers - Implements handler_t -.
 
int mutt_signed_handler (struct Body *b_email, struct State *state)
 Handler for "multipart/signed" - Implements handler_t -.
 
int crypt_pgp_application_handler (struct Body *b_email, struct State *state)
 Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
 
int crypt_pgp_encrypted_handler (struct Body *b_email, struct State *state)
 Wrapper for CryptModuleSpecs::encrypted_handler() - Implements handler_t -.
 
int crypt_smime_application_handler (struct Body *b_email, struct State *state)
 Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.
 

Detailed Description

Prototype for a function to handle MIME parts.

Parameters
b_emailBody of the email
stateState of text being processed
Return values
0Success
-1Error

Function Documentation

◆ text_enriched_handler()

int text_enriched_handler ( struct Body * b_email,
struct State * state )

Handler for enriched text - Implements handler_t -.

Return values
0Always

Definition at line 473 of file enriched.c.

474{
475 enum
476 {
477 TEXT,
478 LANGLE,
479 TAG,
480 BOGUS_TAG,
481 NEWLINE,
482 ST_EOF,
483 DONE
484 } text_state = TEXT;
485
486 long bytes = b_email->length;
487 struct EnrichedState enriched = { 0 };
488 wint_t wc = 0;
489 int tag_len = 0;
490 wchar_t tag[1024 + 1];
491
492 enriched.state = state;
493 enriched.wrap_margin = ((state->wraplen > 4) &&
494 ((state->flags & STATE_DISPLAY) || (state->wraplen < 76))) ?
495 state->wraplen - 4 :
496 72;
497 enriched.line_max = enriched.wrap_margin * 4;
498 enriched.line = MUTT_MEM_CALLOC(enriched.line_max + 1, wchar_t);
499 enriched.param = MUTT_MEM_CALLOC(256, wchar_t);
500
501 enriched.param_len = 256;
502 enriched.param_used = 0;
503
504 if (state->prefix)
505 {
507 enriched.indent_len += mutt_str_len(state->prefix);
508 }
509
510 while (text_state != DONE)
511 {
512 if (text_state != ST_EOF)
513 {
514 if (!bytes || ((wc = fgetwc(state->fp_in)) == WEOF))
515 text_state = ST_EOF;
516 else
517 bytes--;
518 }
519
520 switch (text_state)
521 {
522 case TEXT:
523 switch (wc)
524 {
525 case '<':
526 text_state = LANGLE;
527 break;
528
529 case '\n':
530 if (enriched.tag_level[RICH_NOFILL])
531 {
532 enriched_flush(&enriched, true);
533 }
534 else
535 {
536 enriched_putwc((wchar_t) ' ', &enriched);
537 text_state = NEWLINE;
538 }
539 break;
540
541 default:
542 enriched_putwc(wc, &enriched);
543 }
544 break;
545
546 case LANGLE:
547 if (wc == (wchar_t) '<')
548 {
549 enriched_putwc(wc, &enriched);
550 text_state = TEXT;
551 break;
552 }
553 else
554 {
555 tag_len = 0;
556 text_state = TAG;
557 }
558 /* Yes, (it wasn't a <<, so this char is first in TAG) */
560
561 case TAG:
562 if (wc == (wchar_t) '>')
563 {
564 tag[tag_len] = (wchar_t) '\0';
565 enriched_set_flags(tag, &enriched);
566 text_state = TEXT;
567 }
568 else if (tag_len < 1024) /* ignore overly long tags */
569 {
570 tag[tag_len++] = wc;
571 }
572 else
573 {
574 text_state = BOGUS_TAG;
575 }
576 break;
577
578 case BOGUS_TAG:
579 if (wc == (wchar_t) '>')
580 text_state = TEXT;
581 break;
582
583 case NEWLINE:
584 if (wc == (wchar_t) '\n')
585 {
586 enriched_flush(&enriched, true);
587 }
588 else
589 {
590 ungetwc(wc, state->fp_in);
591 bytes++;
592 text_state = TEXT;
593 }
594 break;
595
596 case ST_EOF:
597 enriched_putwc((wchar_t) '\0', &enriched);
598 enriched_flush(&enriched, true);
599 text_state = DONE;
600 break;
601
602 default:
603 /* not reached */
604 break;
605 }
606 }
607
608 state_putc(state, '\n'); /* add a final newline */
609
610 FREE(&(enriched.buffer));
611 FREE(&(enriched.line));
612 FREE(&(enriched.param));
613
614 return 0;
615}
static void enriched_set_flags(const wchar_t *tag, struct EnrichedState *enriched)
Set flags on the enriched text state.
Definition enriched.c:381
@ RICH_NOFILL
Text will not be reformatted.
Definition enriched.c:54
static void enriched_putwc(wchar_t c, struct EnrichedState *enriched)
Write one wide character to the state.
Definition enriched.c:279
static void enriched_flush(struct EnrichedState *enriched, bool wrap)
Write enriched text to the State.
Definition enriched.c:242
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define FALLTHROUGH
Definition lib.h:117
#define state_puts(STATE, STR)
Definition state.h:58
#define STATE_DISPLAY
Output is displayed to the user.
Definition state.h:33
#define state_putc(STATE, STR)
Definition state.h:59
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
State of enriched-text parser.
Definition enriched.c:99
wchar_t * buffer
Output buffer.
Definition enriched.c:100
size_t param_used
Used bytes in param buffer.
Definition enriched.c:110
wchar_t * param
Current parameter.
Definition enriched.c:102
wchar_t * line
Current line.
Definition enriched.c:101
int tag_level[RICH_MAX]
Nesting level of each tag type.
Definition enriched.c:112
struct State * state
State wrapper.
Definition enriched.c:114
size_t param_len
Capacity of param buffer.
Definition enriched.c:111
size_t line_max
Maximum line width.
Definition enriched.c:106
int wrap_margin
Wrap margin.
Definition enriched.c:113
size_t indent_len
Current indentation level.
Definition enriched.c:107
int wraplen
Width to wrap lines to (when flags & STATE_DISPLAY)
Definition state.h:53
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition state.h:52
FILE * fp_in
File to read from.
Definition state.h:49
const char * prefix
String to add to the beginning of each output line.
Definition state.h:51
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ autoview_handler()

static int autoview_handler ( struct Body * b_email,
struct State * state )
static

Handler for autoviewable attachments - Implements handler_t -.

Definition at line 541 of file handler.c.

542{
543 struct MailcapEntry *entry = mailcap_entry_new();
544 char buf[1024] = { 0 };
545 char type[256] = { 0 };
546 struct Buffer *cmd = buf_pool_get();
547 struct Buffer *tempfile = buf_pool_get();
548 char *fname = NULL;
549 FILE *fp_in = NULL;
550 FILE *fp_out = NULL;
551 FILE *fp_err = NULL;
552 pid_t pid;
553 int rc = 0;
554
555 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b_email), b_email->subtype);
556 mailcap_lookup(b_email, type, sizeof(type), entry, MUTT_MC_AUTOVIEW);
557
558 fname = mutt_str_dup(b_email->filename);
559 mutt_file_sanitize_filename(fname, true);
560 mailcap_expand_filename(entry->nametemplate, fname, tempfile);
561 FREE(&fname);
562
563 if (entry->command)
564 {
565 buf_strcpy(cmd, entry->command);
566
567 /* mailcap_expand_command returns 0 if the file is required */
568 bool piped = mailcap_expand_command(b_email, buf_string(tempfile), type, cmd);
569
570 if (state->flags & STATE_DISPLAY)
571 {
572 state_mark_attach(state);
573 state_printf(state, _("[-- Autoview using %s --]\n"), buf_string(cmd));
574 mutt_message(_("Invoking autoview command: %s"), buf_string(cmd));
575 }
576
577 fp_in = mutt_file_fopen(buf_string(tempfile), "w+");
578 if (!fp_in)
579 {
580 mutt_perror("fopen");
581 mailcap_entry_free(&entry);
582 rc = -1;
583 goto cleanup;
584 }
585
586 mutt_file_copy_bytes(state->fp_in, fp_in, b_email->length);
587
588 if (piped)
589 {
590 unlink(buf_string(tempfile));
591 fflush(fp_in);
592 rewind(fp_in);
593 pid = filter_create_fd(buf_string(cmd), NULL, &fp_out, &fp_err,
594 fileno(fp_in), -1, -1, NeoMutt->env);
595 }
596 else
597 {
598 mutt_file_fclose(&fp_in);
599 pid = filter_create(buf_string(cmd), NULL, &fp_out, &fp_err, NeoMutt->env);
600 }
601
602 if (pid < 0)
603 {
604 mutt_perror(_("Can't create filter"));
605 if (state->flags & STATE_DISPLAY)
606 {
607 state_mark_attach(state);
608 state_printf(state, _("[-- Can't run %s --]\n"), buf_string(cmd));
609 }
610 rc = -1;
611 goto bail;
612 }
613
614 if (state->prefix)
615 {
616 /* Remove ansi and formatting from autoview output in replies only. The
617 * user may want to see the formatting in the pager, but it shouldn't be
618 * in their quoted reply text too. */
619 struct Buffer *stripped = buf_pool_get();
620 while (fgets(buf, sizeof(buf), fp_out))
621 {
622 buf_strip_formatting(stripped, buf, false);
623 state_puts(state, state->prefix);
624 state_puts(state, buf_string(stripped));
625 }
626 buf_pool_release(&stripped);
627
628 /* check for data on stderr */
629 if (fgets(buf, sizeof(buf), fp_err))
630 {
631 if (state->flags & STATE_DISPLAY)
632 {
633 state_mark_attach(state);
634 state_printf(state, _("[-- Autoview stderr of %s --]\n"), buf_string(cmd));
635 }
636
637 state_puts(state, state->prefix);
638 state_puts(state, buf);
639 while (fgets(buf, sizeof(buf), fp_err))
640 {
641 state_puts(state, state->prefix);
642 state_puts(state, buf);
643 }
644 }
645 }
646 else
647 {
648 mutt_file_copy_stream(fp_out, state->fp_out);
649 /* Check for stderr messages */
650 if (fgets(buf, sizeof(buf), fp_err))
651 {
652 if (state->flags & STATE_DISPLAY)
653 {
654 state_mark_attach(state);
655 state_printf(state, _("[-- Autoview stderr of %s --]\n"), buf_string(cmd));
656 }
657
658 state_puts(state, buf);
659 mutt_file_copy_stream(fp_err, state->fp_out);
660 }
661 }
662
663 bail:
664 mutt_file_fclose(&fp_out);
665 mutt_file_fclose(&fp_err);
666
667 filter_wait(pid);
668 if (piped)
669 mutt_file_fclose(&fp_in);
670 else
671 mutt_file_unlink(buf_string(tempfile));
672
673 if (state->flags & STATE_DISPLAY)
675 }
676
677cleanup:
678 mailcap_entry_free(&entry);
679
680 buf_pool_release(&cmd);
681 buf_pool_release(&tempfile);
682
683 return rc;
684}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition display.c:736
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
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
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition file.c:582
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition file.c:156
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
#define mutt_message(...)
Definition logging2.h:93
#define mutt_perror(...)
Definition logging2.h:95
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition mailcap.c:455
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition mailcap.c:446
int mailcap_expand_command(struct Body *b, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition mailcap.c:70
void mailcap_expand_filename(const char *nametemplate, const char *oldfile, struct Buffer *newfile)
Expand a new filename from a template or existing filename.
Definition mailcap.c:553
bool mailcap_lookup(struct Body *b, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition mailcap.c:484
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition mailcap.h:61
#define BODY_TYPE(body)
Get the type name of a body part.
Definition mime.h:93
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:228
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr, char **envlist)
Run a command on a pipe (optionally connect stdin/stdout)
Definition filter.c:62
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition filter.c:217
#define _(a)
Definition message.h:28
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
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
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
char * subtype
content-type subtype
Definition body.h:61
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
A mailcap entry.
Definition mailcap.h:37
char * nametemplate
Filename template.
Definition mailcap.h:44
char * command
Command to run.
Definition mailcap.h:38
Container for Accounts, Notifications.
Definition neomutt.h:41
char ** env
Private copy of the environment variables.
Definition neomutt.h:57
FILE * fp_out
File to write to.
Definition state.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ text_plain_handler()

static int text_plain_handler ( struct Body * b_email,
struct State * state )
static

Handler for plain text - Implements handler_t -.

Return values
0Always

When generating format=flowed ($text_flowed is set) from format=fixed, strip all trailing spaces to improve interoperability; if $text_flowed is unset, simply verbatim copy input.

Definition at line 694 of file handler.c.

695{
696 char *buf = NULL;
697 size_t sz = 0;
698
699 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
700 while ((buf = mutt_file_read_line(buf, &sz, state->fp_in, NULL, MUTT_RL_NO_FLAGS)))
701 {
702 if (!mutt_str_equal(buf, "-- ") && c_text_flowed)
703 {
704 size_t len = mutt_str_len(buf);
705 while ((len > 0) && (buf[len - 1] == ' '))
706 buf[--len] = '\0';
707 }
708 if (state->prefix)
709 state_puts(state, state->prefix);
710 state_puts(state, buf);
711 state_putc(state, '\n');
712 }
713
714 FREE(&buf);
715 return 0;
716}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
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
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition file.h:40
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ message_handler()

static int message_handler ( struct Body * b_email,
struct State * state )
static

Handler for message/rfc822 body parts - Implements handler_t -.

Definition at line 721 of file handler.c.

722{
723 struct Body *b = NULL;
724 LOFF_T off_start;
725 int rc = 0;
726
727 off_start = ftello(state->fp_in);
728 if (off_start < 0)
729 return -1;
730
731 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
732 (b_email->encoding == ENC_UUENCODED))
733 {
734 b = mutt_body_new();
736 b->parts = mutt_rfc822_parse_message(state->fp_in, b);
737 }
738 else
739 {
740 b = b_email;
741 }
742
743 if (b->parts)
744 {
746 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
747 if ((state->flags & STATE_WEED) ||
748 ((state->flags & (STATE_DISPLAY | STATE_PRINTING)) && c_weed))
749 {
750 chflags |= CH_WEED | CH_REORDER;
751 }
752 if (state->prefix)
753 chflags |= CH_PREFIX;
754 if (state->flags & STATE_DISPLAY)
755 chflags |= CH_DISPLAY;
756
757 mutt_copy_hdr(state->fp_in, state->fp_out, off_start, b->parts->offset,
758 chflags, state->prefix, 0);
759
760 if (state->prefix)
761 state_puts(state, state->prefix);
762 state_putc(state, '\n');
763
764 rc = mutt_body_handler(b->parts, state);
765 }
766
767 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
768 (b_email->encoding == ENC_UUENCODED))
769 {
770 mutt_body_free(&b);
771 }
772
773 return rc;
774}
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
#define CH_DECODE
Do RFC2047 header decoding.
Definition copy_email.h:58
#define CH_PREFIX
Quote header using $indent_string string?
Definition copy_email.h:61
#define CH_FROM
Retain the "From " message separator?
Definition copy_email.h:60
#define CH_WEED
Weed the headers?
Definition copy_email.h:57
#define CH_REORDER
Re-order output of headers (specified by 'header-order')
Definition copy_email.h:63
#define CH_DISPLAY
Display result to user.
Definition copy_email.h:74
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:54
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
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *b)
Parse a Message/RFC822 body.
Definition parse.c:1809
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1432
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1664
@ ENC_UUENCODED
UUEncoded text.
Definition mime.h:54
@ ENC_BASE64
Base-64 encoded text.
Definition mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition mime.h:51
#define STATE_WEED
Weed headers even when not in display mode.
Definition state.h:36
#define STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition state.h:38
The body of an email.
Definition body.h:36
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
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ external_body_handler()

static int external_body_handler ( struct Body * b_email,
struct State * state )
static

Handler for external-body emails - Implements handler_t -.

Definition at line 779 of file handler.c.

780{
781 const char *access_type = mutt_param_get(&b_email->parameter, "access-type");
782 if (!access_type)
783 {
784 if (state->flags & STATE_DISPLAY)
785 {
786 state_mark_attach(state);
787 state_puts(state, _("[-- Error: message/external-body has no access-type parameter --]\n"));
788 return 0;
789 }
790 else
791 {
792 return -1;
793 }
794 }
795
796 const char *fmt = NULL;
797 struct Buffer *banner = buf_pool_get();
798
799 const char *expiration = mutt_param_get(&b_email->parameter, "expiration");
800 time_t expire;
801 if (expiration)
802 expire = mutt_date_parse_date(expiration, NULL);
803 else
804 expire = -1;
805
806 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
807 if (mutt_istr_equal(access_type, "x-mutt-deleted"))
808 {
809 if (state->flags & (STATE_DISPLAY | STATE_PRINTING))
810 {
811 struct Buffer *pretty_size = buf_pool_get();
812 char *length = mutt_param_get(&b_email->parameter, "length");
813 if (length)
814 {
815 const long size = strtol(length, NULL, 10);
816 mutt_str_pretty_size(pretty_size, size);
817 if (expire != -1)
818 {
819 fmt = ngettext(
820 /* L10N: If the translation of this string is a multi line string, then
821 each line should start with "[-- " and end with " --]".
822 The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
823 expands to a date as returned by `mutt_date_parse_date()`.
824
825 Note: The size argument printed is not the actual number as passed
826 to gettext but the prettified version, e.g. size = 2048 will be
827 printed as 2K. Your language might be sensitive to that: For
828 example although '1K' and '1024' represent the same number your
829 language might inflect the noun 'byte' differently.
830
831 Sadly, we can't do anything about that at the moment besides
832 passing the precise size in bytes. If you are interested the
833 function responsible for the prettification is
834 mutt_str_pretty_size() in muttlib.c */
835 "[-- This %s/%s attachment (size %s byte) has been deleted --]\n"
836 "[-- on %s --]\n",
837 "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n"
838 "[-- on %s --]\n",
839 size);
840 }
841 else
842 {
843 fmt = ngettext(
844 /* L10N: If the translation of this string is a multi line string, then
845 each line should start with "[-- " and end with " --]".
846 The first "%s/%s" is a MIME type, e.g. "text/plain".
847
848 Note: The size argument printed is not the actual number as passed
849 to gettext but the prettified version, e.g. size = 2048 will be
850 printed as 2K. Your language might be sensitive to that: For
851 example although '1K' and '1024' represent the same number your
852 language might inflect the noun 'byte' differently.
853
854 Sadly, we can't do anything about that at the moment besides
855 passing the precise size in bytes. If you are interested the
856 function responsible for the prettification is
857 mutt_str_pretty_size() in muttlib.c */
858 "[-- This %s/%s attachment (size %s byte) has been deleted --]\n",
859 "[-- This %s/%s attachment (size %s bytes) has been deleted --]\n", size);
860 }
861 }
862 else
863 {
864 if (expire != -1)
865 {
866 /* L10N: If the translation of this string is a multi line string, then
867 each line should start with "[-- " and end with " --]".
868 The first "%s/%s" is a MIME type, e.g. "text/plain". The last %s
869 expands to a date as returned by `mutt_date_parse_date()`.
870
871 Caution: Argument three %3$ is also defined but should not be used
872 in this translation! */
873 fmt = _("[-- This %s/%s attachment has been deleted --]\n[-- on %4$s --]\n");
874 }
875 else
876 {
877 /* L10N: If the translation of this string is a multi line string, then
878 each line should start with "[-- " and end with " --]".
879 The first "%s/%s" is a MIME type, e.g. "text/plain". */
880 fmt = _("[-- This %s/%s attachment has been deleted --]\n");
881 }
882 }
883
884 buf_printf(banner, fmt, BODY_TYPE(b_email->parts),
885 b_email->parts->subtype, buf_string(pretty_size), expiration);
886 state_attach_puts(state, buf_string(banner));
887 if (b_email->parts->filename)
888 {
889 state_mark_attach(state);
890 state_printf(state, _("[-- name: %s --]\n"), b_email->parts->filename);
891 }
892
893 CopyHeaderFlags chflags = CH_DECODE;
894 if (c_weed)
895 chflags |= CH_WEED | CH_REORDER;
896
897 mutt_copy_hdr(state->fp_in, state->fp_out, ftello(state->fp_in),
898 b_email->parts->offset, chflags, NULL, 0);
899 buf_pool_release(&pretty_size);
900 }
901 }
902 else if (expiration && (expire < mutt_date_now()))
903 {
904 if (state->flags & STATE_DISPLAY)
905 {
906 /* L10N: If the translation of this string is a multi line string, then
907 each line should start with "[-- " and end with " --]".
908 The "%s/%s" is a MIME type, e.g. "text/plain". */
909 buf_printf(banner, _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated external source has expired --]\n"),
910 BODY_TYPE(b_email->parts), b_email->parts->subtype);
911 state_attach_puts(state, buf_string(banner));
912
914 if (c_weed)
915 chflags |= CH_WEED | CH_REORDER;
916
917 mutt_copy_hdr(state->fp_in, state->fp_out, ftello(state->fp_in),
918 b_email->parts->offset, chflags, NULL, 0);
919 }
920 }
921 else
922 {
923 if (state->flags & STATE_DISPLAY)
924 {
925 /* L10N: If the translation of this string is a multi line string, then
926 each line should start with "[-- " and end with " --]".
927 The "%s/%s" is a MIME type, e.g. "text/plain". The %s after
928 access-type is an access-type as defined by the MIME RFCs, e.g. "FTP",
929 "LOCAL-FILE", "MAIL-SERVER". */
930 buf_printf(banner, _("[-- This %s/%s attachment is not included, --]\n[-- and the indicated access-type %s is unsupported --]\n"),
931 BODY_TYPE(b_email->parts), b_email->parts->subtype, access_type);
932 state_attach_puts(state, buf_string(banner));
933
935 if (c_weed)
936 chflags |= CH_WEED | CH_REORDER;
937
938 mutt_copy_hdr(state->fp_in, state->fp_out, ftello(state->fp_in),
939 b_email->parts->offset, chflags, NULL, 0);
940 }
941 }
942 buf_pool_release(&banner);
943
944 return 0;
945}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:717
void state_attach_puts(struct State *state, const char *t)
Write a string to the state.
Definition state.c:104
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:677
int mutt_str_pretty_size(struct Buffer *buf, size_t num)
Display an abbreviated size, like 3.4K.
Definition muttlib.c:935
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition parameter.c:85
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ alternative_handler()

static int alternative_handler ( struct Body * b_email,
struct State * state )
static

Handler for multipart alternative emails - Implements handler_t -.

Definition at line 950 of file handler.c.

951{
952 struct Body *const head = b_email;
953 struct Body *choice = NULL;
954 struct Body *b = NULL;
955 bool mustfree = false;
956 int rc = 0;
957
958 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
959 (b_email->encoding == ENC_UUENCODED))
960 {
961 mustfree = true;
962 b = mutt_body_new();
964 b->parts = mutt_parse_multipart(state->fp_in,
965 mutt_param_get(&b_email->parameter, "boundary"),
966 b->length,
967 mutt_istr_equal("digest", b_email->subtype));
968 }
969 else
970 {
971 b = b_email;
972 }
973
974 b_email = b;
975
977 ASSERT(mod_data);
978
979 /* First, search list of preferred types */
980 struct ListNode *np = NULL;
981 STAILQ_FOREACH(np, &mod_data->alternative_order, entries)
982 {
983 int btlen; /* length of basetype */
984 bool wild; /* do we have a wildcard to match all subtypes? */
985
986 char *c = strchr(np->data, '/');
987 if (c)
988 {
989 wild = ((c[1] == '*') && (c[2] == '\0'));
990 btlen = c - np->data;
991 }
992 else
993 {
994 wild = true;
995 btlen = mutt_str_len(np->data);
996 }
997
998 if (b_email->parts)
999 b = b_email->parts;
1000 else
1001 b = b_email;
1002 while (b)
1003 {
1004 const char *bt = BODY_TYPE(b);
1005 if (mutt_istrn_equal(bt, np->data, btlen) && (bt[btlen] == 0))
1006 {
1007 /* the basetype matches */
1008 if (wild || mutt_istr_equal(np->data + btlen + 1, b->subtype))
1009 {
1010 choice = b;
1011 }
1012 }
1013 b = b->next;
1014 }
1015
1016 if (choice)
1017 break;
1018 }
1019
1020 /* Next, look for an autoviewable type */
1021 if (!choice)
1022 {
1023 if (b_email->parts)
1024 b = b_email->parts;
1025 else
1026 b = b_email;
1027 while (b)
1028 {
1029 if (is_autoview(b))
1030 choice = b;
1031 b = b->next;
1032 }
1033 }
1034
1035 /* Then, look for a text entry */
1036 if (!choice)
1037 {
1038 if (b_email->parts)
1039 b = b_email->parts;
1040 else
1041 b = b_email;
1042 int type = 0;
1043 while (b)
1044 {
1045 if (b->type == TYPE_TEXT)
1046 {
1047 if (mutt_istr_equal("plain", b->subtype) && (type <= TXT_PLAIN))
1048 {
1049 choice = b;
1050 type = TXT_PLAIN;
1051 }
1052 else if (mutt_istr_equal("enriched", b->subtype) && (type <= TXT_ENRICHED))
1053 {
1054 choice = b;
1055 type = TXT_ENRICHED;
1056 }
1057 else if (mutt_istr_equal("html", b->subtype) && (type <= TXT_HTML))
1058 {
1059 choice = b;
1060 type = TXT_HTML;
1061 }
1062 }
1063 b = b->next;
1064 }
1065 }
1066
1067 /* Finally, look for other possibilities */
1068 if (!choice)
1069 {
1070 if (b_email->parts)
1071 b = b_email->parts;
1072 else
1073 b = b_email;
1074 while (b)
1075 {
1076 if (mutt_can_decode(b))
1077 choice = b;
1078 b = b->next;
1079 }
1080 }
1081
1082 if (choice)
1083 {
1084 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1085 if (state->flags & STATE_DISPLAY && !c_weed &&
1086 mutt_file_seek(state->fp_in, choice->hdr_offset, SEEK_SET))
1087 {
1088 mutt_file_copy_bytes(state->fp_in, state->fp_out, choice->offset - choice->hdr_offset);
1089 }
1090
1091 const char *const c_show_multipart_alternative = cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1092 if (mutt_str_equal("info", c_show_multipart_alternative))
1093 {
1094 print_part_line(state, choice, 0);
1095 }
1096 mutt_body_handler(choice, state);
1097
1098 /* Let it flow back to the main part */
1099 head->nowrap = choice->nowrap;
1100 choice->nowrap = false;
1101
1102 if (mutt_str_equal("info", c_show_multipart_alternative))
1103 {
1104 if (b_email->parts)
1105 b = b_email->parts;
1106 else
1107 b = b_email;
1108 int count = 0;
1109 while (b)
1110 {
1111 if (choice != b)
1112 {
1113 count += 1;
1114 if (count == 1)
1115 state_putc(state, '\n');
1116
1117 print_part_line(state, b, count);
1118 }
1119 b = b->next;
1120 }
1121 }
1122 }
1123 else if (state->flags & STATE_DISPLAY)
1124 {
1125 /* didn't find anything that we could display! */
1126 state_mark_attach(state);
1127 state_puts(state, _("[-- Error: Could not display any parts of Multipart/Alternative --]\n"));
1128 rc = -1;
1129 }
1130
1131 if (mustfree)
1132 mutt_body_free(&b_email);
1133
1134 return rc;
1135}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition parse.c:1825
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
static bool is_autoview(struct Body *b)
Should email body be filtered by mailcap.
Definition handler.c:491
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition handler.c:1898
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
#define TXT_HTML
HTML text format.
Definition handler.c:75
#define TXT_ENRICHED
Enriched text format.
Definition handler.c:77
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
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
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:665
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define ASSERT(COND)
Definition signal2.h:59
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
long hdr_offset
Offset in stream where the headers begin.
Definition body.h:81
unsigned int type
content-type primary type, ContentType
Definition body.h:40
Email private Module data.
Definition module_data.h:32
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ multilingual_handler()

static int multilingual_handler ( struct Body * b_email,
struct State * state )
static

Handler for multi-lingual emails - Implements handler_t -.

Return values
0Always

Definition at line 1141 of file handler.c.

1142{
1143 struct Body *b = NULL;
1144 bool mustfree = false;
1145 int rc = 0;
1146
1147 mutt_debug(LL_DEBUG2, "RFC8255 >> entering in handler multilingual handler\n");
1148 /* If the body is transfer-encoded, decode it and re-parse the MIME parts */
1149 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1150 (b_email->encoding == ENC_UUENCODED))
1151 {
1152 mustfree = true;
1153 b = mutt_body_new();
1154 b->length = mutt_file_get_size_fp(state->fp_in);
1155 b->parts = mutt_parse_multipart(state->fp_in,
1156 mutt_param_get(&b_email->parameter, "boundary"),
1157 b->length,
1158 mutt_istr_equal("digest", b_email->subtype));
1159 }
1160 else
1161 {
1162 b = b_email;
1163 }
1164
1165 b_email = b;
1166
1167 if (b_email->parts)
1168 b = b_email->parts;
1169 else
1170 b = b_email;
1171
1172 struct Body *choice = NULL;
1173 struct Body *first_part = NULL;
1174 struct Body *zxx_part = NULL;
1175 struct ListNode *np = NULL;
1176
1177 /* Find the first decodable part as a fallback */
1178 while (b)
1179 {
1180 if (mutt_can_decode(b))
1181 {
1182 first_part = b;
1183 break;
1184 }
1185 b = b->next;
1186 }
1187
1188 /* Search for a part matching the user's preferred languages (RFC 8255).
1189 * Also track any "zxx" (no linguistic content) part as a secondary fallback. */
1190 const struct Slist *c_preferred_languages = cs_subset_slist(NeoMutt->sub, "preferred_languages");
1191 if (c_preferred_languages)
1192 {
1193 struct Buffer *langs = buf_pool_get();
1194 cs_subset_str_string_get(NeoMutt->sub, "preferred_languages", langs);
1195 mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_languages set in config to '%s'\n",
1196 buf_string(langs));
1197 buf_pool_release(&langs);
1198
1199 STAILQ_FOREACH(np, &c_preferred_languages->head, entries)
1200 {
1201 while (b)
1202 {
1203 if (mutt_can_decode(b))
1204 {
1205 if (b->language && mutt_str_equal("zxx", b->language))
1206 zxx_part = b;
1207
1208 mutt_debug(LL_DEBUG2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n",
1209 np->data, b->language);
1210 if (b->language && mutt_str_equal(np->data, b->language))
1211 {
1212 mutt_debug(LL_DEBUG2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n",
1213 np->data, b->language);
1214 choice = b;
1215 break;
1216 }
1217 }
1218
1219 b = b->next;
1220 }
1221
1222 if (choice)
1223 break;
1224
1225 if (b_email->parts)
1226 b = b_email->parts;
1227 else
1228 b = b_email;
1229 }
1230 }
1231
1232 /* Display the best match: preferred language > zxx > first decodable part */
1233 if (choice)
1234 {
1235 mutt_body_handler(choice, state);
1236 }
1237 else
1238 {
1239 if (zxx_part)
1240 mutt_body_handler(zxx_part, state);
1241 else if (first_part)
1242 mutt_body_handler(first_part, state);
1243 }
1244
1245 if (mustfree)
1246 mutt_body_free(&b_email);
1247
1248 return rc;
1249}
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
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
char * language
content-language (RFC8255)
Definition body.h:78
String list.
Definition slist.h:37
struct ListHead head
List containing values.
Definition slist.h:38
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ multipart_handler()

static int multipart_handler ( struct Body * b_email,
struct State * state )
static

Handler for multipart emails - Implements handler_t -.

Definition at line 1254 of file handler.c.

1255{
1256 struct Body *b = NULL, *p = NULL;
1257 int count;
1258 int rc = 0;
1259
1260 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1261 (b_email->encoding == ENC_UUENCODED))
1262 {
1263 b = mutt_body_new();
1264 b->length = mutt_file_get_size_fp(state->fp_in);
1265 b->parts = mutt_parse_multipart(state->fp_in,
1266 mutt_param_get(&b_email->parameter, "boundary"),
1267 b->length,
1268 mutt_istr_equal("digest", b_email->subtype));
1269 }
1270 else
1271 {
1272 b = b_email;
1273 }
1274
1275 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1276 const bool c_include_only_first = cs_subset_bool(NeoMutt->sub, "include_only_first");
1277
1278 for (p = b->parts, count = 1; p; p = p->next, count++)
1279 {
1280 if (state->flags & STATE_DISPLAY)
1281 {
1282 state_mark_attach(state);
1283 if (p->description || p->filename || p->form_name)
1284 {
1285 /* L10N: %s is the attachment description, filename or form_name. */
1286 state_printf(state, _("[-- Attachment #%d: %s --]\n"), count,
1287 p->description ? p->description :
1288 p->filename ? p->filename :
1289 p->form_name);
1290 }
1291 else
1292 {
1293 state_printf(state, _("[-- Attachment #%d --]\n"), count);
1294 }
1295 print_part_line(state, p, 0);
1296 if (c_weed)
1297 {
1298 state_putc(state, '\n');
1299 }
1300 else if (mutt_file_seek(state->fp_in, p->hdr_offset, SEEK_SET))
1301 {
1302 mutt_file_copy_bytes(state->fp_in, state->fp_out, p->offset - p->hdr_offset);
1303 }
1304 }
1305
1306 rc = mutt_body_handler(p, state);
1307 state_putc(state, '\n');
1308
1309 if (rc != 0)
1310 {
1311 mutt_error(_("One or more parts of this message could not be displayed"));
1312 mutt_debug(LL_DEBUG1, "Failed on attachment #%d, type %s/%s\n", count,
1313 BODY_TYPE(p), NONULL(p->subtype));
1314 }
1315
1316 if ((state->flags & STATE_REPLYING) && c_include_only_first && (state->flags & STATE_FIRSTDONE))
1317 {
1318 break;
1319 }
1320 }
1321
1322 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
1323 (b_email->encoding == ENC_UUENCODED))
1324 {
1325 mutt_body_free(&b);
1326 }
1327
1328 /* make failure of a single part non-fatal */
1329 if (rc < 0)
1330 rc = 1;
1331 return rc;
1332}
#define mutt_error(...)
Definition logging2.h:94
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define STATE_FIRSTDONE
The first attachment has been done.
Definition state.h:40
#define STATE_REPLYING
Are we replying?
Definition state.h:39
#define NONULL(x)
Definition string2.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ valid_pgp_encrypted_handler()

static int valid_pgp_encrypted_handler ( struct Body * b_email,
struct State * state )
static

Handler for valid pgp-encrypted emails - Implements handler_t -.

Definition at line 1501 of file handler.c.

1502{
1503 struct Body *octetstream = b_email->parts->next;
1504
1505 /* clear out any mime headers before the handler, so they can't be spoofed. */
1506 mutt_env_free(&b_email->mime_headers);
1507 mutt_env_free(&octetstream->mime_headers);
1508
1509 int rc;
1510 /* Some clients improperly encode the octetstream part. */
1511 if (octetstream->encoding != ENC_7BIT)
1512 rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, 0);
1513 else
1514 rc = crypt_pgp_encrypted_handler(octetstream, state);
1515 b_email->goodsig |= octetstream->goodsig;
1516
1517 /* Relocate protected headers onto the multipart/encrypted part */
1518 if (!rc && octetstream->mime_headers)
1519 {
1520 b_email->mime_headers = octetstream->mime_headers;
1521 octetstream->mime_headers = NULL;
1522 }
1523
1524 return rc;
1525}
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
int crypt_pgp_encrypted_handler(struct Body *b_email, struct State *state)
Wrapper for CryptModuleSpecs::encrypted_handler() - Implements handler_t -.
Definition cryptglue.c:260
static int run_decode_and_handler(struct Body *b, struct State *state, handler_t handler, bool plaintext)
Run an appropriate decoder for an email.
Definition handler.c:1343
@ ENC_7BIT
7-bit text
Definition mime.h:49
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
bool goodsig
Good cryptographic signature.
Definition body.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ malformed_pgp_encrypted_handler()

static int malformed_pgp_encrypted_handler ( struct Body * b_email,
struct State * state )
static

Handler for invalid pgp-encrypted emails - Implements handler_t -.

Definition at line 1530 of file handler.c.

1531{
1532 if (!b_email->parts || !b_email->parts->next || !b_email->parts->next->next)
1533 return -1;
1534
1535 struct Body *octetstream = b_email->parts->next->next;
1536
1537 /* clear out any mime headers before the handler, so they can't be spoofed. */
1538 mutt_env_free(&b_email->mime_headers);
1539 mutt_env_free(&octetstream->mime_headers);
1540
1541 /* exchange encodes the octet-stream, so re-run it through the decoder */
1542 int rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, false);
1543 b_email->goodsig |= octetstream->goodsig;
1544#ifdef USE_AUTOCRYPT
1545 b_email->is_autocrypt |= octetstream->is_autocrypt;
1546#endif
1547
1548 /* Relocate protected headers onto the multipart/encrypted part */
1549 if (!rc && octetstream->mime_headers)
1550 {
1551 b_email->mime_headers = octetstream->mime_headers;
1552 octetstream->mime_headers = NULL;
1553 }
1554
1555 return rc;
1556}
bool is_autocrypt
Flag autocrypt-decrypted messages for replying.
Definition body.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rfc3676_handler()

int rfc3676_handler ( struct Body * b_email,
struct State * state )

Handler for format=flowed - Implements handler_t -.

Return values
0Always

Definition at line 327 of file rfc3676.c.

328{
329 char *buf = NULL;
330 unsigned int quotelevel = 0;
331 bool delsp = false;
332 size_t sz = 0;
333 struct FlowedState fst = { 0 };
334
335 /* respect DelSp of RFC3676 only with f=f parts */
336 char *t = mutt_param_get(&b_email->parameter, "delsp");
337 if (t)
338 {
339 delsp = mutt_istr_equal(t, "yes");
340 t = NULL;
341 fst.delsp = true;
342 }
343
344 mutt_debug(LL_DEBUG3, "f=f: DelSp: %s\n", delsp ? "yes" : "no");
345
346 while ((buf = mutt_file_read_line(buf, &sz, state->fp_in, NULL, MUTT_RL_NO_FLAGS)))
347 {
348 const size_t buflen = mutt_str_len(buf);
349 const unsigned int newql = get_quote_level(buf);
350
351 /* end flowed paragraph (if we're within one) if quoting level
352 * changes (should not but can happen, see RFC3676, sec. 4.5.) */
353 if (newql != quotelevel)
354 flush_par(state, &fst);
355
356 quotelevel = newql;
357 int buf_off = newql;
358
359 /* respect sender's space-stuffing by removing one leading space */
360 if (buf[buf_off] == ' ')
361 buf_off++;
362
363 /* test for signature separator */
364 const unsigned int sigsep = mutt_str_equal(buf + buf_off, "-- ");
365
366 /* a fixed line either has no trailing space or is the
367 * signature separator */
368 const bool fixed = (buflen == buf_off) || (buf[buflen - 1] != ' ') || sigsep;
369
370 /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
371 * fixed lines */
372 if ((fixed && ((fst.width == 0) || (buflen == 0))) || sigsep)
373 {
374 /* if we're within a flowed paragraph, terminate it */
375 flush_par(state, &fst);
376 print_fixed_line(buf + buf_off, state, quotelevel, &fst);
377 continue;
378 }
379
380 /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
381 if (delsp && !fixed)
382 buf[buflen - 1] = '\0';
383
384 print_flowed_line(buf + buf_off, state, quotelevel, &fst, fixed);
385 }
386
387 flush_par(state, &fst);
388
389 FREE(&buf);
390 return 0;
391}
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
static void print_fixed_line(const char *line, struct State *state, int ql, struct FlowedState *fst)
Print a fixed format line.
Definition rfc3676.c:311
static int get_quote_level(const char *line)
Get the quote level of a line.
Definition rfc3676.c:67
static void print_flowed_line(char *line, struct State *state, int ql, struct FlowedState *fst, bool term)
Print a format-flowed line.
Definition rfc3676.c:232
static void flush_par(struct State *state, struct FlowedState *fst)
Write out the paragraph.
Definition rfc3676.c:179
State of a Format-Flowed line of text.
Definition rfc3676.c:56
bool delsp
Delete trailing space.
Definition rfc3676.c:59
size_t width
Wrap width.
Definition rfc3676.c:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_protected_headers_handler()

int mutt_protected_headers_handler ( struct Body * b_email,
struct State * state )

Handler for protected headers - Implements handler_t -.

Definition at line 1122 of file crypt.c.

1123{
1124 if (!cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read"))
1125 return 0;
1126
1128
1129 if (!b_email->mime_headers)
1130 goto blank;
1131
1132 /* Load display preferences: weeding, wrapping, and security debug flags */
1133 const bool c_devel_security = cs_subset_bool(NeoMutt->sub, "devel_security");
1134 const bool display = (state->flags & STATE_DISPLAY);
1135 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1136 const bool c_crypt_protected_headers_weed = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_weed");
1137 const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
1138 const int wraplen = display ? mutt_window_wrap_cols(state->wraplen, c_wrap) : 0;
1139 const CopyHeaderFlags chflags = display ? CH_DISPLAY : CH_NO_FLAGS;
1140 struct Buffer *buf = buf_pool_get();
1141 bool weed = (display && c_weed && c_crypt_protected_headers_weed);
1142
1143 /* When devel_security is enabled, output all protected envelope headers
1144 * (Date, From, To, Cc, etc.) subject to weed rules */
1145 if (c_devel_security)
1146 {
1147 if (b_email->mime_headers->date && (!display || !c_weed || !mutt_matches_ignore("date")))
1148 {
1149 mutt_write_one_header(state->fp_out, "Date", b_email->mime_headers->date,
1150 state->prefix, wraplen, chflags, NeoMutt->sub);
1151 }
1152
1153 if (!weed || !mutt_matches_ignore("return-path"))
1154 {
1155 mutt_addrlist_write(&b_email->mime_headers->return_path, buf, display);
1156 mutt_write_one_header(state->fp_out, "Return-Path", buf_string(buf),
1157 state->prefix, wraplen, chflags, NeoMutt->sub);
1158 }
1159 if (!weed || !mutt_matches_ignore("from"))
1160 {
1161 buf_reset(buf);
1162 mutt_addrlist_write(&b_email->mime_headers->from, buf, display);
1163 mutt_write_one_header(state->fp_out, "From", buf_string(buf),
1164 state->prefix, wraplen, chflags, NeoMutt->sub);
1165 }
1166 if (!weed || !mutt_matches_ignore("to"))
1167 {
1168 buf_reset(buf);
1169 mutt_addrlist_write(&b_email->mime_headers->to, buf, display);
1170 mutt_write_one_header(state->fp_out, "To", buf_string(buf), state->prefix,
1171 wraplen, chflags, NeoMutt->sub);
1172 }
1173 if (!weed || !mutt_matches_ignore("cc"))
1174 {
1175 buf_reset(buf);
1176 mutt_addrlist_write(&b_email->mime_headers->cc, buf, display);
1177 mutt_write_one_header(state->fp_out, "Cc", buf_string(buf), state->prefix,
1178 wraplen, chflags, NeoMutt->sub);
1179 }
1180 if (!weed || !mutt_matches_ignore("sender"))
1181 {
1182 buf_reset(buf);
1183 mutt_addrlist_write(&b_email->mime_headers->sender, buf, display);
1184 mutt_write_one_header(state->fp_out, "Sender", buf_string(buf),
1185 state->prefix, wraplen, chflags, NeoMutt->sub);
1186 }
1187 if (!weed || !mutt_matches_ignore("reply-to"))
1188 {
1189 buf_reset(buf);
1190 mutt_addrlist_write(&b_email->mime_headers->reply_to, buf, display);
1191 mutt_write_one_header(state->fp_out, "Reply-To", buf_string(buf),
1192 state->prefix, wraplen, chflags, NeoMutt->sub);
1193 }
1194 if (!weed || !mutt_matches_ignore("mail-followup-to"))
1195 {
1196 buf_reset(buf);
1197 mutt_addrlist_write(&b_email->mime_headers->mail_followup_to, buf, display);
1198 mutt_write_one_header(state->fp_out, "Mail-Followup-To", buf_string(buf),
1199 state->prefix, wraplen, chflags, NeoMutt->sub);
1200 }
1201 if (!weed || !mutt_matches_ignore("x-original-to"))
1202 {
1203 buf_reset(buf);
1204 mutt_addrlist_write(&b_email->mime_headers->x_original_to, buf, display);
1205 mutt_write_one_header(state->fp_out, "X-Original-To", buf_string(buf),
1206 state->prefix, wraplen, chflags, NeoMutt->sub);
1207 }
1208 }
1209
1210 if (b_email->mime_headers->subject && (!weed || !mutt_matches_ignore("subject")))
1211 {
1212 mutt_write_one_header(state->fp_out, "Subject", b_email->mime_headers->subject,
1213 state->prefix, wraplen, chflags, NeoMutt->sub);
1214 }
1215
1216 if (c_devel_security)
1217 {
1218 if (b_email->mime_headers->message_id && (!weed || !mutt_matches_ignore("message-id")))
1219 {
1220 mutt_write_one_header(state->fp_out, "Message-ID", b_email->mime_headers->message_id,
1221 state->prefix, wraplen, chflags, NeoMutt->sub);
1222 }
1223 if (!weed || !mutt_matches_ignore("references"))
1224 {
1225 buf_reset(buf);
1226 mutt_list_write(&b_email->mime_headers->references, buf);
1227 mutt_write_one_header(state->fp_out, "References", buf_string(buf),
1228 state->prefix, wraplen, chflags, NeoMutt->sub);
1229 }
1230 if (!weed || !mutt_matches_ignore("in-reply-to"))
1231 {
1232 buf_reset(buf);
1233 mutt_list_write(&b_email->mime_headers->in_reply_to, buf);
1234 mutt_write_one_header(state->fp_out, "In-Reply-To", buf_string(buf),
1235 state->prefix, wraplen, chflags, NeoMutt->sub);
1236 }
1237 }
1238
1239 buf_pool_release(&buf);
1240
1241blank:
1242 state_puts(state, "\n");
1243 return 0;
1244}
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1215
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
#define CH_NO_FLAGS
No flags are set.
Definition copy_email.h:55
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:320
size_t mutt_list_write(const struct ListHead *h, struct Buffer *buf)
Write a list to a buffer.
Definition list.c:293
void state_mark_protected_header(struct State *state)
Write a unique marker around protected headers.
Definition state.c:88
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
int mutt_write_one_header(FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags, struct ConfigSubset *sub)
Write one header line to a file.
Definition header.c:424
struct AddressList return_path
Return path for the Email.
Definition envelope.h:58
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList sender
Email's sender.
Definition envelope.h:63
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
char * date
Sent date.
Definition envelope.h:75
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_signed_handler()

int mutt_signed_handler ( struct Body * b_email,
struct State * state )

Handler for "multipart/signed" - Implements handler_t -.

Definition at line 1249 of file crypt.c.

1250{
1251 if (!WithCrypto)
1252 return -1;
1253
1254 bool inconsistent = false;
1255 struct Body *top = b_email;
1256 struct Body **signatures = NULL;
1257 int sigcnt = 0;
1258 int rc = 0;
1259 struct Buffer *tempfile = NULL;
1260
1261 /* Identify the signature type from the multipart/signed protocol parameter */
1262 b_email = b_email->parts;
1263 SecurityFlags signed_type = mutt_is_multipart_signed(top);
1264 if (signed_type == SEC_NO_FLAGS)
1265 {
1266 /* A null protocol value is already checked for in mutt_body_handler() */
1267 state_printf(state, _("[-- Error: Unknown multipart/signed protocol %s --]\n\n"),
1268 mutt_param_get(&top->parameter, "protocol"));
1269 return mutt_body_handler(b_email, state);
1270 }
1271
1272 /* Validate that the second MIME part matches the expected signature type */
1273 if (!(b_email && b_email->next))
1274 {
1275 inconsistent = true;
1276 }
1277 else
1278 {
1279 switch (signed_type)
1280 {
1281 case SEC_SIGN:
1282 if ((b_email->next->type != TYPE_MULTIPART) ||
1283 !mutt_istr_equal(b_email->next->subtype, "mixed"))
1284 {
1285 inconsistent = true;
1286 }
1287 break;
1288 case PGP_SIGN:
1289 if ((b_email->next->type != TYPE_APPLICATION) ||
1290 !mutt_istr_equal(b_email->next->subtype, "pgp-signature"))
1291 {
1292 inconsistent = true;
1293 }
1294 break;
1295 case SMIME_SIGN:
1296 if ((b_email->next->type != TYPE_APPLICATION) ||
1297 (!mutt_istr_equal(b_email->next->subtype, "x-pkcs7-signature") &&
1298 !mutt_istr_equal(b_email->next->subtype, "pkcs7-signature")))
1299 {
1300 inconsistent = true;
1301 }
1302 break;
1303 default:
1304 inconsistent = true;
1305 }
1306 }
1307 if (inconsistent)
1308 {
1309 state_attach_puts(state, _("[-- Error: Missing or bad-format multipart/signed signature --]\n\n"));
1310 return mutt_body_handler(b_email, state);
1311 }
1312
1313 if (state->flags & STATE_DISPLAY)
1314 {
1315 /* Verify each signature part (PGP or S/MIME) against the signed body
1316 * written to a temporary file */
1317 crypt_fetch_signatures(&signatures, b_email->next, &sigcnt);
1318
1319 if (sigcnt != 0)
1320 {
1321 tempfile = buf_pool_get();
1322 buf_mktemp(tempfile);
1323 bool goodsig = true;
1324 if (crypt_write_signed(b_email, state, buf_string(tempfile)) == 0)
1325 {
1326 for (int i = 0; i < sigcnt; i++)
1327 {
1328 if (((WithCrypto & APPLICATION_PGP) != 0) &&
1329 (signatures[i]->type == TYPE_APPLICATION) &&
1330 mutt_istr_equal(signatures[i]->subtype, "pgp-signature"))
1331 {
1332 if (crypt_pgp_verify_one(signatures[i], state, buf_string(tempfile)) != 0)
1333 goodsig = false;
1334
1335 continue;
1336 }
1337
1338 if (((WithCrypto & APPLICATION_SMIME) != 0) &&
1339 (signatures[i]->type == TYPE_APPLICATION) &&
1340 (mutt_istr_equal(signatures[i]->subtype, "x-pkcs7-signature") ||
1341 mutt_istr_equal(signatures[i]->subtype, "pkcs7-signature")))
1342 {
1343 if (crypt_smime_verify_one(signatures[i], state, buf_string(tempfile)) != 0)
1344 goodsig = false;
1345
1346 continue;
1347 }
1348
1349 state_printf(state, _("[-- Warning: We can't verify %s/%s signatures --]\n\n"),
1350 BODY_TYPE(signatures[i]), signatures[i]->subtype);
1351 }
1352 }
1353
1354 mutt_file_unlink(buf_string(tempfile));
1355 buf_pool_release(&tempfile);
1356
1357 top->goodsig = goodsig;
1358 top->badsig = !goodsig;
1359
1360 /* Now display the signed body */
1361 state_attach_puts(state, _("[-- The following data is signed --]\n"));
1362
1363 mutt_protected_headers_handler(b_email, state);
1364
1365 FREE(&signatures);
1366 }
1367 else
1368 {
1369 state_attach_puts(state, _("[-- Warning: Can't find any signatures --]\n\n"));
1370 }
1371 }
1372
1373 rc = mutt_body_handler(b_email, state);
1374
1375 if ((state->flags & STATE_DISPLAY) && (sigcnt != 0))
1376 state_attach_puts(state, _("[-- End of signed data --]\n"));
1377
1378 return rc;
1379}
static void crypt_fetch_signatures(struct Body ***b_sigs, struct Body *b, int *n)
Create an array of an emails parts.
Definition crypt.c:1079
SecurityFlags mutt_is_multipart_signed(struct Body *b)
Is a message signed?
Definition crypt.c:408
int crypt_write_signed(struct Body *b, struct State *state, const char *tempfile)
Write the message body/part.
Definition crypt.c:764
int crypt_smime_verify_one(struct Body *b, struct State *state, const char *tempf)
Wrapper for CryptModuleSpecs::verify_one()
Definition cryptglue.c:529
int crypt_pgp_verify_one(struct Body *b, struct State *state, const char *tempf)
Wrapper for CryptModuleSpecs::verify_one()
Definition cryptglue.c:385
int mutt_protected_headers_handler(struct Body *b_email, struct State *state)
Handler for protected headers - Implements handler_t -.
Definition crypt.c:1122
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
#define PGP_SIGN
Email is PGP signed.
Definition lib.h:106
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition lib.h:85
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:99
#define SMIME_SIGN
Email is S/MIME signed.
Definition lib.h:112
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition lib.h:100
#define SEC_NO_FLAGS
No flags are set.
Definition lib.h:86
#define WithCrypto
Definition lib.h:125
#define SEC_SIGN
Email is signed.
Definition lib.h:88
bool badsig
Bad cryptographic signature (needed to check encrypted s/mime-signatures)
Definition body.h:43
#define buf_mktemp(buf)
Definition tmp.h:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ crypt_pgp_application_handler()

int crypt_pgp_application_handler ( struct Body * b_email,
struct State * state )

Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.

Definition at line 249 of file cryptglue.c.

250{
251 if (CRYPT_MOD_CALL_CHECK(PGP, application_handler))
252 return CRYPT_MOD_CALL(PGP, application_handler)(b_email, state);
253
254 return -1;
255}
#define CRYPT_MOD_CALL_CHECK(identifier, func)
Definition cryptglue.c:84
#define CRYPT_MOD_CALL(identifier, func)
Definition cryptglue.c:90
+ Here is the caller graph for this function:

◆ crypt_pgp_encrypted_handler()

int crypt_pgp_encrypted_handler ( struct Body * b_email,
struct State * state )

Wrapper for CryptModuleSpecs::encrypted_handler() - Implements handler_t -.

Definition at line 260 of file cryptglue.c.

261{
262#ifdef USE_AUTOCRYPT
263 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
264 if (c_autocrypt)
265 {
266 OptAutocryptGpgme = true;
267 int result = pgp_gpgme_encrypted_handler(b_email, state);
268 OptAutocryptGpgme = false;
269 if (result == 0)
270 {
271 b_email->is_autocrypt = true;
272 return result;
273 }
274 }
275#endif
276
277 if (CRYPT_MOD_CALL_CHECK(PGP, encrypted_handler))
278 return CRYPT_MOD_CALL(PGP, encrypted_handler)(b_email, state);
279
280 return -1;
281}
bool OptAutocryptGpgme
(pseudo) use Autocrypt context inside ncrypt/crypt_gpgme.c
Definition globals.c:44
int pgp_gpgme_encrypted_handler(struct Body *b, struct State *state)
Manage a PGP or S/MIME encrypted MIME part - Implements CryptModuleSpecs::encrypted_handler() -.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ crypt_smime_application_handler()

int crypt_smime_application_handler ( struct Body * b_email,
struct State * state )

Wrapper for CryptModuleSpecs::application_handler() - Implements handler_t -.

Definition at line 456 of file cryptglue.c.

457{
458 if (CRYPT_MOD_CALL_CHECK(SMIME, application_handler))
459 return CRYPT_MOD_CALL(SMIME, application_handler)(b_email, state);
460
461 return -1;
462}
+ Here is the caller graph for this function: