NeoMutt  2025-12-11-980-ge38c27
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 404 of file parse.c.

405{
406 if (mutt_istr_startswith(c, "7bit"))
407 return ENC_7BIT;
408 if (mutt_istr_startswith(c, "8bit"))
409 return ENC_8BIT;
410 if (mutt_istr_startswith(c, "binary"))
411 return ENC_BINARY;
412 if (mutt_istr_startswith(c, "quoted-printable"))
414 if (mutt_istr_startswith(c, "base64"))
415 return ENC_BASE64;
416 if (mutt_istr_startswith(c, "x-uuencode"))
417 return ENC_UUENCODED;
418 if (mutt_istr_startswith(c, "uuencode"))
419 return ENC_UUENCODED;
420 return ENC_OTHER;
421}
@ 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 333 of file parse.c.

334{
335 if (mutt_istr_equal("text", s))
336 return TYPE_TEXT;
337 if (mutt_istr_equal("multipart", s))
338 return TYPE_MULTIPART;
339 if (mutt_istr_equal("x-sun-attachment", s))
340 return TYPE_MULTIPART;
341 if (mutt_istr_equal("application", s))
342 return TYPE_APPLICATION;
343 if (mutt_istr_equal("message", s))
344 return TYPE_MESSAGE;
345 if (mutt_istr_equal("image", s))
346 return TYPE_IMAGE;
347 if (mutt_istr_equal("audio", s))
348 return TYPE_AUDIO;
349 if (mutt_istr_equal("video", s))
350 return TYPE_VIDEO;
351 if (mutt_istr_equal("model", s))
352 return TYPE_MODEL;
353 if (mutt_istr_equal("*", s))
354 return TYPE_ANY;
355 if (mutt_istr_equal(".*", s))
356 return TYPE_ANY;
357
358 return TYPE_OTHER;
359}
@ 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
Return values
ptrMessage id found
NULLNo more message ids

Definition at line 368 of file parse.c.

369{
370 if (!s || (*s == '\0'))
371 return NULL;
372
373 char *decoded = mutt_str_dup(s);
374 rfc2047_decode(&decoded);
375
376 char *res = NULL;
377
378 for (const char *p = decoded, *beg = NULL; *p; p++)
379 {
380 if (*p == '<')
381 {
382 beg = p;
383 continue;
384 }
385
386 if (beg && (*p == '>'))
387 {
388 if (len)
389 *len = p - decoded + 1;
390 res = mutt_strn_dup(beg, (p + 1) - beg);
391 break;
392 }
393 }
394
395 FREE(&decoded);
396 return res;
397}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
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 1620 of file parse.c.

1621{
1622 if (type != TYPE_MESSAGE)
1623 return false;
1624
1625 subtype = NONULL(subtype);
1626 return (mutt_istr_equal(subtype, "rfc822") ||
1627 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1628}
#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 320 of file parse.c.

321{
323 ASSERT(mod_data);
324
325 return mutt_list_match(s, &mod_data->ignore) && !mutt_list_match(s, &mod_data->unignore);
326}
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 433 of file parse.c.

434{
435 if (!s || !b)
436 return;
437
438 FREE(&b->subtype);
440
441 /* First extract any existing parameters */
442 char *pc = strchr(s, ';');
443 if (pc)
444 {
445 *pc++ = 0;
446 while (*pc && mutt_isspace(*pc))
447 pc++;
448 parse_parameters(&b->parameter, pc, false);
449
450 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
451 * but if a filename has already been set in the content-disposition,
452 * let that take precedence, and don't set it here */
453 pc = mutt_param_get(&b->parameter, "name");
454 if (pc && !b->filename)
455 b->filename = mutt_str_dup(pc);
456
457 /* this is deep and utter perversion */
458 pc = mutt_param_get(&b->parameter, "conversions");
459 if (pc)
461 }
462
463 /* Now get the subtype */
464 char *subtype = strchr(s, '/');
465 if (subtype)
466 {
467 *subtype++ = '\0';
468 for (pc = subtype; *pc && !mutt_isspace(*pc) && (*pc != ';'); pc++)
469 ; // do nothing
470
471 *pc = '\0';
472 mutt_str_replace(&b->subtype, subtype);
473 }
474
475 /* Finally, get the major type */
477
478 if (mutt_istr_equal("x-sun-attachment", s))
479 mutt_str_replace(&b->subtype, "x-sun-attachment");
480
481 if (b->type == TYPE_OTHER)
482 {
483 mutt_str_replace(&b->xtype, s);
484 }
485
486 if (!b->subtype)
487 {
488 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
489 * field, so we can attempt to convert the type to Body here. */
490 if (b->type == TYPE_TEXT)
491 {
492 b->subtype = mutt_str_dup("plain");
493 }
494 else if (b->type == TYPE_AUDIO)
495 {
496 b->subtype = mutt_str_dup("basic");
497 }
498 else if (b->type == TYPE_MESSAGE)
499 {
500 b->subtype = mutt_str_dup("rfc822");
501 }
502 else if (b->type == TYPE_OTHER)
503 {
504 char buf[128] = { 0 };
505
507 snprintf(buf, sizeof(buf), "x-%s", s);
508 b->subtype = mutt_str_dup(buf);
509 }
510 else
511 {
512 b->subtype = mutt_str_dup("x-unknown");
513 }
514 }
515
516 /* Default character set for text types. */
517 if (b->type == TYPE_TEXT)
518 {
519 pc = mutt_param_get(&b->parameter, "charset");
520 if (pc)
521 {
522 /* Microsoft Outlook seems to think it is necessary to repeat
523 * charset=, strip it off not to confuse ourselves */
524 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
525 mutt_param_set(&b->parameter, "charset", pc + (sizeof("charset=") - 1));
526 }
527 else
528 {
529 mutt_param_set(&b->parameter, "charset",
531 }
532 }
533}
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:333
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:404
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 1878 of file parse.c.

1879{
1880 if (!env || !src)
1881 return false;
1882
1883 struct Url *url = url_parse(src);
1884 if (!url)
1885 return false;
1886
1887 if (url->host)
1888 {
1889 /* this is not a path-only URL */
1890 url_free(&url);
1891 return false;
1892 }
1893
1894 mutt_addrlist_parse(&env->to, url->path);
1895
1897 ASSERT(mod_data);
1898
1899 struct UrlQuery *np = NULL;
1900 STAILQ_FOREACH(np, &url->query_strings, entries)
1901 {
1903 const char *tag = np->name;
1904 char *value = np->value;
1905 /* Determine if this header field is on the allowed list. Since NeoMutt
1906 * interprets some header fields specially (such as
1907 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1908 * only safe fields are allowed.
1909 *
1910 * RFC2368, "4. Unsafe headers"
1911 * The user agent interpreting a mailto URL SHOULD choose not to create
1912 * a message if any of the headers are considered dangerous; it may also
1913 * choose to create a message with only a subset of the headers given in
1914 * the URL. */
1915 if (mailto_header_allowed(tag, &mod_data->mail_to_allow))
1916 {
1917 if (mutt_istr_equal(tag, "body"))
1918 {
1919 if (body)
1920 mutt_str_replace(body, value);
1921 }
1922 else
1923 {
1924 char *scratch = NULL;
1925 size_t taglen = mutt_str_len(tag);
1926
1928 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1929 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1930 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1931 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1932 FREE(&scratch);
1933 }
1934 }
1935 }
1936
1937 /* RFC2047 decode after the RFC822 parsing */
1939
1940 url_free(&url);
1941 return true;
1942}
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:798
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1856
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 638 of file parse.c.

639{
640 if (!body || !links)
641 return 0;
642
643 if (!links->stqh_last)
644 STAILQ_INIT(links);
645
646 size_t count = 0;
647 for (const char *beg = body; (beg = strchr(beg, '<')) != NULL; beg++)
648 {
649 beg++;
650
651 const char *end = strchr(beg, '>');
652 if (!end)
653 break;
654
655 if (end > beg)
656 {
657 mutt_list_insert_tail(links, mutt_strn_dup(beg, end - beg));
658 count++;
659 }
660
661 beg = end;
662 }
663
664 return count;
665}
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
#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 672 of file parse.c.

673{
674 return rfc2369_first_value(body, true);
675}
static char * rfc2369_first_value(const char *body, bool mailto_only)
Extract the first useful URL from an RFC 2369 list.
Definition parse.c:607
+ 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 704 of file parse.c.

705{
706 if (!headers)
707 return;
708
710
711 mutt_list_free(&headers->archive);
712 mutt_list_free(&headers->help);
713 mutt_list_free(&headers->owner);
714 mutt_list_free(&headers->post);
715 mutt_list_free(&headers->subscribe);
716 mutt_list_free(&headers->unsubscribe);
717}
static void rfc2369_list_headers_ensure_init(struct Rfc2369ListHeaders *headers)
Initialise RFC 2369 header lists.
Definition parse.c:681
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 724 of file parse.c.

725{
726 if (!fp || !headers)
727 return;
728
731
732 struct Buffer *line = buf_pool_get();
733 while (true)
734 {
735 size_t len = mutt_rfc822_read_line(fp, line);
736 if ((len == 0) || buf_is_empty(line))
737 break;
738
739 const char *lines = buf_string(line);
740 const char *p = strpbrk(lines, ": \t");
741 if (!p || (*p != ':'))
742 {
743 time_t t = 0;
744
745 /* Some MTAs quote the original mbox separator. */
746 if (mutt_str_startswith(lines, ">From "))
747 continue;
748 if (is_from(lines, NULL, 0, &t))
749 continue;
750
751 break;
752 }
753
754 const size_t name_len = p - lines;
755 const char *body = mutt_str_skip_whitespace(p + 1);
756 if (*body == '\0')
757 continue;
758
759 struct ListHead *value = NULL;
760 if ((name_len == 12) && eqi12(lines, "List-Archive"))
761 value = &headers->archive;
762 else if ((name_len == 9) && eqi9(lines, "List-Help"))
763 value = &headers->help;
764 else if ((name_len == 10) && eqi10(lines, "List-Owner"))
765 value = &headers->owner;
766 else if ((name_len == 9) && eqi9(lines, "List-Post"))
767 value = &headers->post;
768 else if ((name_len == 14) && eqi14(lines, "List-Subscribe"))
769 value = &headers->subscribe;
770 else if ((name_len == 16) && eqi16(lines, "List-Unsubscribe"))
771 value = &headers->unsubscribe;
772
773 if (!value)
774 continue;
775
776 mutt_list_free(value);
777 mutt_rfc2369_parse(body, value);
778 }
779 buf_pool_release(&line);
780}
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:1245
size_t mutt_rfc2369_parse(const char *body, struct ListHead *links)
Extract URLs from an RFC 2369 list header.
Definition parse.c:638
void mutt_rfc2369_list_headers_free(struct Rfc2369ListHeaders *headers)
Free RFC 2369 mailing-list header values.
Definition parse.c:704
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 1980 of file parse.c.

1981{
1982 int counter = 0;
1983
1984 return parse_multipart(fp, boundary, end_off, digest, &counter);
1985}
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:1707
+ 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 1949 of file parse.c.

1950{
1951 int counter = 0;
1952
1953 parse_part(fp, b, &counter);
1954}
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1636
+ 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 1484 of file parse.c.

1485{
1486 if (!fp)
1487 return NULL;
1488
1489 struct Body *b = mutt_body_new();
1490 struct Envelope *env = mutt_env_new();
1491 char *c = NULL;
1492 struct Buffer *buf = buf_pool_get();
1493 bool matched = false;
1494
1495 b->hdr_offset = ftello(fp);
1496
1497 b->encoding = ENC_7BIT; /* default from RFC1521 */
1498 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1500
1501 while (mutt_rfc822_read_line(fp, buf) != 0)
1502 {
1503 const char *line = buf_string(buf);
1504 /* Find the value of the current header */
1505 c = strchr(line, ':');
1506 if (c)
1507 {
1508 *c = '\0';
1509 c = mutt_str_skip_email_wsp(c + 1);
1510 if (*c == '\0')
1511 {
1512 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1513 continue;
1514 }
1515 }
1516 else
1517 {
1518 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1519 break;
1520 }
1521
1522 size_t plen = mutt_istr_startswith(line, "content-");
1523 if (plen != 0)
1524 {
1525 if (mutt_istr_equal("type", line + plen))
1526 {
1528 }
1529 else if (mutt_istr_equal("language", line + plen))
1530 {
1532 }
1533 else if (mutt_istr_equal("transfer-encoding", line + plen))
1534 {
1536 }
1537 else if (mutt_istr_equal("disposition", line + plen))
1538 {
1540 }
1541 else if (mutt_istr_equal("description", line + plen))
1542 {
1545 }
1546 else if (mutt_istr_equal("id", line + plen))
1547 {
1548 // strip <angle braces> from Content-ID: header
1549 char *id = c;
1550 int cid_len = mutt_str_len(c);
1551 if (cid_len > 2)
1552 {
1553 if (id[0] == '<')
1554 {
1555 id++;
1556 cid_len--;
1557 }
1558 if (id[cid_len - 1] == '>')
1559 id[cid_len - 1] = '\0';
1560 }
1562 }
1563 }
1564 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1565 {
1566 if (mutt_istr_equal("data-type", line + plen))
1567 {
1569 }
1570 else if (mutt_istr_equal("encoding-info", line + plen))
1571 {
1573 }
1574 else if (mutt_istr_equal("content-lines", line + plen))
1575 {
1576 mutt_param_set(&b->parameter, "content-lines", c);
1577 }
1578 else if (mutt_istr_equal("data-description", line + plen))
1579 {
1582 }
1583 }
1584 else
1585 {
1586 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1587 {
1588 matched = true;
1589 }
1590 }
1591 }
1592 b->offset = ftello(fp); /* Mark the start of the real data */
1593 if ((b->type == TYPE_TEXT) && !b->subtype)
1594 b->subtype = mutt_str_dup("plain");
1595 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1596 b->subtype = mutt_str_dup("rfc822");
1597
1598 buf_pool_release(&buf);
1599
1600 if (matched)
1601 {
1602 b->mime_headers = env;
1604 }
1605 else
1606 {
1607 mutt_env_free(&env);
1608 }
1609
1610 return b;
1611}
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:433
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition parse.c:304
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 798 of file parse.c.

801{
802 if (!env || !name)
803 return 0;
804
805 bool matched = false;
806
807 switch (name[0] | 0x20)
808 {
809 case 'a':
810 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
811 {
812 mutt_addrlist_parse(&env->to, body);
813 matched = true;
814 }
815 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
816 {
817 mutt_addrlist_parse(&env->from, body);
818 matched = true;
819 }
820#ifdef USE_AUTOCRYPT
821 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
822 {
823 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
824 if (c_autocrypt)
825 {
826 env->autocrypt = parse_autocrypt(env->autocrypt, body);
827 matched = true;
828 }
829 }
830 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
831 {
832 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
833 if (c_autocrypt)
834 {
836 matched = true;
837 }
838 }
839#endif
840 break;
841
842 case 'b':
843 if ((name_len == 3) && eqi2(name + 1, "cc"))
844 {
845 mutt_addrlist_parse(&env->bcc, body);
846 matched = true;
847 }
848 break;
849
850 case 'c':
851 if ((name_len == 2) && eqi1(name + 1, "c"))
852 {
853 mutt_addrlist_parse(&env->cc, body);
854 matched = true;
855 }
856 else
857 {
858 if ((name_len >= 12) && eqi8(name, "content-"))
859 {
860 if ((name_len == 12) && eqi4(name + 8, "type"))
861 {
862 if (e)
864 matched = true;
865 }
866 else if ((name_len == 16) && eqi8(name + 8, "language"))
867 {
868 if (e)
870 matched = true;
871 }
872 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
873 {
874 if (e)
876 matched = true;
877 }
878 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
879 {
880 if (e)
881 {
882 unsigned long len = 0;
883 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
884 }
885 matched = true;
886 }
887 else if ((name_len == 19) && eqi11(name + 8, "description"))
888 {
889 if (e)
890 {
893 }
894 matched = true;
895 }
896 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
897 {
898 if (e)
900 matched = true;
901 }
902 }
903 }
904 break;
905
906 case 'd':
907 if ((name_len != 4) || !eqi4(name, "date"))
908 break;
909
910 mutt_str_replace(&env->date, body);
911 if (e)
912 {
913 struct Tz tz = { 0 };
914 // the caller will check e->date_sent for -1
915 e->date_sent = mutt_date_parse_date(body, &tz);
916 if (e->date_sent > 0)
917 {
918 e->zhours = tz.zhours;
919 e->zminutes = tz.zminutes;
920 e->zoccident = tz.zoccident;
921 }
922 }
923 matched = true;
924 break;
925
926 case 'e':
927 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
928 {
929 const time_t expired = mutt_date_parse_date(body, NULL);
930 if ((expired != -1) && (expired < mutt_date_now()))
931 {
932 e->expired = true;
933 }
934 }
935 break;
936
937 case 'f':
938 if ((name_len == 4) && eqi4(name, "from"))
939 {
940 mutt_addrlist_parse(&env->from, body);
941 matched = true;
942 }
943 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
944 {
945 if (!env->followup_to)
946 {
949 }
950 matched = true;
951 }
952 break;
953
954 case 'i':
955 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
956 break;
957
959 char *body2 = mutt_str_dup(body); // Create a mutable copy
961 parse_references(&env->in_reply_to, body2);
962 FREE(&body2);
963 matched = true;
964 break;
965
966 case 'l':
967 if ((name_len == 5) && eqi4(name + 1, "ines"))
968 {
969 if (e)
970 {
971 unsigned int ui = 0; // we don't want a negative number of lines
972 mutt_str_atoui(body, &ui);
973 e->lines = ui;
974 }
975
976 matched = true;
977 }
978 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
979 {
980 /* RFC2369 */
981 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
982 {
983 char *mailto = mutt_rfc2369_first_mailto(body);
984 if (mailto)
985 {
986 FREE(&env->list_post);
987 env->list_post = mailto;
988 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
989 if (c_auto_subscribe)
991 }
992 }
993 matched = true;
994 }
995 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
996 {
997 /* RFC2369 */
998 char *uri = rfc2369_first_value(body, false);
999 if (uri)
1000 {
1001 FREE(&env->list_subscribe);
1002 env->list_subscribe = uri;
1003 }
1004 matched = true;
1005 }
1006 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
1007 {
1008 /* RFC2369 */
1009 char *uri = rfc2369_first_value(body, false);
1010 if (uri)
1011 {
1012 FREE(&env->list_unsubscribe);
1013 env->list_unsubscribe = uri;
1014 }
1015 matched = true;
1016 }
1017 break;
1018
1019 case 'm':
1020 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
1021 {
1022 if (e)
1023 e->mime = true;
1024 matched = true;
1025 }
1026 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
1027 {
1028 /* We add a new "Message-ID:" when building a message */
1029 FREE(&env->message_id);
1030 env->message_id = mutt_extract_message_id(body, NULL);
1031 matched = true;
1032 }
1033 else
1034 {
1035 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
1036 {
1037 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
1038 {
1039 /* override the Reply-To: field */
1041 mutt_addrlist_parse(&env->reply_to, body);
1042 matched = true;
1043 }
1044 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
1045 {
1047 matched = true;
1048 }
1049 }
1050 }
1051 break;
1052
1053 case 'n':
1054 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
1055 {
1056 FREE(&env->newsgroups);
1059 matched = true;
1060 }
1061 break;
1062
1063 case 'o':
1064 /* field 'Organization:' saves only for pager! */
1065 if ((name_len == 12) && eqi11(name + 1, "rganization"))
1066 {
1067 if (!env->organization && !mutt_istr_equal(body, "unknown"))
1068 env->organization = mutt_str_dup(body);
1069 }
1070 break;
1071
1072 case 'r':
1073 if ((name_len == 10) && eqi9(name + 1, "eferences"))
1074 {
1076 parse_references(&env->references, body);
1077 matched = true;
1078 }
1079 else if ((name_len == 8) && eqi8(name, "reply-to"))
1080 {
1081 mutt_addrlist_parse(&env->reply_to, body);
1082 matched = true;
1083 }
1084 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
1085 {
1086 mutt_addrlist_parse(&env->return_path, body);
1087 matched = true;
1088 }
1089 else if ((name_len == 8) && eqi8(name, "received"))
1090 {
1091 if (e && (e->received == 0))
1092 {
1093 const char *d = strrchr(body, ';');
1094 if (d)
1095 {
1096 d = mutt_str_skip_email_wsp(d + 1);
1097 // the caller will check e->received for -1
1098 e->received = mutt_date_parse_date(d, NULL);
1099 }
1100 }
1101 }
1102 break;
1103
1104 case 's':
1105 if ((name_len == 7) && eqi6(name + 1, "ubject"))
1106 {
1107 if (!env->subject)
1108 mutt_env_set_subject(env, body);
1109 matched = true;
1110 }
1111 else if ((name_len == 6) && eqi5(name + 1, "ender"))
1112 {
1113 mutt_addrlist_parse(&env->sender, body);
1114 matched = true;
1115 }
1116 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
1117 {
1118 if (e)
1119 {
1120 while (*body)
1121 {
1122 switch (*body)
1123 {
1124 case 'O':
1125 {
1126 e->old = true;
1127 break;
1128 }
1129 case 'R':
1130 e->read = true;
1131 break;
1132 case 'r':
1133 e->replied = true;
1134 break;
1135 }
1136 body++;
1137 }
1138 }
1139 matched = true;
1140 }
1141 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1142 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1143 {
1144 FREE(&env->supersedes);
1145 env->supersedes = mutt_str_dup(body);
1146 }
1147 break;
1148
1149 case 't':
1150 if ((name_len == 2) && eqi1(name + 1, "o"))
1151 {
1152 mutt_addrlist_parse(&env->to, body);
1153 matched = true;
1154 }
1155 break;
1156
1157 case 'x':
1158 if ((name_len == 8) && eqi8(name, "x-status"))
1159 {
1160 if (e)
1161 {
1162 while (*body)
1163 {
1164 switch (*body)
1165 {
1166 case 'A':
1167 e->replied = true;
1168 break;
1169 case 'D':
1170 e->deleted = true;
1171 break;
1172 case 'F':
1173 e->flagged = true;
1174 break;
1175 default:
1176 break;
1177 }
1178 body++;
1179 }
1180 }
1181 matched = true;
1182 }
1183 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1184 {
1185 FREE(&env->x_label);
1186 env->x_label = mutt_str_dup(body);
1187 matched = true;
1188 }
1189 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1190 {
1191 if (!env->x_comment_to)
1192 env->x_comment_to = mutt_str_dup(body);
1193 matched = true;
1194 }
1195 else if ((name_len == 4) && eqi4(name, "xref"))
1196 {
1197 if (!env->xref)
1198 env->xref = mutt_str_dup(body);
1199 matched = true;
1200 }
1201 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1202 {
1204 matched = true;
1205 }
1206 break;
1207
1208 default:
1209 break;
1210 }
1211
1212 /* Keep track of the user-defined headers */
1213 if (!matched && user_hdrs)
1214 {
1215 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1216 char *dup = NULL;
1217 mutt_str_asprintf(&dup, "%s: %s", name, body);
1218
1219 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1220 {
1221 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1222 if (do_2047)
1223 {
1224 rfc2047_decode(&np->data);
1225 }
1226 }
1227 else
1228 {
1229 FREE(&dup);
1230 }
1231 }
1232
1233 return matched;
1234}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1475
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:672
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition parse.c:542
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition parse.c:287
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:320
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:368
#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 1964 of file parse.c.

1965{
1966 int counter = 0;
1967
1968 return rfc822_parse_message(fp, b, &counter);
1969}
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1819
+ 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 1325 of file parse.c.

1326{
1327 if (!fp)
1328 return NULL;
1329
1330 struct Envelope *env = mutt_env_new();
1331 char *p = NULL;
1332 LOFF_T loc = ftello(fp);
1333 if (loc < 0)
1334 {
1335 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1336 loc = 0;
1337 }
1338
1339 struct Buffer *line = buf_pool_get();
1340
1341 if (e)
1342 {
1343 if (!e->body)
1344 {
1345 e->body = mutt_body_new();
1346
1347 /* set the defaults from RFC1521 */
1348 e->body->type = TYPE_TEXT;
1349 e->body->subtype = mutt_str_dup("plain");
1350 e->body->encoding = ENC_7BIT;
1351 e->body->length = -1;
1352
1353 /* RFC2183 says this is arbitrary */
1355 }
1356 }
1357
1359 ASSERT(mod_data);
1360
1361 while (true)
1362 {
1363 LOFF_T line_start_loc = loc;
1364 size_t len = mutt_rfc822_read_line(fp, line);
1365 if (buf_is_empty(line))
1366 {
1367 break;
1368 }
1369 loc += len;
1370 const char *lines = buf_string(line);
1371 p = strpbrk(lines, ": \t");
1372 if (!p || (*p != ':'))
1373 {
1374 time_t t = 0;
1375
1376 /* some bogus MTAs will quote the original "From " line */
1377 if (mutt_str_startswith(lines, ">From "))
1378 {
1379 continue; /* just ignore */
1380 }
1381 else if (is_from(lines, NULL, 0, &t))
1382 {
1383 /* MH sometimes has the From_ line in the middle of the header! */
1384 if (e && (e->received == 0))
1385 e->received = t - mutt_date_local_tz(t);
1386 continue;
1387 }
1388
1389 /* We need to seek back to the start of the body. Note that we
1390 * keep track of loc ourselves, since calling ftello() incurs
1391 * a syscall, which can be expensive to do for every single line */
1392 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1393 break; /* end of header */
1394 }
1395 size_t name_len = p - lines;
1396
1397 char buf[1024] = { 0 };
1398 if (mutt_replacelist_match(&mod_data->spam, buf, sizeof(buf), lines))
1399 {
1400 if (!mutt_regexlist_match(&mod_data->no_spam, lines))
1401 {
1402 /* if spam tag already exists, figure out how to amend it */
1403 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1404 {
1405 /* If `$spam_separator` defined, append with separator */
1406 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1407 if (c_spam_separator)
1408 {
1409 buf_addstr(&env->spam, c_spam_separator);
1410 buf_addstr(&env->spam, buf);
1411 }
1412 else /* overwrite */
1413 {
1414 buf_reset(&env->spam);
1415 buf_addstr(&env->spam, buf);
1416 }
1417 }
1418 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1419 {
1420 /* spam tag is new, and match expr is non-empty; copy */
1421 buf_addstr(&env->spam, buf);
1422 }
1423 else if (buf_is_empty(&env->spam))
1424 {
1425 /* match expr is empty; plug in null string if no existing tag */
1426 buf_addstr(&env->spam, "");
1427 }
1428
1429 if (!buf_is_empty(&env->spam))
1430 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1431 }
1432 }
1433
1434 *p = '\0';
1435 p = mutt_str_skip_email_wsp(p + 1);
1436 if (*p == '\0')
1437 continue; /* skip empty header fields */
1438
1439 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1440 }
1441
1442 buf_pool_release(&line);
1443
1444 if (e)
1445 {
1446 e->body->hdr_offset = e->offset;
1447 e->body->offset = ftello(fp);
1448
1450
1451 if (e->received < 0)
1452 {
1453 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1454 e->received = 0;
1455 }
1456
1457 /* check for missing or invalid date */
1458 if (e->date_sent <= 0)
1459 {
1460 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1461 e->date_sent = e->received;
1462 }
1463
1464#ifdef USE_AUTOCRYPT
1465 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1466 if (c_autocrypt)
1467 {
1469 /* No sense in taking up memory after the header is processed */
1471 }
1472#endif
1473 }
1474
1475 return env;
1476}
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 1245 of file parse.c.

1246{
1247 if (!fp || !buf)
1248 return 0;
1249
1250 size_t read = 0;
1251 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1252
1253 buf_reset(buf);
1254 while (true)
1255 {
1256 if (!fgets(line, sizeof(line), fp))
1257 {
1258 return 0;
1259 }
1260
1261 const size_t linelen = mutt_str_len(line);
1262 if (linelen == 0)
1263 {
1264 break;
1265 }
1266
1267 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1268 {
1269 read = linelen;
1270 break;
1271 }
1272
1273 read += linelen;
1274
1275 size_t off = linelen - 1;
1276 if (line[off] == '\n')
1277 {
1278 /* We did get a full line: remove trailing space */
1279 do
1280 {
1281 line[off] = '\0';
1282 } while (off && mutt_str_is_email_wsp(line[--off]));
1283
1284 /* check to see if the next line is a continuation line */
1285 int ch = fgetc(fp);
1286 if ((ch != ' ') && (ch != '\t'))
1287 {
1288 /* next line is a separate header field or EOH */
1289 ungetc(ch, fp);
1290 buf_addstr(buf, line);
1291 break;
1292 }
1293 read++;
1294
1295 /* eat tabs and spaces from the beginning of the continuation line */
1296 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1297 {
1298 read++;
1299 }
1300
1301 ungetc(ch, fp);
1302 line[off + 1] = ' '; /* string is still terminated because we removed
1303 at least one whitespace char above */
1304 }
1305
1306 buf_addstr(buf, line);
1307 }
1308
1309 return read;
1310}
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: