NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.h File Reference

Miscellaneous email parsing routines. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include "mutt/lib.h"
#include "mime.h"
Include dependency graph for parse.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  Rfc2369ListHeaders
 Mailing-list actions from RFC 2369 headers. More...

Functions

int mutt_check_encoding (const char *c)
 Check the encoding type.
enum ContentType mutt_check_mime_type (const char *s)
 Check a MIME type string.
char * mutt_extract_message_id (const char *s, size_t *len)
 Find a Message-ID.
bool mutt_is_message_type (int type, const char *subtype)
 Determine if a mime type matches a message or not.
bool mutt_matches_ignore (const char *s)
 Does the string match the ignore list.
void mutt_parse_content_type (const char *s, struct Body *b)
 Parse a content type.
bool mutt_parse_mailto (struct Envelope *env, char **body, const char *src)
 Parse a mailto:// url.
size_t mutt_rfc2369_parse (const char *body, struct ListHead *links)
 Extract URLs from an RFC 2369 list header.
char * mutt_rfc2369_first_mailto (const char *body)
 Extract the first mailto: URL from an RFC 2369 list.
void mutt_rfc2369_list_headers_free (struct Rfc2369ListHeaders *headers)
 Free RFC 2369 mailing-list header values.
void mutt_rfc2369_read_headers (FILE *fp, struct Rfc2369ListHeaders *headers)
 Read RFC 2369 mailing-list headers.
struct Bodymutt_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
 Parse a multipart structure.
void mutt_parse_part (FILE *fp, struct Body *b)
 Parse a MIME part.
struct Bodymutt_read_mime_header (FILE *fp, bool digest)
 Parse a MIME header.
int mutt_rfc822_parse_line (struct Envelope *env, struct Email *e, const char *name, size_t name_len, const char *body, bool user_hdrs, bool weed, bool do_2047)
 Parse an email header.
struct Bodymutt_rfc822_parse_message (FILE *fp, struct Body *b)
 Parse a Message/RFC822 body.
struct Envelopemutt_rfc822_read_header (FILE *fp, struct Email *e, bool user_hdrs, bool weed)
 Parses an RFC822 header.
size_t mutt_rfc822_read_line (FILE *fp, struct Buffer *out)
 Read a header line from a file.
void mutt_filter_commandline_header_tag (char *header)
 Sanitise characters in a header tag.
void mutt_filter_commandline_header_value (char *header)
 Sanitise characters in a header value.

Detailed Description

Miscellaneous email parsing routines.

Authors
  • Richard Russon
  • Pietro Cerutti

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file parse.h.

Function Documentation

◆ mutt_check_encoding()

int mutt_check_encoding ( const char * c)

Check the encoding type.

Parameters
cString to check
Return values
enumContentEncoding, e.g. ENC_QUOTED_PRINTABLE

Definition at line 436 of file parse.c.

437{
438 if (mutt_istr_startswith(c, "7bit"))
439 return ENC_7BIT;
440 if (mutt_istr_startswith(c, "8bit"))
441 return ENC_8BIT;
442 if (mutt_istr_startswith(c, "binary"))
443 return ENC_BINARY;
444 if (mutt_istr_startswith(c, "quoted-printable"))
446 if (mutt_istr_startswith(c, "base64"))
447 return ENC_BASE64;
448 if (mutt_istr_startswith(c, "x-uuencode"))
449 return ENC_UUENCODED;
450 if (mutt_istr_startswith(c, "uuencode"))
451 return ENC_UUENCODED;
452 return ENC_OTHER;
453}
@ ENC_7BIT
7-bit text
Definition mime.h:49
@ ENC_UUENCODED
UUEncoded text.
Definition mime.h:54
@ ENC_OTHER
Encoding unknown.
Definition mime.h:48
@ ENC_BINARY
Binary.
Definition mime.h:53
@ ENC_BASE64
Base-64 encoded text.
Definition mime.h:52
@ ENC_8BIT
8-bit text
Definition mime.h:50
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition mime.h:51
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_check_mime_type()

enum ContentType mutt_check_mime_type ( const char * s)

Check a MIME type string.

Parameters
sString to check
Return values
enumContentType, e.g. TYPE_TEXT

Definition at line 380 of file parse.c.

381{
382 if (mutt_istr_equal("text", s))
383 return TYPE_TEXT;
384 if (mutt_istr_equal("multipart", s))
385 return TYPE_MULTIPART;
386 if (mutt_istr_equal("x-sun-attachment", s))
387 return TYPE_MULTIPART;
388 if (mutt_istr_equal("application", s))
389 return TYPE_APPLICATION;
390 if (mutt_istr_equal("message", s))
391 return TYPE_MESSAGE;
392 if (mutt_istr_equal("image", s))
393 return TYPE_IMAGE;
394 if (mutt_istr_equal("audio", s))
395 return TYPE_AUDIO;
396 if (mutt_istr_equal("video", s))
397 return TYPE_VIDEO;
398 if (mutt_istr_equal("model", s))
399 return TYPE_MODEL;
400 if (mutt_istr_equal("*", s))
401 return TYPE_ANY;
402 if (mutt_istr_equal(".*", s))
403 return TYPE_ANY;
404
405 return TYPE_OTHER;
406}
@ TYPE_AUDIO
Type: 'audio/*'.
Definition mime.h:32
@ TYPE_IMAGE
Type: 'image/*'.
Definition mime.h:34
@ TYPE_OTHER
Unknown Content-Type.
Definition mime.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition mime.h:35
@ TYPE_MODEL
Type: 'model/*'.
Definition mime.h:36
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
@ TYPE_ANY
Type: '' or '.'.
Definition mime.h:40
@ TYPE_VIDEO
Type: 'video/*'.
Definition mime.h:39
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_extract_message_id()

char * mutt_extract_message_id ( const char * s,
size_t * len )

Find a Message-ID.

Parameters
[in]sString to parse
[out]lenNumber of bytes of s parsed (optional)
Return values
ptrMessage id found
NULLNo more message ids
Note
Caller must free returned string

Definition at line 417 of file parse.c.

418{
419 if (!s || (*s == '\0'))
420 return NULL;
421
422 char *decoded = mutt_str_dup(s);
423 rfc2047_decode(&decoded);
424
425 char *res = extract_message_id(decoded, len);
426
427 FREE(&decoded);
428 return res;
429}
static char * extract_message_id(const char *s, size_t *len)
Find a message-id in a string.
Definition parse.c:295
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:679
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_is_message_type()

bool mutt_is_message_type ( int type,
const char * subtype )

Determine if a mime type matches a message or not.

Parameters
typeMessage type enum value
subtypeMessage subtype
Return values
trueType is message/news or message/rfc822
falseOtherwise

Definition at line 1653 of file parse.c.

1654{
1655 if (type != TYPE_MESSAGE)
1656 return false;
1657
1658 subtype = NONULL(subtype);
1659 return (mutt_istr_equal(subtype, "rfc822") ||
1660 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1661}
#define NONULL(x)
Definition string2.h:44
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_matches_ignore()

bool mutt_matches_ignore ( const char * s)

Does the string match the ignore list.

Parameters
sString to check
Return values
trueString matches

Checks ignore and unignore using mutt_list_match

Definition at line 367 of file parse.c.

368{
370 ASSERT(mod_data);
371
372 return mutt_list_match(s, &mod_data->ignore) && !mutt_list_match(s, &mod_data->unignore);
373}
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes).
Definition list.c:197
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
#define ASSERT(COND)
Definition signal2.h:59
Email private Module data.
Definition module_data.h:32
struct ListHead unignore
Header patterns to unignore.
Definition module_data.h:43
struct ListHead ignore
Header patterns to ignore.
Definition module_data.h:37
Container for Accounts, Notifications.
Definition neomutt.h:41
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_parse_content_type()

void mutt_parse_content_type ( const char * s,
struct Body * b )

Parse a content type.

Parameters
sString to parse
bBody to save the result

WARNING: this function modifies the 's' argument to segregate type, subtype, and parametners, so, e.g., a param of "text/plain" will be changed into "text\0plain".

Definition at line 464 of file parse.c.

465{
466 if (!s || !b)
467 return;
468
469 FREE(&b->subtype);
471
472 /* First extract any existing parameters */
473 char *pc = strchr(s, ';');
474 if (pc)
475 {
476 *pc++ = 0;
477 while (*pc && mutt_isspace(*pc))
478 pc++;
479 parse_parameters(&b->parameter, pc, false);
480
481 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
482 * but if a filename has already been set in the content-disposition,
483 * let that take precedence, and don't set it here */
484 pc = mutt_param_get(&b->parameter, "name");
485 if (pc && !b->filename)
486 b->filename = mutt_str_dup(pc);
487
488 /* this is deep and utter perversion */
489 pc = mutt_param_get(&b->parameter, "conversions");
490 if (pc)
492 }
493
494 /* Now get the subtype */
495 char *subtype = strchr(s, '/');
496 if (subtype)
497 {
498 *subtype++ = '\0';
499 for (pc = subtype; *pc && !mutt_isspace(*pc) && (*pc != ';'); pc++)
500 ; // do nothing
501
502 *pc = '\0';
503 mutt_str_replace(&b->subtype, subtype);
504 }
505
506 /* Finally, get the major type */
508
509 if (mutt_istr_equal("x-sun-attachment", s))
510 mutt_str_replace(&b->subtype, "x-sun-attachment");
511
512 if (b->type == TYPE_OTHER)
513 {
514 mutt_str_replace(&b->xtype, s);
515 }
516
517 if (!b->subtype)
518 {
519 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
520 * field, so we can attempt to convert the type to Body here. */
521 if (b->type == TYPE_TEXT)
522 {
523 b->subtype = mutt_str_dup("plain");
524 }
525 else if (b->type == TYPE_AUDIO)
526 {
527 b->subtype = mutt_str_dup("basic");
528 }
529 else if (b->type == TYPE_MESSAGE)
530 {
531 b->subtype = mutt_str_dup("rfc822");
532 }
533 else if (b->type == TYPE_OTHER)
534 {
535 char buf[128] = { 0 };
536
538 snprintf(buf, sizeof(buf), "x-%s", s);
539 b->subtype = mutt_str_dup(buf);
540 }
541 else
542 {
543 b->subtype = mutt_str_dup("x-unknown");
544 }
545 }
546
547 /* Default character set for text types. */
548 if (b->type == TYPE_TEXT)
549 {
550 pc = mutt_param_get(&b->parameter, "charset");
551 if (pc)
552 {
553 /* Microsoft Outlook seems to think it is necessary to repeat
554 * charset=, strip it off not to confuse ourselves */
555 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
556 mutt_param_set(&b->parameter, "charset", pc + (sizeof("charset=") - 1));
557 }
558 else
559 {
560 mutt_param_set(&b->parameter, "charset",
562 }
563 }
564}
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
bool mutt_isspace(int arg)
Wrapper for isspace(3).
Definition ctype.c:96
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:380
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:436
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition parse.c:116
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition charset.c:452
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
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition parameter.c:85
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition parameter.c:111
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition parameter.c:62
char * xtype
content-type if x-unknown
Definition body.h:62
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
unsigned int type
content-type primary type, ContentType
Definition body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_parse_mailto()

bool mutt_parse_mailto ( struct Envelope * env,
char ** body,
const char * src )

Parse a mailto:// url.

Parameters
[in]envEnvelope to fill
[out]bodyBody to
[in]srcString to parse
Return values
trueSuccess
falseError

Definition at line 1911 of file parse.c.

1912{
1913 if (!env || !src)
1914 return false;
1915
1916 struct Url *url = url_parse(src);
1917 if (!url)
1918 return false;
1919
1920 if (url->host)
1921 {
1922 /* this is not a path-only URL */
1923 url_free(&url);
1924 return false;
1925 }
1926
1927 mutt_addrlist_parse(&env->to, url->path);
1928
1930 ASSERT(mod_data);
1931
1932 struct UrlQuery *np = NULL;
1933 STAILQ_FOREACH(np, &url->query_strings, entries)
1934 {
1936 const char *tag = np->name;
1937 char *value = np->value;
1938 /* Determine if this header field is on the allowed list. Since NeoMutt
1939 * interprets some header fields specially (such as
1940 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1941 * only safe fields are allowed.
1942 *
1943 * RFC2368, "4. Unsafe headers"
1944 * The user agent interpreting a mailto URL SHOULD choose not to create
1945 * a message if any of the headers are considered dangerous; it may also
1946 * choose to create a message with only a subset of the headers given in
1947 * the URL. */
1948 if (mailto_header_allowed(tag, &mod_data->mail_to_allow))
1949 {
1950 if (mutt_istr_equal(tag, "body"))
1951 {
1952 if (body)
1953 mutt_str_replace(body, value);
1954 }
1955 else
1956 {
1957 char *scratch = NULL;
1958 size_t taglen = mutt_str_len(tag);
1959
1961 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1962 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1963 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1964 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1965 FREE(&scratch);
1966 }
1967 }
1968 }
1969
1970 /* RFC2047 decode after the RFC822 parsing */
1972
1973 url_free(&url);
1974 return true;
1975}
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:481
int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, const char *name, size_t name_len, const char *body, bool user_hdrs, bool weed, bool do_2047)
Parse an email header.
Definition parse.c:829
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1889
void mutt_filter_commandline_header_tag(char *header)
Sanitise characters in a header tag.
Definition parse.c:73
void mutt_filter_commandline_header_value(char *header)
Sanitise characters in a header value.
Definition parse.c:93
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:809
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:614
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition rfc2047.c:850
struct ListHead mail_to_allow
Permitted fields in a mailto: url.
Definition module_data.h:38
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
Parsed Query String.
Definition url.h:58
char * name
Query name.
Definition url.h:59
char * value
Query value.
Definition url.h:60
A parsed URL proto://user:password@host:port/path?a=1&b=2.
Definition url.h:69
struct UrlQueryList query_strings
List of query strings.
Definition url.h:76
char * host
Host.
Definition url.h:73
char * src
Raw URL string.
Definition url.h:77
char * path
Path.
Definition url.h:75
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_parse()

size_t mutt_rfc2369_parse ( const char * body,
struct ListHead * links )

Extract URLs from an RFC 2369 list header.

Parameters
bodyBody of the header
linksList of extracted URLs
Return values
numNumber of URLs extracted

Definition at line 669 of file parse.c.

670{
671 if (!body || !links)
672 return 0;
673
674 if (!links->stqh_last)
675 STAILQ_INIT(links);
676
677 size_t count = 0;
678 for (const char *beg = body; (beg = strchr(beg, '<')) != NULL; beg++)
679 {
680 beg++;
681
682 const char *end = strchr(beg, '>');
683 if (!end)
684 break;
685
686 if (end > beg)
687 {
688 mutt_list_insert_tail(links, mutt_strn_dup(beg, end - beg));
689 count++;
690 }
691
692 beg = end;
693 }
694
695 return count;
696}
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
#define STAILQ_INIT(head)
Definition queue.h:410
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_first_mailto()

char * mutt_rfc2369_first_mailto ( const char * body)

Extract the first mailto: URL from an RFC 2369 list.

Parameters
bodyBody of the header
Return values
ptrFirst mailto: URL found, or NULL if none was found

Definition at line 703 of file parse.c.

704{
705 return rfc2369_first_value(body, true);
706}
static char * rfc2369_first_value(const char *body, bool mailto_only)
Extract the first useful URL from an RFC 2369 list.
Definition parse.c:638
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_list_headers_free()

void mutt_rfc2369_list_headers_free ( struct Rfc2369ListHeaders * headers)

Free RFC 2369 mailing-list header values.

Parameters
headersHeaders to free

Definition at line 735 of file parse.c.

736{
737 if (!headers)
738 return;
739
741
742 mutt_list_free(&headers->archive);
743 mutt_list_free(&headers->help);
744 mutt_list_free(&headers->owner);
745 mutt_list_free(&headers->post);
746 mutt_list_free(&headers->subscribe);
747 mutt_list_free(&headers->unsubscribe);
748}
static void rfc2369_list_headers_ensure_init(struct Rfc2369ListHeaders *headers)
Initialise RFC 2369 header lists.
Definition parse.c:712
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
struct ListHead owner
List-Owner.
Definition parse.h:44
struct ListHead archive
List-Archive.
Definition parse.h:42
struct ListHead post
List-Post.
Definition parse.h:45
struct ListHead unsubscribe
List-Unsubscribe.
Definition parse.h:47
struct ListHead subscribe
List-Subscribe.
Definition parse.h:46
struct ListHead help
List-Help.
Definition parse.h:43
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_read_headers()

void mutt_rfc2369_read_headers ( FILE * fp,
struct Rfc2369ListHeaders * headers )

Read RFC 2369 mailing-list headers.

Parameters
fpMessage stream positioned at the start of the headers
headersParsed header values

Definition at line 755 of file parse.c.

756{
757 if (!fp || !headers)
758 return;
759
762
763 struct Buffer *line = buf_pool_get();
764 while (true)
765 {
766 size_t len = mutt_rfc822_read_line(fp, line);
767 if ((len == 0) || buf_is_empty(line))
768 break;
769
770 const char *lines = buf_string(line);
771 const char *p = strpbrk(lines, ": \t");
772 if (!p || (*p != ':'))
773 {
774 time_t t = 0;
775
776 /* Some MTAs quote the original mbox separator. */
777 if (mutt_str_startswith(lines, ">From "))
778 continue;
779 if (is_from(lines, NULL, 0, &t))
780 continue;
781
782 break;
783 }
784
785 const size_t name_len = p - lines;
786 const char *body = mutt_str_skip_whitespace(p + 1);
787 if (*body == '\0')
788 continue;
789
790 struct ListHead *value = NULL;
791 if ((name_len == 12) && eqi12(lines, "List-Archive"))
792 value = &headers->archive;
793 else if ((name_len == 9) && eqi9(lines, "List-Help"))
794 value = &headers->help;
795 else if ((name_len == 10) && eqi10(lines, "List-Owner"))
796 value = &headers->owner;
797 else if ((name_len == 9) && eqi9(lines, "List-Post"))
798 value = &headers->post;
799 else if ((name_len == 14) && eqi14(lines, "List-Subscribe"))
800 value = &headers->subscribe;
801 else if ((name_len == 16) && eqi16(lines, "List-Unsubscribe"))
802 value = &headers->unsubscribe;
803
804 if (!value)
805 continue;
806
807 mutt_list_free(value);
808 mutt_rfc2369_parse(body, value);
809 }
810 buf_pool_release(&line);
811}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition parse.c:1278
size_t mutt_rfc2369_parse(const char *body, struct ListHead *links)
Extract URLs from an RFC 2369 list header.
Definition parse.c:669
void mutt_rfc2369_list_headers_free(struct Rfc2369ListHeaders *headers)
Free RFC 2369 mailing-list header values.
Definition parse.c:735
static bool eqi9(const char *a, const char b[9])
eqi9 - Compare two 9-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:162
static bool eqi10(const char *a, const char b[10])
eqi10 - Compare two 10-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:168
static bool eqi14(const char *a, const char b[14])
eqi14 - Compare two 14-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:192
static bool eqi12(const char *a, const char b[12])
eqi12 - Compare two 12-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:180
static bool eqi16(const char *a, const char b[16])
eqi16 - Compare two 16-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:204
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition string.c:557
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
String manipulation buffer.
Definition buffer.h:36
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_parse_multipart()

struct Body * mutt_parse_multipart ( FILE * fp,
const char * boundary,
LOFF_T end_off,
bool digest )

Parse a multipart structure.

Parameters
fpStream to read from
boundaryBody separator
end_offLength of the multipart body (used when the final boundary is missing to avoid reading too far)
digesttrue if reading a multipart/digest
Return values
ptrNew Body containing parsed structure

Definition at line 2013 of file parse.c.

2014{
2015 int counter = 0;
2016
2017 return parse_multipart(fp, boundary, end_off, digest, &counter);
2018}
static struct Body * parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest, int *counter)
Parse a multipart structure.
Definition parse.c:1740
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_parse_part()

void mutt_parse_part ( FILE * fp,
struct Body * b )

Parse a MIME part.

Parameters
fpFile to read from
bBody to store the results in

Definition at line 1982 of file parse.c.

1983{
1984 int counter = 0;
1985
1986 parse_part(fp, b, &counter);
1987}
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1669
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_read_mime_header()

struct Body * mutt_read_mime_header ( FILE * fp,
bool digest )

Parse a MIME header.

Parameters
fpstream to read from
digesttrue if reading subparts of a multipart/digest
Return values
ptrNew Body containing parsed structure

Definition at line 1517 of file parse.c.

1518{
1519 if (!fp)
1520 return NULL;
1521
1522 struct Body *b = mutt_body_new();
1523 struct Envelope *env = mutt_env_new();
1524 char *c = NULL;
1525 struct Buffer *buf = buf_pool_get();
1526 bool matched = false;
1527
1528 b->hdr_offset = ftello(fp);
1529
1530 b->encoding = ENC_7BIT; /* default from RFC1521 */
1531 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1533
1534 while (mutt_rfc822_read_line(fp, buf) != 0)
1535 {
1536 const char *line = buf_string(buf);
1537 /* Find the value of the current header */
1538 c = strchr(line, ':');
1539 if (c)
1540 {
1541 *c = '\0';
1542 c = mutt_str_skip_email_wsp(c + 1);
1543 if (*c == '\0')
1544 {
1545 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1546 continue;
1547 }
1548 }
1549 else
1550 {
1551 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1552 break;
1553 }
1554
1555 size_t plen = mutt_istr_startswith(line, "content-");
1556 if (plen != 0)
1557 {
1558 if (mutt_istr_equal("type", line + plen))
1559 {
1561 }
1562 else if (mutt_istr_equal("language", line + plen))
1563 {
1565 }
1566 else if (mutt_istr_equal("transfer-encoding", line + plen))
1567 {
1569 }
1570 else if (mutt_istr_equal("disposition", line + plen))
1571 {
1573 }
1574 else if (mutt_istr_equal("description", line + plen))
1575 {
1578 }
1579 else if (mutt_istr_equal("id", line + plen))
1580 {
1581 // strip <angle braces> from Content-ID: header
1582 char *id = c;
1583 int cid_len = mutt_str_len(c);
1584 if (cid_len > 2)
1585 {
1586 if (id[0] == '<')
1587 {
1588 id++;
1589 cid_len--;
1590 }
1591 if (id[cid_len - 1] == '>')
1592 id[cid_len - 1] = '\0';
1593 }
1595 }
1596 }
1597 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1598 {
1599 if (mutt_istr_equal("data-type", line + plen))
1600 {
1602 }
1603 else if (mutt_istr_equal("encoding-info", line + plen))
1604 {
1606 }
1607 else if (mutt_istr_equal("content-lines", line + plen))
1608 {
1609 mutt_param_set(&b->parameter, "content-lines", c);
1610 }
1611 else if (mutt_istr_equal("data-description", line + plen))
1612 {
1615 }
1616 }
1617 else
1618 {
1619 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1620 {
1621 matched = true;
1622 }
1623 }
1624 }
1625 b->offset = ftello(fp); /* Mark the start of the real data */
1626 if ((b->type == TYPE_TEXT) && !b->subtype)
1627 b->subtype = mutt_str_dup("plain");
1628 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1629 b->subtype = mutt_str_dup("rfc822");
1630
1631 buf_pool_release(&buf);
1632
1633 if (matched)
1634 {
1635 b->mime_headers = env;
1637 }
1638 else
1639 {
1640 mutt_env_free(&env);
1641 }
1642
1643 return b;
1644}
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:464
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition parse.c:351
static void parse_content_disposition(const char *s, struct Body *b)
Parse a content disposition.
Definition parse.c:255
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
@ DISP_INLINE
Content is inline.
Definition mime.h:62
The body of an email.
Definition body.h:36
char * content_id
Content-Id (RFC2392).
Definition body.h:58
LOFF_T offset
offset where the actual data begins
Definition body.h:52
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
char * description
content-description
Definition body.h:55
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
long hdr_offset
Offset in stream where the headers begin.
Definition body.h:81
The header of an Email.
Definition envelope.h:57
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc822_parse_line()

int mutt_rfc822_parse_line ( struct Envelope * env,
struct Email * e,
const char * name,
size_t name_len,
const char * body,
bool user_hdrs,
bool weed,
bool do_2047 )

Parse an email header.

Parameters
envEnvelope of the email
eEmail
nameHeader field name, e.g. 'to'
name_lenMust be equivalent to strlen(name)
bodyHeader field body, e.g. 'john@.nosp@m.exam.nosp@m.ple.c.nosp@m.om'
user_hdrsIf true, save into the Envelope's userhdrs
weedIf true, perform header weeding (filtering)
do_2047If true, perform RFC2047 decoding of the field
Return values
1The field is recognised
0The field is not recognised

Process a line from an email header. Each line that is recognised is parsed and the information put in the Envelope or Header.

Definition at line 829 of file parse.c.

832{
833 if (!env || !name)
834 return 0;
835
836 bool matched = false;
837
838 switch (name[0] | 0x20)
839 {
840 case 'a':
841 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
842 {
843 mutt_addrlist_parse(&env->to, body);
844 matched = true;
845 }
846 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
847 {
848 mutt_addrlist_parse(&env->from, body);
849 matched = true;
850 }
851#ifdef USE_AUTOCRYPT
852 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
853 {
854 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
855 if (c_autocrypt)
856 {
857 env->autocrypt = parse_autocrypt(env->autocrypt, body);
858 matched = true;
859 }
860 }
861 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
862 {
863 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
864 if (c_autocrypt)
865 {
867 matched = true;
868 }
869 }
870#endif
871 break;
872
873 case 'b':
874 if ((name_len == 3) && eqi2(name + 1, "cc"))
875 {
876 mutt_addrlist_parse(&env->bcc, body);
877 matched = true;
878 }
879 break;
880
881 case 'c':
882 if ((name_len == 2) && eqi1(name + 1, "c"))
883 {
884 mutt_addrlist_parse(&env->cc, body);
885 matched = true;
886 }
887 else
888 {
889 if ((name_len >= 12) && eqi8(name, "content-"))
890 {
891 if ((name_len == 12) && eqi4(name + 8, "type"))
892 {
893 if (e)
895 matched = true;
896 }
897 else if ((name_len == 16) && eqi8(name + 8, "language"))
898 {
899 if (e)
901 matched = true;
902 }
903 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
904 {
905 if (e)
907 matched = true;
908 }
909 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
910 {
911 if (e)
912 {
913 unsigned long len = 0;
914 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
915 }
916 matched = true;
917 }
918 else if ((name_len == 19) && eqi11(name + 8, "description"))
919 {
920 if (e)
921 {
924 }
925 matched = true;
926 }
927 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
928 {
929 if (e)
931 matched = true;
932 }
933 }
934 }
935 break;
936
937 case 'd':
938 if ((name_len != 4) || !eqi4(name, "date"))
939 break;
940
941 mutt_str_replace(&env->date, body);
942 if (e)
943 {
944 struct Tz tz = { 0 };
945 // the caller will check e->date_sent for -1
946 e->date_sent = mutt_date_parse_date(body, &tz);
947 if (e->date_sent > 0)
948 {
949 e->zhours = tz.zhours;
950 e->zminutes = tz.zminutes;
951 e->zoccident = tz.zoccident;
952 }
953 }
954 matched = true;
955 break;
956
957 case 'e':
958 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
959 {
960 const time_t expired = mutt_date_parse_date(body, NULL);
961 if ((expired != -1) && (expired < mutt_date_now()))
962 {
963 e->expired = true;
964 }
965 }
966 break;
967
968 case 'f':
969 if ((name_len == 4) && eqi4(name, "from"))
970 {
971 mutt_addrlist_parse(&env->from, body);
972 matched = true;
973 }
974 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
975 {
976 if (!env->followup_to)
977 {
980 }
981 matched = true;
982 }
983 break;
984
985 case 'i':
986 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
987 break;
988
990 char *body2 = mutt_str_dup(body); // Create a mutable copy
992 parse_references(&env->in_reply_to, body2);
993 FREE(&body2);
994 matched = true;
995 break;
996
997 case 'l':
998 if ((name_len == 5) && eqi4(name + 1, "ines"))
999 {
1000 if (e)
1001 {
1002 unsigned int ui = 0; // we don't want a negative number of lines
1003 mutt_str_atoui(body, &ui);
1004 e->lines = ui;
1005 }
1006
1007 matched = true;
1008 }
1009 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
1010 {
1011 /* RFC2369 */
1012 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
1013 {
1014 char *mailto = mutt_rfc2369_first_mailto(body);
1015 if (mailto)
1016 {
1017 FREE(&env->list_post);
1018 env->list_post = mailto;
1019 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
1020 if (c_auto_subscribe)
1022 }
1023 }
1024 matched = true;
1025 }
1026 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
1027 {
1028 /* RFC2369 */
1029 char *uri = rfc2369_first_value(body, false);
1030 if (uri)
1031 {
1032 FREE(&env->list_subscribe);
1033 env->list_subscribe = uri;
1034 }
1035 matched = true;
1036 }
1037 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
1038 {
1039 /* RFC2369 */
1040 char *uri = rfc2369_first_value(body, false);
1041 if (uri)
1042 {
1043 FREE(&env->list_unsubscribe);
1044 env->list_unsubscribe = uri;
1045 }
1046 matched = true;
1047 }
1048 break;
1049
1050 case 'm':
1051 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
1052 {
1053 if (e)
1054 e->mime = true;
1055 matched = true;
1056 }
1057 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
1058 {
1059 /* We add a new "Message-ID:" when building a message */
1060 FREE(&env->message_id);
1061 env->message_id = mutt_extract_message_id(body, NULL);
1062 matched = true;
1063 }
1064 else
1065 {
1066 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
1067 {
1068 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
1069 {
1070 /* override the Reply-To: field */
1072 mutt_addrlist_parse(&env->reply_to, body);
1073 matched = true;
1074 }
1075 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
1076 {
1078 matched = true;
1079 }
1080 }
1081 }
1082 break;
1083
1084 case 'n':
1085 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
1086 {
1087 FREE(&env->newsgroups);
1090 matched = true;
1091 }
1092 break;
1093
1094 case 'o':
1095 /* field 'Organization:' saves only for pager! */
1096 if ((name_len == 12) && eqi11(name + 1, "rganization"))
1097 {
1098 if (!env->organization && !mutt_istr_equal(body, "unknown"))
1099 env->organization = mutt_str_dup(body);
1100 }
1101 break;
1102
1103 case 'r':
1104 if ((name_len == 10) && eqi9(name + 1, "eferences"))
1105 {
1107 parse_references(&env->references, body);
1108 matched = true;
1109 }
1110 else if ((name_len == 8) && eqi8(name, "reply-to"))
1111 {
1112 mutt_addrlist_parse(&env->reply_to, body);
1113 matched = true;
1114 }
1115 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
1116 {
1117 mutt_addrlist_parse(&env->return_path, body);
1118 matched = true;
1119 }
1120 else if ((name_len == 8) && eqi8(name, "received"))
1121 {
1122 if (e && (e->received == 0))
1123 {
1124 const char *d = strrchr(body, ';');
1125 if (d)
1126 {
1127 d = mutt_str_skip_email_wsp(d + 1);
1128 // the caller will check e->received for -1
1129 e->received = mutt_date_parse_date(d, NULL);
1130 }
1131 }
1132 }
1133 break;
1134
1135 case 's':
1136 if ((name_len == 7) && eqi6(name + 1, "ubject"))
1137 {
1138 if (!env->subject)
1139 mutt_env_set_subject(env, body);
1140 matched = true;
1141 }
1142 else if ((name_len == 6) && eqi5(name + 1, "ender"))
1143 {
1144 mutt_addrlist_parse(&env->sender, body);
1145 matched = true;
1146 }
1147 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
1148 {
1149 if (e)
1150 {
1151 while (*body)
1152 {
1153 switch (*body)
1154 {
1155 case 'O':
1156 {
1157 e->old = true;
1158 break;
1159 }
1160 case 'R':
1161 e->read = true;
1162 break;
1163 case 'r':
1164 e->replied = true;
1165 break;
1166 default:
1167 break;
1168 }
1169 body++;
1170 }
1171 }
1172 matched = true;
1173 }
1174 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1175 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1176 {
1177 FREE(&env->supersedes);
1178 env->supersedes = mutt_str_dup(body);
1179 }
1180 break;
1181
1182 case 't':
1183 if ((name_len == 2) && eqi1(name + 1, "o"))
1184 {
1185 mutt_addrlist_parse(&env->to, body);
1186 matched = true;
1187 }
1188 break;
1189
1190 case 'x':
1191 if ((name_len == 8) && eqi8(name, "x-status"))
1192 {
1193 if (e)
1194 {
1195 while (*body)
1196 {
1197 switch (*body)
1198 {
1199 case 'A':
1200 e->replied = true;
1201 break;
1202 case 'D':
1203 e->deleted = true;
1204 break;
1205 case 'F':
1206 e->flagged = true;
1207 break;
1208 default:
1209 break;
1210 }
1211 body++;
1212 }
1213 }
1214 matched = true;
1215 }
1216 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1217 {
1218 FREE(&env->x_label);
1219 env->x_label = mutt_str_dup(body);
1220 matched = true;
1221 }
1222 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1223 {
1224 if (!env->x_comment_to)
1225 env->x_comment_to = mutt_str_dup(body);
1226 matched = true;
1227 }
1228 else if ((name_len == 4) && eqi4(name, "xref"))
1229 {
1230 if (!env->xref)
1231 env->xref = mutt_str_dup(body);
1232 matched = true;
1233 }
1234 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1235 {
1237 matched = true;
1238 }
1239 break;
1240
1241 default:
1242 break;
1243 }
1244
1245 /* Keep track of the user-defined headers */
1246 if (!matched && user_hdrs)
1247 {
1248 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1249 char *dup = NULL;
1250 mutt_str_asprintf(&dup, "%s: %s", name, body);
1251
1252 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1253 {
1254 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1255 if (do_2047)
1256 {
1257 rfc2047_decode(&np->data);
1258 }
1259 }
1260 else
1261 {
1262 FREE(&dup);
1263 }
1264 }
1265
1266 return matched;
1267}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1473
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition commands.c:49
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition atoi.c:243
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
char * mutt_rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from an RFC 2369 list.
Definition parse.c:703
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition parse.c:573
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition parse.c:324
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:367
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:417
#define CONTENT_TOO_BIG
Maximum reasonable Content-Length value (1 GiB) to prevent overflow.
Definition parse.c:62
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
static bool eqi17(const char *a, const char b[17])
eqi17 - Compare two 17-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:210
static bool eqi8(const char *a, const char b[8])
Compare two 8-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:126
static bool eqi11(const char *a, const char b[11])
eqi11 - Compare two 11-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:174
static bool eqi6(const char *a, const char b[6])
eqi6 - Compare two 6-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:154
static bool eqi13(const char *a, const char b[13])
eqi13 - Compare two 13-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:186
static bool eqi4(const char *a, const char b[4])
Compare two 4-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:107
static bool eqi5(const char *a, const char b[5])
eqi5 - Compare two 5-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:148
static bool eqi15(const char *a, const char b[15])
eqi15 - Compare two 15-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:198
static bool eqi1(const char *a, const char b[1])
Compare two 1-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:78
static bool eqi2(const char *a, const char b[2])
Compare two 2-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:90
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
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 mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:571
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition string.c:429
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
bool read
Email is read.
Definition email.h:50
unsigned int zminutes
Minutes away from UTC.
Definition email.h:57
bool mime
Has a MIME-Version header?
Definition email.h:48
int lines
How many lines in the body of this message?
Definition email.h:62
struct Body * body
List of MIME parts.
Definition email.h:69
bool old
Email is seen, but unread.
Definition email.h:49
bool zoccident
True, if west of UTC, False if east.
Definition email.h:58
bool flagged
Marked important?
Definition email.h:47
unsigned int zhours
Hours away from UTC.
Definition email.h:56
time_t date_sent
Time when the message was sent (UTC).
Definition email.h:60
bool replied
Email has been replied to.
Definition email.h:51
bool expired
Already expired?
Definition email.h:46
bool deleted
Email is deleted.
Definition email.h:78
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
char * supersedes
Supersedes header.
Definition envelope.h:74
char * list_subscribe
This stores a list URI, preferring mailto.
Definition envelope.h:68
struct AddressList return_path
Return path for the Email.
Definition envelope.h:58
char *const subject
Email's subject.
Definition envelope.h:70
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
struct AutocryptHeader * autocrypt_gossip
Autocrypt Gossip header.
Definition envelope.h:88
char * newsgroups
List of newsgroups.
Definition envelope.h:78
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 AutocryptHeader * autocrypt
Autocrypt header.
Definition envelope.h:87
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
char * xref
List of cross-references.
Definition envelope.h:79
char * organization
Organisation header.
Definition envelope.h:77
char * x_label
X-Label.
Definition envelope.h:76
char * list_post
This stores a mailto URL, or nothing.
Definition envelope.h:67
char * date
Sent date.
Definition envelope.h:75
char * list_unsubscribe
This stores a list URI, preferring mailto.
Definition envelope.h:69
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
List of recognised Timezones.
Definition date.h:53
unsigned char zminutes
Minutes away from UTC.
Definition date.h:56
bool zoccident
True if west of UTC, False if East.
Definition date.h:57
unsigned char zhours
Hours away from UTC.
Definition date.h:55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc822_parse_message()

struct Body * mutt_rfc822_parse_message ( FILE * fp,
struct Body * b )

Parse a Message/RFC822 body.

Parameters
fpStream to read from
bInfo about the message/rfc822 body part
Return values
ptrNew Body containing parsed message
Note
This assumes that 'b->length' has been set!

Definition at line 1997 of file parse.c.

1998{
1999 int counter = 0;
2000
2001 return rfc822_parse_message(fp, b, &counter);
2002}
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1852
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc822_read_header()

struct Envelope * mutt_rfc822_read_header ( FILE * fp,
struct Email * e,
bool user_hdrs,
bool weed )

Parses an RFC822 header.

Parameters
fpStream to read from
eCurrent Email (optional)
user_hdrsIf set, store user headers Used for recall-message and postpone modes
weedIf this parameter is set and the user has activated the $weed option, honor the header weed list for user headers. Used for recall-message
Return values
ptrNewly allocated envelope structure

Caller should free the Envelope using mutt_env_free().

Definition at line 1358 of file parse.c.

1359{
1360 if (!fp)
1361 return NULL;
1362
1363 struct Envelope *env = mutt_env_new();
1364 char *p = NULL;
1365 LOFF_T loc = ftello(fp);
1366 if (loc < 0)
1367 {
1368 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1369 loc = 0;
1370 }
1371
1372 struct Buffer *line = buf_pool_get();
1373
1374 if (e)
1375 {
1376 if (!e->body)
1377 {
1378 e->body = mutt_body_new();
1379
1380 /* set the defaults from RFC1521 */
1381 e->body->type = TYPE_TEXT;
1382 e->body->subtype = mutt_str_dup("plain");
1383 e->body->encoding = ENC_7BIT;
1384 e->body->length = -1;
1385
1386 /* RFC2183 says this is arbitrary */
1388 }
1389 }
1390
1392 ASSERT(mod_data);
1393
1394 while (true)
1395 {
1396 LOFF_T line_start_loc = loc;
1397 size_t len = mutt_rfc822_read_line(fp, line);
1398 if (buf_is_empty(line))
1399 {
1400 break;
1401 }
1402 loc += len;
1403 const char *lines = buf_string(line);
1404 p = strpbrk(lines, ": \t");
1405 if (!p || (*p != ':'))
1406 {
1407 time_t t = 0;
1408
1409 /* some bogus MTAs will quote the original "From " line */
1410 if (mutt_str_startswith(lines, ">From "))
1411 {
1412 continue; /* just ignore */
1413 }
1414 else if (is_from(lines, NULL, 0, &t))
1415 {
1416 /* MH sometimes has the From_ line in the middle of the header! */
1417 if (e && (e->received == 0))
1418 e->received = t - mutt_date_local_tz(t);
1419 continue;
1420 }
1421
1422 /* We need to seek back to the start of the body. Note that we
1423 * keep track of loc ourselves, since calling ftello() incurs
1424 * a syscall, which can be expensive to do for every single line */
1425 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1426 break; /* end of header */
1427 }
1428 size_t name_len = p - lines;
1429
1430 char buf[1024] = { 0 };
1431 if (mutt_replacelist_match(&mod_data->spam, buf, sizeof(buf), lines))
1432 {
1433 if (!mutt_regexlist_match(&mod_data->no_spam, lines))
1434 {
1435 /* if spam tag already exists, figure out how to amend it */
1436 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1437 {
1438 /* If `$spam_separator` defined, append with separator */
1439 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1440 if (c_spam_separator)
1441 {
1442 buf_addstr(&env->spam, c_spam_separator);
1443 buf_addstr(&env->spam, buf);
1444 }
1445 else /* overwrite */
1446 {
1447 buf_reset(&env->spam);
1448 buf_addstr(&env->spam, buf);
1449 }
1450 }
1451 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1452 {
1453 /* spam tag is new, and match expr is non-empty; copy */
1454 buf_addstr(&env->spam, buf);
1455 }
1456 else if (buf_is_empty(&env->spam))
1457 {
1458 /* match expr is empty; plug in null string if no existing tag */
1459 buf_addstr(&env->spam, "");
1460 }
1461
1462 if (!buf_is_empty(&env->spam))
1463 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1464 }
1465 }
1466
1467 *p = '\0';
1468 p = mutt_str_skip_email_wsp(p + 1);
1469 if (*p == '\0')
1470 continue; /* skip empty header fields */
1471
1472 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1473 }
1474
1475 buf_pool_release(&line);
1476
1477 if (e)
1478 {
1479 e->body->hdr_offset = e->offset;
1480 e->body->offset = ftello(fp);
1481
1483
1484 if (e->received < 0)
1485 {
1486 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1487 e->received = 0;
1488 }
1489
1490 /* check for missing or invalid date */
1491 if (e->date_sent <= 0)
1492 {
1493 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1494 e->date_sent = e->received;
1495 }
1496
1497#ifdef USE_AUTOCRYPT
1498 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1499 if (c_autocrypt)
1500 {
1502 /* No sense in taking up memory after the header is processed */
1504 }
1505#endif
1506 }
1507
1508 return env;
1509}
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition autocrypt.c:263
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
void mutt_autocrypthdr_free(struct AutocryptHeader **ptr)
Free an AutocryptHeader.
Definition envelope.c:103
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition regex.c:481
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition regex.c:201
char * data
Pointer to data.
Definition buffer.h:37
struct ReplaceList spam
Regexes and patterns to match spam emails.
Definition module_data.h:40
struct RegexList no_spam
Regexes to identify non-spam emails.
Definition module_data.h:39
LOFF_T offset
Where in the stream does this message begin?
Definition email.h:71
struct Buffer spam
Spam header.
Definition envelope.h:82
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc822_read_line()

size_t mutt_rfc822_read_line ( FILE * fp,
struct Buffer * buf )

Read a header line from a file.

Parameters
fpFile to read from
bufBuffer to store the result
Return values
numNumber of bytes read from fp

Reads an arbitrarily long header field, and looks ahead for continuation lines.

Definition at line 1278 of file parse.c.

1279{
1280 if (!fp || !buf)
1281 return 0;
1282
1283 size_t read = 0;
1284 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1285
1286 buf_reset(buf);
1287 while (true)
1288 {
1289 if (!fgets(line, sizeof(line), fp))
1290 {
1291 return 0;
1292 }
1293
1294 const size_t linelen = mutt_str_len(line);
1295 if (linelen == 0)
1296 {
1297 break;
1298 }
1299
1300 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1301 {
1302 read = linelen;
1303 break;
1304 }
1305
1306 read += linelen;
1307
1308 size_t off = linelen - 1;
1309 if (line[off] == '\n')
1310 {
1311 /* We did get a full line: remove trailing space */
1312 do
1313 {
1314 line[off] = '\0';
1315 } while (off && mutt_str_is_email_wsp(line[--off]));
1316
1317 /* check to see if the next line is a continuation line */
1318 int ch = fgetc(fp);
1319 if ((ch != ' ') && (ch != '\t'))
1320 {
1321 /* next line is a separate header field or EOH */
1322 ungetc(ch, fp);
1323 buf_addstr(buf, line);
1324 break;
1325 }
1326 read++;
1327
1328 /* eat tabs and spaces from the beginning of the continuation line */
1329 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1330 {
1331 read++;
1332 }
1333
1334 ungetc(ch, fp);
1335 line[off + 1] = ' '; /* string is still terminated because we removed
1336 at least one whitespace char above */
1337 }
1338
1339 buf_addstr(buf, line);
1340 }
1341
1342 return read;
1343}
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header).
Definition string2.h:111
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_filter_commandline_header_tag()

void mutt_filter_commandline_header_tag ( char * header)

Sanitise characters in a header tag.

Parameters
headerString to sanitise

Definition at line 73 of file parse.c.

74{
75 if (!header)
76 return;
77
78 for (; (*header != '\0'); header++)
79 {
80 if ((*header < 33) || (*header > 126) || (*header == ':'))
81 *header = '?';
82 }
83}
Here is the caller graph for this function:

◆ mutt_filter_commandline_header_value()

void mutt_filter_commandline_header_value ( char * header)

Sanitise characters in a header value.

Parameters
headerString to sanitise

It might be preferable to use mutt_filter_unprintable() instead. This filter is being lax, but preventing a header injection via an embedded newline.

Definition at line 93 of file parse.c.

94{
95 if (!header)
96 return;
97
98 for (; (*header != '\0'); header++)
99 {
100 if ((*header == '\n') || (*header == '\r'))
101 *header = ' ';
102 }
103}
Here is the caller graph for this function: