NeoMutt  2025-12-11-1039-g550ac6
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 474 of file enriched.c.

475{
476 enum
477 {
478 TEXT,
479 LANGLE,
480 TAG,
481 BOGUS_TAG,
482 NEWLINE,
483 ST_EOF,
484 DONE
485 } text_state = TEXT;
486
487 LOFF_T bytes = b_email->length;
488 struct EnrichedState enriched = { 0 };
489 wint_t wc = 0;
490 int tag_len = 0;
491 wchar_t tag[1024 + 1] = { 0 };
492
493 enriched.state = state;
494 enriched.wrap_margin = ((state->wraplen > 4) &&
495 ((state->flags & STATE_DISPLAY) || (state->wraplen < 76))) ?
496 state->wraplen - 4 :
497 72;
498 enriched.line_max = enriched.wrap_margin * 4;
499 enriched.line = MUTT_MEM_CALLOC(enriched.line_max + 1, wchar_t);
500 enriched.param = MUTT_MEM_CALLOC(256, wchar_t);
501
502 enriched.param_len = 256;
503 enriched.param_used = 0;
504
505 if (state->prefix)
506 {
508 enriched.indent_len += mutt_str_len(state->prefix);
509 }
510
511 while (text_state != DONE)
512 {
513 if (text_state != ST_EOF)
514 {
515 if (!bytes || ((wc = fgetwc(state->fp_in)) == WEOF))
516 text_state = ST_EOF;
517 else
518 bytes--;
519 }
520
521 switch (text_state)
522 {
523 case TEXT:
524 switch (wc)
525 {
526 case '<':
527 text_state = LANGLE;
528 break;
529
530 case '\n':
531 if (enriched.tag_level[RICH_NOFILL])
532 {
533 enriched_flush(&enriched, true);
534 }
535 else
536 {
537 enriched_putwc((wchar_t) ' ', &enriched);
538 text_state = NEWLINE;
539 }
540 break;
541
542 default:
543 enriched_putwc(wc, &enriched);
544 }
545 break;
546
547 case LANGLE:
548 if (wc == (wchar_t) '<')
549 {
550 enriched_putwc(wc, &enriched);
551 text_state = TEXT;
552 break;
553 }
554 else
555 {
556 tag_len = 0;
557 text_state = TAG;
558 }
559 /* Yes, (it wasn't a <<, so this char is first in TAG) */
561
562 case TAG:
563 if (wc == (wchar_t) '>')
564 {
565 tag[tag_len] = (wchar_t) '\0';
566 enriched_set_flags(tag, &enriched);
567 text_state = TEXT;
568 }
569 else if (tag_len < 1024) /* ignore overly long tags */
570 {
571 tag[tag_len++] = wc;
572 }
573 else
574 {
575 text_state = BOGUS_TAG;
576 }
577 break;
578
579 case BOGUS_TAG:
580 if (wc == (wchar_t) '>')
581 text_state = TEXT;
582 break;
583
584 case NEWLINE:
585 if (wc == (wchar_t) '\n')
586 {
587 enriched_flush(&enriched, true);
588 }
589 else
590 {
591 ungetwc(wc, state->fp_in);
592 bytes++;
593 text_state = TEXT;
594 }
595 break;
596
597 case ST_EOF:
598 enriched_putwc((wchar_t) '\0', &enriched);
599 enriched_flush(&enriched, true);
600 text_state = DONE;
601 break;
602
603 default:
604 /* not reached */
605 break;
606 }
607 }
608
609 state_putc(state, '\n'); /* add a final newline */
610
611 FREE(&(enriched.buffer));
612 FREE(&(enriched.line));
613 FREE(&(enriched.param));
614
615 return 0;
616}
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:64
#define state_putc(STATE, STR)
Definition state.h:65
@ STATE_DISPLAY
Output is displayed to the user.
Definition state.h:37
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:59
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition state.h:58
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
Here is the call graph for this function:
Here is the caller graph for this function:

◆ autoview_handler()

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

Handler for autoviewable attachments - Implements handler_t -.

Definition at line 545 of file handler.c.

546{
547 struct MailcapEntry *entry = mailcap_entry_new();
548 char buf[1024] = { 0 };
549 char type[256] = { 0 };
550 struct Buffer *cmd = buf_pool_get();
551 struct Buffer *tempfile = buf_pool_get();
552 char *fname = NULL;
553 FILE *fp_in = NULL;
554 FILE *fp_out = NULL;
555 FILE *fp_err = NULL;
556 pid_t pid;
557 int rc = 0;
558
559 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b_email), b_email->subtype);
560 mailcap_lookup(b_email, type, sizeof(type), entry, MUTT_MC_AUTOVIEW);
561
562 fname = mutt_str_dup(b_email->filename);
563 mutt_file_sanitize_filename(fname, true);
564 mailcap_expand_filename(entry->nametemplate, fname, tempfile);
565 FREE(&fname);
566
567 if (entry->command)
568 {
569 buf_strcpy(cmd, entry->command);
570
571 /* mailcap_expand_command returns 0 if the file is required */
572 bool piped = mailcap_expand_command(b_email, buf_string(tempfile), type, cmd);
573
574 if (state->flags & STATE_DISPLAY)
575 {
576 state_mark_attach(state);
577 state_printf(state, _("[-- Autoview using %s --]\n"), buf_string(cmd));
578 mutt_message(_("Invoking autoview command: %s"), buf_string(cmd));
579 }
580
581 fp_in = mutt_file_fopen(buf_string(tempfile), "w+");
582 if (!fp_in)
583 {
584 mutt_perror("fopen");
585 mailcap_entry_free(&entry);
586 rc = -1;
587 goto cleanup;
588 }
589
590 mutt_file_copy_bytes(state->fp_in, fp_in, b_email->length);
591
592 if (piped)
593 {
594 unlink(buf_string(tempfile));
595 fflush(fp_in);
596 fseek(fp_in, 0, SEEK_SET);
597 clearerr(fp_in);
598 pid = filter_create_fd(buf_string(cmd), NULL, &fp_out, &fp_err,
599 fileno(fp_in), -1, -1, NeoMutt->env);
600 }
601 else
602 {
603 mutt_file_fclose(&fp_in);
604 pid = filter_create(buf_string(cmd), NULL, &fp_out, &fp_err, NeoMutt->env);
605 }
606
607 if (pid < 0)
608 {
609 mutt_perror(_("Can't create filter"));
610 if (state->flags & STATE_DISPLAY)
611 {
612 state_mark_attach(state);
613 state_printf(state, _("[-- Can't run %s --]\n"), buf_string(cmd));
614 }
615 rc = -1;
616 goto bail;
617 }
618
619 if (state->prefix)
620 {
621 /* Remove ansi and formatting from autoview output in replies only. The
622 * user may want to see the formatting in the pager, but it shouldn't be
623 * in their quoted reply text too. */
624 struct Buffer *stripped = buf_pool_get();
625 while (fgets(buf, sizeof(buf), fp_out))
626 {
627 buf_strip_formatting(stripped, buf, false);
628 state_puts(state, state->prefix);
629 state_puts(state, buf_string(stripped));
630 }
631 buf_pool_release(&stripped);
632
633 /* check for data on stderr */
634 if (fgets(buf, sizeof(buf), fp_err))
635 {
636 if (state->flags & STATE_DISPLAY)
637 {
638 state_mark_attach(state);
639 state_printf(state, _("[-- Autoview stderr of %s --]\n"), buf_string(cmd));
640 }
641
642 state_puts(state, state->prefix);
643 state_puts(state, buf);
644 while (fgets(buf, sizeof(buf), fp_err))
645 {
646 state_puts(state, state->prefix);
647 state_puts(state, buf);
648 }
649 }
650 }
651 else
652 {
653 mutt_file_copy_stream(fp_out, state->fp_out);
654 /* Check for stderr messages */
655 if (fgets(buf, sizeof(buf), fp_err))
656 {
657 if (state->flags & STATE_DISPLAY)
658 {
659 state_mark_attach(state);
660 state_printf(state, _("[-- Autoview stderr of %s --]\n"), buf_string(cmd));
661 }
662
663 state_puts(state, buf);
664 mutt_file_copy_stream(fp_err, state->fp_out);
665 }
666 }
667
668 bail:
669 mutt_file_fclose(&fp_out);
670 mutt_file_fclose(&fp_err);
671
672 filter_wait(pid);
673 if (piped)
674 mutt_file_fclose(&fp_in);
675 else
676 mutt_file_unlink(buf_string(tempfile));
677
678 if (state->flags & STATE_DISPLAY)
680 }
681
682cleanup:
683 mailcap_entry_free(&entry);
684
685 buf_pool_release(&cmd);
686 buf_pool_release(&tempfile);
687
688 return rc;
689}
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
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition display.c:737
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:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#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: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
@ 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:92
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
#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:56
Here is the call graph for this function:
Here is the caller graph for this function:

◆ text_plain_handler()

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 699 of file handler.c.

700{
701 char *buf = NULL;
702 size_t sz = 0;
703
704 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
705 while ((buf = mutt_file_read_line(buf, &sz, state->fp_in, NULL, MUTT_RL_NONE)))
706 {
707 if (!mutt_str_equal(buf, "-- ") && c_text_flowed)
708 {
709 size_t len = mutt_str_len(buf);
710 while ((len > 0) && (buf[len - 1] == ' '))
711 buf[--len] = '\0';
712 }
713 if (state->prefix)
714 state_puts(state, state->prefix);
715 state_puts(state, buf);
716 state_putc(state, '\n');
717 }
718
719 FREE(&buf);
720 return 0;
721}
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
@ MUTT_RL_NONE
No flags are set.
Definition file.h:43
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
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()

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

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

Definition at line 726 of file handler.c.

727{
728 struct Body *b = NULL;
729 LOFF_T off_start;
730 int rc = 0;
731
732 off_start = ftello(state->fp_in);
733 if (off_start < 0)
734 return -1;
735
736 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
737 (b_email->encoding == ENC_UUENCODED))
738 {
739 b = mutt_body_new();
741 b->parts = mutt_rfc822_parse_message(state->fp_in, b);
742 }
743 else
744 {
745 b = b_email;
746 }
747
748 if (b->parts)
749 {
751 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
752 if ((state->flags & STATE_WEED) ||
753 ((state->flags & (STATE_DISPLAY | STATE_PRINTING)) && c_weed))
754 {
755 chflags |= CH_WEED | CH_REORDER;
756 }
757 if (state->prefix)
758 chflags |= CH_PREFIX;
759 if (state->flags & STATE_DISPLAY)
760 chflags |= CH_DISPLAY;
761
762 mutt_copy_hdr(state->fp_in, state->fp_out, off_start, b->parts->offset,
763 chflags, state->prefix, 0);
764
765 if (state->prefix)
766 state_puts(state, state->prefix);
767 state_putc(state, '\n');
768
769 rc = mutt_body_handler(b->parts, state);
770 }
771
772 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
773 (b_email->encoding == ENC_UUENCODED))
774 {
775 mutt_body_free(&b);
776 }
777
778 return rc;
779}
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
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
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:1997
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1433
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1675
@ 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
@ STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition state.h:42
@ STATE_WEED
Weed headers even when not in display mode.
Definition state.h:40
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()

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

Handler for external-body emails - Implements handler_t -.

Definition at line 784 of file handler.c.

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

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

Handler for multipart alternative emails - Implements handler_t -.

Definition at line 955 of file handler.c.

956{
957 struct Body *const head = b_email;
958 struct Body *choice = NULL;
959 struct Body *b = NULL;
960 bool mustfree = false;
961 int rc = 0;
962
963 if ((b_email->encoding == ENC_BASE64) || (b_email->encoding == ENC_QUOTED_PRINTABLE) ||
964 (b_email->encoding == ENC_UUENCODED))
965 {
966 mustfree = true;
967 b = mutt_body_new();
969 b->parts = mutt_parse_multipart(state->fp_in,
970 mutt_param_get(&b_email->parameter, "boundary"),
971 b->length,
972 mutt_istr_equal("digest", b_email->subtype));
973 }
974 else
975 {
976 b = b_email;
977 }
978
979 b_email = b;
980
982 ASSERT(mod_data);
983
984 /* First, search list of preferred types */
985 struct ListNode *np = NULL;
986 STAILQ_FOREACH(np, &mod_data->alternative_order, entries)
987 {
988 int btlen; /* length of basetype */
989 bool wild; /* do we have a wildcard to match all subtypes? */
990
991 char *c = strchr(np->data, '/');
992 if (c)
993 {
994 wild = ((c[1] == '*') && (c[2] == '\0'));
995 btlen = c - np->data;
996 }
997 else
998 {
999 wild = true;
1000 btlen = mutt_str_len(np->data);
1001 }
1002
1003 if (b_email->parts)
1004 b = b_email->parts;
1005 else
1006 b = b_email;
1007 while (b)
1008 {
1009 const char *bt = BODY_TYPE(b);
1010 if (mutt_istrn_equal(bt, np->data, btlen) && (bt[btlen] == 0))
1011 {
1012 /* the basetype matches */
1013 if (wild || mutt_istr_equal(np->data + btlen + 1, b->subtype))
1014 {
1015 choice = b;
1016 }
1017 }
1018 b = b->next;
1019 }
1020
1021 if (choice)
1022 break;
1023 }
1024
1025 /* Next, look for an autoviewable type */
1026 if (!choice)
1027 {
1028 if (b_email->parts)
1029 b = b_email->parts;
1030 else
1031 b = b_email;
1032 while (b)
1033 {
1034 if (is_autoview(b))
1035 choice = b;
1036 b = b->next;
1037 }
1038 }
1039
1040 /* Then, look for a text entry */
1041 if (!choice)
1042 {
1043 if (b_email->parts)
1044 b = b_email->parts;
1045 else
1046 b = b_email;
1047 int type = 0;
1048 while (b)
1049 {
1050 if (b->type == TYPE_TEXT)
1051 {
1052 if (mutt_istr_equal("plain", b->subtype) && (type <= TXT_PLAIN))
1053 {
1054 choice = b;
1055 type = TXT_PLAIN;
1056 }
1057 else if (mutt_istr_equal("enriched", b->subtype) && (type <= TXT_ENRICHED))
1058 {
1059 choice = b;
1060 type = TXT_ENRICHED;
1061 }
1062 else if (mutt_istr_equal("html", b->subtype) && (type <= TXT_HTML))
1063 {
1064 choice = b;
1065 type = TXT_HTML;
1066 }
1067 }
1068 b = b->next;
1069 }
1070 }
1071
1072 /* Finally, look for other possibilities */
1073 if (!choice)
1074 {
1075 if (b_email->parts)
1076 b = b_email->parts;
1077 else
1078 b = b_email;
1079 while (b)
1080 {
1081 if (mutt_can_decode(b))
1082 choice = b;
1083 b = b->next;
1084 }
1085 }
1086
1087 if (choice)
1088 {
1089 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1090 if (state->flags & STATE_DISPLAY && !c_weed &&
1091 mutt_file_seek(state->fp_in, choice->hdr_offset, SEEK_SET))
1092 {
1093 mutt_file_copy_bytes(state->fp_in, state->fp_out, choice->offset - choice->hdr_offset);
1094 }
1095
1096 const char *const c_show_multipart_alternative = cs_subset_string(NeoMutt->sub, "show_multipart_alternative");
1097 if (mutt_str_equal("info", c_show_multipart_alternative))
1098 {
1099 print_part_line(state, choice, 0);
1100 }
1101 mutt_body_handler(choice, state);
1102
1103 /* Let it flow back to the main part */
1104 head->nowrap = choice->nowrap;
1105 choice->nowrap = false;
1106
1107 if (mutt_str_equal("info", c_show_multipart_alternative))
1108 {
1109 if (b_email->parts)
1110 b = b_email->parts;
1111 else
1112 b = b_email;
1113 int count = 0;
1114 while (b)
1115 {
1116 if (choice != b)
1117 {
1118 count += 1;
1119 if (count == 1)
1120 state_putc(state, '\n');
1121
1122 print_part_line(state, b, count);
1123 }
1124 b = b->next;
1125 }
1126 }
1127 }
1128 else if (state->flags & STATE_DISPLAY)
1129 {
1130 /* didn't find anything that we could display! */
1131 state_mark_attach(state);
1132 state_puts(state, _("[-- Error: Could not display any parts of Multipart/Alternative --]\n"));
1133 rc = -1;
1134 }
1135
1136 if (mustfree)
1137 mutt_body_free(&b_email);
1138
1139 return rc;
1140}
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:2013
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:495
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition handler.c:1910
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:666
#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()

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 1146 of file handler.c.

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

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

Handler for multipart emails - Implements handler_t -.

Definition at line 1259 of file handler.c.

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

◆ valid_pgp_encrypted_handler()

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 1507 of file handler.c.

1508{
1509 struct Body *octetstream = b_email->parts->next;
1510
1511 /* clear out any mime headers before the handler, so they can't be spoofed. */
1512 mutt_env_free(&b_email->mime_headers);
1513 mutt_env_free(&octetstream->mime_headers);
1514
1515 int rc;
1516 /* Some clients improperly encode the octetstream part. */
1517 if (octetstream->encoding != ENC_7BIT)
1518 rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, 0);
1519 else
1520 rc = crypt_pgp_encrypted_handler(octetstream, state);
1521 b_email->goodsig |= octetstream->goodsig;
1522
1523 /* Relocate protected headers onto the multipart/encrypted part */
1524 if (!rc && octetstream->mime_headers)
1525 {
1526 b_email->mime_headers = octetstream->mime_headers;
1527 octetstream->mime_headers = NULL;
1528 }
1529
1530 return rc;
1531}
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:280
static int run_decode_and_handler(struct Body *b, struct State *state, handler_t handler, bool plaintext)
Run an appropriate decoder for an email.
Definition handler.c:1349
@ 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()

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 1536 of file handler.c.

1537{
1538 if (!b_email->parts || !b_email->parts->next || !b_email->parts->next->next)
1539 return -1;
1540
1541 struct Body *octetstream = b_email->parts->next->next;
1542
1543 /* clear out any mime headers before the handler, so they can't be spoofed. */
1544 mutt_env_free(&b_email->mime_headers);
1545 mutt_env_free(&octetstream->mime_headers);
1546
1547 /* exchange encodes the octet-stream, so re-run it through the decoder */
1548 int rc = run_decode_and_handler(octetstream, state, crypt_pgp_encrypted_handler, false);
1549 b_email->goodsig |= octetstream->goodsig;
1550#ifdef USE_AUTOCRYPT
1551 b_email->is_autocrypt |= octetstream->is_autocrypt;
1552#endif
1553
1554 /* Relocate protected headers onto the multipart/encrypted part */
1555 if (!rc && octetstream->mime_headers)
1556 {
1557 b_email->mime_headers = octetstream->mime_headers;
1558 octetstream->mime_headers = NULL;
1559 }
1560
1561 return rc;
1562}
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 329 of file rfc3676.c.

330{
331 char *buf = NULL;
332 unsigned int quotelevel = 0;
333 bool delsp = false;
334 size_t sz = 0;
335 struct FlowedState fst = { 0 };
336
337 /* respect DelSp of RFC3676 only with f=f parts */
338 char *t = mutt_param_get(&b_email->parameter, "delsp");
339 if (t)
340 {
341 delsp = mutt_istr_equal(t, "yes");
342 t = NULL;
343 fst.delsp = true;
344 }
345
346 mutt_debug(LL_DEBUG3, "f=f: DelSp: %s\n", delsp ? "yes" : "no");
347
348 while ((buf = mutt_file_read_line(buf, &sz, state->fp_in, NULL, MUTT_RL_NONE)))
349 {
350 const size_t buflen = mutt_str_len(buf);
351 const unsigned int newql = get_quote_level(buf);
352
353 /* end flowed paragraph (if we're within one) if quoting level
354 * changes (should not but can happen, see RFC3676, sec. 4.5.) */
355 if (newql != quotelevel)
356 flush_par(state, &fst);
357
358 quotelevel = newql;
359 int buf_off = newql;
360
361 /* respect sender's space-stuffing by removing one leading space */
362 if (buf[buf_off] == ' ')
363 buf_off++;
364
365 /* test for signature separator */
366 const unsigned int sigsep = mutt_str_equal(buf + buf_off, "-- ");
367
368 /* a fixed line either has no trailing space or is the
369 * signature separator */
370 const bool fixed = (buflen == buf_off) || (buf[buflen - 1] != ' ') || sigsep;
371
372 /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
373 * fixed lines */
374 if ((fixed && ((fst.width == 0) || (buflen == 0))) || sigsep)
375 {
376 /* if we're within a flowed paragraph, terminate it */
377 flush_par(state, &fst);
378 print_fixed_line(buf + buf_off, state, quotelevel, &fst);
379 continue;
380 }
381
382 /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
383 if (delsp && !fixed)
384 buf[buflen - 1] = '\0';
385
386 print_flowed_line(buf + buf_off, state, quotelevel, &fst, fixed);
387 }
388
389 flush_par(state, &fst);
390
391 FREE(&buf);
392 return 0;
393}
@ 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:313
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 1124 of file crypt.c.

1125{
1126 if (!cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read"))
1127 return 0;
1128
1130
1131 if (!b_email->mime_headers)
1132 goto blank;
1133
1134 /* Load display preferences: weeding, wrapping, and security debug flags */
1135 const bool c_devel_security = cs_subset_bool(NeoMutt->sub, "devel_security");
1136 const bool display = (state->flags & STATE_DISPLAY);
1137 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1138 const bool c_crypt_protected_headers_weed = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_weed");
1139 const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
1140 const int wraplen = display ? mutt_window_wrap_cols(state->wraplen, c_wrap) : 0;
1141 const CopyHeaderFlags chflags = display ? CH_DISPLAY : CH_NONE;
1142 struct Buffer *buf = buf_pool_get();
1143 bool weed = (display && c_weed && c_crypt_protected_headers_weed);
1144
1145 /* When devel_security is enabled, output all protected envelope headers
1146 * (Date, From, To, Cc, etc.) subject to weed rules */
1147 if (c_devel_security)
1148 {
1149 if (b_email->mime_headers->date && (!display || !c_weed || !mutt_matches_ignore("date")))
1150 {
1151 mutt_write_one_header(state->fp_out, "Date", b_email->mime_headers->date,
1152 state->prefix, wraplen, chflags, NeoMutt->sub);
1153 }
1154
1155 if (!weed || !mutt_matches_ignore("return-path"))
1156 {
1157 mutt_addrlist_write(&b_email->mime_headers->return_path, buf, display);
1158 mutt_write_one_header(state->fp_out, "Return-Path", buf_string(buf),
1159 state->prefix, wraplen, chflags, NeoMutt->sub);
1160 }
1161 if (!weed || !mutt_matches_ignore("from"))
1162 {
1163 buf_reset(buf);
1164 mutt_addrlist_write(&b_email->mime_headers->from, buf, display);
1165 mutt_write_one_header(state->fp_out, "From", buf_string(buf),
1166 state->prefix, wraplen, chflags, NeoMutt->sub);
1167 }
1168 if (!weed || !mutt_matches_ignore("to"))
1169 {
1170 buf_reset(buf);
1171 mutt_addrlist_write(&b_email->mime_headers->to, buf, display);
1172 mutt_write_one_header(state->fp_out, "To", buf_string(buf), state->prefix,
1173 wraplen, chflags, NeoMutt->sub);
1174 }
1175 if (!weed || !mutt_matches_ignore("cc"))
1176 {
1177 buf_reset(buf);
1178 mutt_addrlist_write(&b_email->mime_headers->cc, buf, display);
1179 mutt_write_one_header(state->fp_out, "Cc", buf_string(buf), state->prefix,
1180 wraplen, chflags, NeoMutt->sub);
1181 }
1182 if (!weed || !mutt_matches_ignore("sender"))
1183 {
1184 buf_reset(buf);
1185 mutt_addrlist_write(&b_email->mime_headers->sender, buf, display);
1186 mutt_write_one_header(state->fp_out, "Sender", buf_string(buf),
1187 state->prefix, wraplen, chflags, NeoMutt->sub);
1188 }
1189 if (!weed || !mutt_matches_ignore("reply-to"))
1190 {
1191 buf_reset(buf);
1192 mutt_addrlist_write(&b_email->mime_headers->reply_to, buf, display);
1193 mutt_write_one_header(state->fp_out, "Reply-To", buf_string(buf),
1194 state->prefix, wraplen, chflags, NeoMutt->sub);
1195 }
1196 if (!weed || !mutt_matches_ignore("mail-followup-to"))
1197 {
1198 buf_reset(buf);
1199 mutt_addrlist_write(&b_email->mime_headers->mail_followup_to, buf, display);
1200 mutt_write_one_header(state->fp_out, "Mail-Followup-To", buf_string(buf),
1201 state->prefix, wraplen, chflags, NeoMutt->sub);
1202 }
1203 if (!weed || !mutt_matches_ignore("x-original-to"))
1204 {
1205 buf_reset(buf);
1206 mutt_addrlist_write(&b_email->mime_headers->x_original_to, buf, display);
1207 mutt_write_one_header(state->fp_out, "X-Original-To", buf_string(buf),
1208 state->prefix, wraplen, chflags, NeoMutt->sub);
1209 }
1210 }
1211
1212 if (b_email->mime_headers->subject && (!weed || !mutt_matches_ignore("subject")))
1213 {
1214 mutt_write_one_header(state->fp_out, "Subject", b_email->mime_headers->subject,
1215 state->prefix, wraplen, chflags, NeoMutt->sub);
1216 }
1217
1218 if (c_devel_security)
1219 {
1220 if (b_email->mime_headers->message_id && (!weed || !mutt_matches_ignore("message-id")))
1221 {
1222 mutt_write_one_header(state->fp_out, "Message-ID", b_email->mime_headers->message_id,
1223 state->prefix, wraplen, chflags, NeoMutt->sub);
1224 }
1225 if (!weed || !mutt_matches_ignore("references"))
1226 {
1227 buf_reset(buf);
1228 mutt_list_write(&b_email->mime_headers->references, buf);
1229 mutt_write_one_header(state->fp_out, "References", buf_string(buf),
1230 state->prefix, wraplen, chflags, NeoMutt->sub);
1231 }
1232 if (!weed || !mutt_matches_ignore("in-reply-to"))
1233 {
1234 buf_reset(buf);
1235 mutt_list_write(&b_email->mime_headers->in_reply_to, buf);
1236 mutt_write_one_header(state->fp_out, "In-Reply-To", buf_string(buf),
1237 state->prefix, wraplen, chflags, NeoMutt->sub);
1238 }
1239 }
1240
1241 buf_pool_release(&buf);
1242
1243blank:
1244 state_puts(state, "\n");
1245 return 0;
1246}
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1217
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
@ CH_NONE
No flags are set.
Definition copy_email.h:65
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:367
size_t mutt_list_write(const struct ListHead *h, struct Buffer *buf)
Write a list to a buffer.
Definition list.c:296
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:427
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 1251 of file crypt.c.

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

267{
269 ASSERT(mod_data);
270
271 if (CRYPT_MOD_CALL_CHECK(mod_data, PGP, application_handler))
272 return CRYPT_MOD_CALL(mod_data, PGP, application_handler)(b_email, state);
273
274 return -1;
275}
#define CRYPT_MOD_CALL(mod_data, identifier, func)
Definition cryptglue.c:91
#define CRYPT_MOD_CALL_CHECK(mod_data, identifier, func)
Definition cryptglue.c:85
@ MODULE_ID_NCRYPT
ModuleNcrypt, Ncrypt
Definition module_api.h:82
Ncrypt private Module data.
Definition module_data.h:39
Here is the call graph for this function:
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 280 of file cryptglue.c.

281{
282#ifdef USE_AUTOCRYPT
283 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
284 if (c_autocrypt)
285 {
286 OptAutocryptGpgme = true;
287 int result = pgp_gpgme_encrypted_handler(b_email, state);
288 OptAutocryptGpgme = false;
289 if (result == 0)
290 {
291 b_email->is_autocrypt = true;
292 return result;
293 }
294 }
295#endif
296
298 ASSERT(mod_data);
299
300 if (CRYPT_MOD_CALL_CHECK(mod_data, PGP, encrypted_handler))
301 return CRYPT_MOD_CALL(mod_data, PGP, encrypted_handler)(b_email, state);
302
303 return -1;
304}
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 524 of file cryptglue.c.

525{
527 ASSERT(mod_data);
528
529 if (CRYPT_MOD_CALL_CHECK(mod_data, SMIME, application_handler))
530 return CRYPT_MOD_CALL(mod_data, SMIME, application_handler)(b_email, state);
531
532 return -1;
533}
Here is the call graph for this function:
Here is the caller graph for this function: