NeoMutt  2024-12-12-14-g7b49f7
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 "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.

Functions

void mutt_auto_subscribe (const char *mailto)
 Check if user is subscribed to mailing list.
 
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.
 
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_auto_subscribe()

void mutt_auto_subscribe ( const char *  mailto)

Check if user is subscribed to mailing list.

Parameters
mailtoURL of mailing list subscribe

Definition at line 109 of file parse.c.

110{
111 if (!mailto)
112 return;
113
116
118 return;
119
121
122 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
123
124 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
125 {
126 const char *mailbox = buf_string(TAILQ_FIRST(&lpenv->to)->mailbox);
127 if (mailbox && !mutt_regexlist_match(&SubscribedLists, mailbox) &&
128 !mutt_regexlist_match(&UnMailLists, mailbox) &&
130 {
131 /* mutt_regexlist_add() detects duplicates, so it is safe to
132 * try to add here without any checks. */
133 mutt_regexlist_add(&MailLists, mailbox, REG_ICASE, NULL);
134 mutt_regexlist_add(&SubscribedLists, mailbox, REG_ICASE, NULL);
135 }
136 }
137
138 mutt_env_free(&lpenv);
139}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
struct RegexList SubscribedLists
List of header patterns to unignore (see)
Definition: globals.c:48
struct HashTable * AutoSubscribeCache
< Hash Table: "mailto:" -> AutoSubscribeCache
Definition: globals.c:36
struct RegexList UnSubscribedLists
Definition: globals.c:54
struct RegexList UnMailLists
List of regexes to exclude false matches in SubscribedLists.
Definition: globals.c:52
struct RegexList MailLists
List of permitted fields in a mailto: url.
Definition: globals.c:40
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1754
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:126
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:46
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition: hash.c:335
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:362
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition: hash.h:111
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:110
int mutt_regexlist_add(struct RegexList *rl, const char *str, uint16_t flags, struct Buffer *err)
Compile a regex string and add it to a list.
Definition: regex.c:140
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition: regex.c:200
#define TAILQ_FIRST(head)
Definition: queue.h:741
#define TAILQ_EMPTY(head)
Definition: queue.h:739
The header of an Email.
Definition: envelope.h:57
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ 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 437 of file parse.c.

438{
439 if (mutt_istr_startswith(c, "7bit"))
440 return ENC_7BIT;
441 if (mutt_istr_startswith(c, "8bit"))
442 return ENC_8BIT;
443 if (mutt_istr_startswith(c, "binary"))
444 return ENC_BINARY;
445 if (mutt_istr_startswith(c, "quoted-printable"))
447 if (mutt_istr_startswith(c, "base64"))
448 return ENC_BASE64;
449 if (mutt_istr_startswith(c, "x-uuencode"))
450 return ENC_UUENCODED;
451 if (mutt_istr_startswith(c, "uuencode"))
452 return ENC_UUENCODED;
453 return ENC_OTHER;
454}
@ 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:242
+ 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 366 of file parse.c.

367{
368 if (mutt_istr_equal("text", s))
369 return TYPE_TEXT;
370 if (mutt_istr_equal("multipart", s))
371 return TYPE_MULTIPART;
372 if (mutt_istr_equal("x-sun-attachment", s))
373 return TYPE_MULTIPART;
374 if (mutt_istr_equal("application", s))
375 return TYPE_APPLICATION;
376 if (mutt_istr_equal("message", s))
377 return TYPE_MESSAGE;
378 if (mutt_istr_equal("image", s))
379 return TYPE_IMAGE;
380 if (mutt_istr_equal("audio", s))
381 return TYPE_AUDIO;
382 if (mutt_istr_equal("video", s))
383 return TYPE_VIDEO;
384 if (mutt_istr_equal("model", s))
385 return TYPE_MODEL;
386 if (mutt_istr_equal("*", s))
387 return TYPE_ANY;
388 if (mutt_istr_equal(".*", s))
389 return TYPE_ANY;
390
391 return TYPE_OTHER;
392}
@ 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:672
+ 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 401 of file parse.c.

402{
403 if (!s || (*s == '\0'))
404 return NULL;
405
406 char *decoded = mutt_str_dup(s);
407 rfc2047_decode(&decoded);
408
409 char *res = NULL;
410
411 for (const char *p = decoded, *beg = NULL; *p; p++)
412 {
413 if (*p == '<')
414 {
415 beg = p;
416 continue;
417 }
418
419 if (beg && (*p == '>'))
420 {
421 if (len)
422 *len = p - decoded + 1;
423 res = mutt_strn_dup(beg, (p + 1) - beg);
424 break;
425 }
426 }
427
428 FREE(&decoded);
429 return res;
430}
#define FREE(x)
Definition: memory.h:55
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:380
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:661
+ 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 1498 of file parse.c.

1499{
1500 if (type != TYPE_MESSAGE)
1501 return false;
1502
1503 subtype = NONULL(subtype);
1504 return (mutt_istr_equal(subtype, "rfc822") ||
1505 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1506}
#define NONULL(x)
Definition: string2.h:37
+ 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 356 of file parse.c.

357{
358 return mutt_list_match(s, &Ignore) && !mutt_list_match(s, &UnIgnore);
359}
struct ListHead Ignore
List of regexes to match mailing lists.
Definition: globals.c:38
struct ListHead UnIgnore
List of regexes to exclude false matches in MailLists.
Definition: globals.c:50
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes)
Definition: list.c:194
+ 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

e.g. parse a string "inline" and set DISP_INLINE.

Definition at line 463 of file parse.c.

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

1755{
1756 if (!env || !src)
1757 return false;
1758
1759 struct Url *url = url_parse(src);
1760 if (!url)
1761 return false;
1762
1763 if (url->host)
1764 {
1765 /* this is not a path-only URL */
1766 url_free(&url);
1767 return false;
1768 }
1769
1770 mutt_addrlist_parse(&env->to, url->path);
1771
1772 struct UrlQuery *np;
1773 STAILQ_FOREACH(np, &url->query_strings, entries)
1774 {
1776 const char *tag = np->name;
1777 char *value = np->value;
1778 /* Determine if this header field is on the allowed list. Since NeoMutt
1779 * interprets some header fields specially (such as
1780 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1781 * only safe fields are allowed.
1782 *
1783 * RFC2368, "4. Unsafe headers"
1784 * The user agent interpreting a mailto URL SHOULD choose not to create
1785 * a message if any of the headers are considered dangerous; it may also
1786 * choose to create a message with only a subset of the headers given in
1787 * the URL. */
1789 {
1790 if (mutt_istr_equal(tag, "body"))
1791 {
1792 if (body)
1793 mutt_str_replace(body, value);
1794 }
1795 else
1796 {
1797 char *scratch = NULL;
1798 size_t taglen = mutt_str_len(tag);
1799
1801 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1802 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1803 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1804 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1805 FREE(&scratch);
1806 }
1807 }
1808 }
1809
1810 /* RFC2047 decode after the RFC822 parsing */
1812
1813 url_free(&url);
1814 return true;
1815}
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:480
struct ListHead MailToAllow
List of regexes to identify non-spam emails.
Definition: globals.c:42
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:678
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition: parse.c:1732
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:803
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:608
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:832
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:239
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_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 1853 of file parse.c.

1854{
1855 int counter = 0;
1856
1857 return parse_multipart(fp, boundary, end_off, digest, &counter);
1858}
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:1585
+ 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 1822 of file parse.c.

1823{
1824 int counter = 0;
1825
1826 parse_part(fp, b, &counter);
1827}
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition: parse.c:1514
+ 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 1362 of file parse.c.

1363{
1364 if (!fp)
1365 return NULL;
1366
1367 struct Body *b = mutt_body_new();
1368 struct Envelope *env = mutt_env_new();
1369 char *c = NULL;
1370 struct Buffer *buf = buf_pool_get();
1371 bool matched = false;
1372
1373 b->hdr_offset = ftello(fp);
1374
1375 b->encoding = ENC_7BIT; /* default from RFC1521 */
1376 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1378
1379 while (mutt_rfc822_read_line(fp, buf) != 0)
1380 {
1381 const char *line = buf_string(buf);
1382 /* Find the value of the current header */
1383 c = strchr(line, ':');
1384 if (c)
1385 {
1386 *c = '\0';
1387 c = mutt_str_skip_email_wsp(c + 1);
1388 if (*c == '\0')
1389 {
1390 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1391 continue;
1392 }
1393 }
1394 else
1395 {
1396 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1397 break;
1398 }
1399
1400 size_t plen = mutt_istr_startswith(line, "content-");
1401 if (plen != 0)
1402 {
1403 if (mutt_istr_equal("type", line + plen))
1404 {
1406 }
1407 else if (mutt_istr_equal("language", line + plen))
1408 {
1410 }
1411 else if (mutt_istr_equal("transfer-encoding", line + plen))
1412 {
1414 }
1415 else if (mutt_istr_equal("disposition", line + plen))
1416 {
1418 }
1419 else if (mutt_istr_equal("description", line + plen))
1420 {
1423 }
1424 else if (mutt_istr_equal("id", line + plen))
1425 {
1426 // strip <angle braces> from Content-ID: header
1427 char *id = c;
1428 int cid_len = mutt_str_len(c);
1429 if (cid_len > 2)
1430 {
1431 if (id[0] == '<')
1432 {
1433 id++;
1434 cid_len--;
1435 }
1436 if (id[cid_len - 1] == '>')
1437 id[cid_len - 1] = '\0';
1438 }
1440 }
1441 }
1442 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1443 {
1444 if (mutt_istr_equal("data-type", line + plen))
1445 {
1447 }
1448 else if (mutt_istr_equal("encoding-info", line + plen))
1449 {
1451 }
1452 else if (mutt_istr_equal("content-lines", line + plen))
1453 {
1454 mutt_param_set(&b->parameter, "content-lines", c);
1455 }
1456 else if (mutt_istr_equal("data-description", line + plen))
1457 {
1460 }
1461 }
1462 else
1463 {
1464 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1465 {
1466 matched = true;
1467 }
1468 }
1469 }
1470 b->offset = ftello(fp); /* Mark the start of the real data */
1471 if ((b->type == TYPE_TEXT) && !b->subtype)
1472 b->subtype = mutt_str_dup("plain");
1473 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1474 b->subtype = mutt_str_dup("rfc822");
1475
1476 buf_pool_release(&buf);
1477
1478 if (matched)
1479 {
1480 b->mime_headers = env;
1482 }
1483 else
1484 {
1485 mutt_env_free(&env);
1486 }
1487
1488 return b;
1489}
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:463
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition: parse.c:1125
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition: parse.c:340
static void parse_content_disposition(const char *s, struct Body *b)
Parse a content disposition.
Definition: parse.c:291
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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
String manipulation buffer.
Definition: buffer.h:36
+ 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 678 of file parse.c.

681{
682 if (!env || !name)
683 return 0;
684
685 bool matched = false;
686
687 switch (name[0] | 0x20)
688 {
689 case 'a':
690 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
691 {
692 mutt_addrlist_parse(&env->to, body);
693 matched = true;
694 }
695 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
696 {
697 mutt_addrlist_parse(&env->from, body);
698 matched = true;
699 }
700#ifdef USE_AUTOCRYPT
701 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
702 {
703 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
704 if (c_autocrypt)
705 {
706 env->autocrypt = parse_autocrypt(env->autocrypt, body);
707 matched = true;
708 }
709 }
710 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
711 {
712 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
713 if (c_autocrypt)
714 {
716 matched = true;
717 }
718 }
719#endif
720 break;
721
722 case 'b':
723 if ((name_len == 3) && eqi2(name + 1, "cc"))
724 {
725 mutt_addrlist_parse(&env->bcc, body);
726 matched = true;
727 }
728 break;
729
730 case 'c':
731 if ((name_len == 2) && eqi1(name + 1, "c"))
732 {
733 mutt_addrlist_parse(&env->cc, body);
734 matched = true;
735 }
736 else
737 {
738 if ((name_len >= 12) && eqi8(name, "content-"))
739 {
740 if ((name_len == 12) && eqi4(name + 8, "type"))
741 {
742 if (e)
744 matched = true;
745 }
746 else if ((name_len == 16) && eqi8(name + 8, "language"))
747 {
748 if (e)
750 matched = true;
751 }
752 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
753 {
754 if (e)
756 matched = true;
757 }
758 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
759 {
760 if (e)
761 {
762 unsigned long len = 0;
763 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
764 }
765 matched = true;
766 }
767 else if ((name_len == 19) && eqi11(name + 8, "description"))
768 {
769 if (e)
770 {
773 }
774 matched = true;
775 }
776 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
777 {
778 if (e)
780 matched = true;
781 }
782 }
783 }
784 break;
785
786 case 'd':
787 if ((name_len != 4) || !eqi4(name, "date"))
788 break;
789
790 mutt_str_replace(&env->date, body);
791 if (e)
792 {
793 struct Tz tz = { 0 };
794 // the caller will check e->date_sent for -1
795 e->date_sent = mutt_date_parse_date(body, &tz);
796 if (e->date_sent > 0)
797 {
798 e->zhours = tz.zhours;
799 e->zminutes = tz.zminutes;
800 e->zoccident = tz.zoccident;
801 }
802 }
803 matched = true;
804 break;
805
806 case 'e':
807 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
808 {
809 const time_t expired = mutt_date_parse_date(body, NULL);
810 if ((expired != -1) && (expired < mutt_date_now()))
811 {
812 e->expired = true;
813 }
814 }
815 break;
816
817 case 'f':
818 if ((name_len == 4) && eqi4(name, "from"))
819 {
820 mutt_addrlist_parse(&env->from, body);
821 matched = true;
822 }
823 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
824 {
825 if (!env->followup_to)
826 {
829 }
830 matched = true;
831 }
832 break;
833
834 case 'i':
835 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
836 break;
837
839 char *body2 = mutt_str_dup(body); // Create a mutable copy
841 parse_references(&env->in_reply_to, body2);
842 FREE(&body2);
843 matched = true;
844 break;
845
846 case 'l':
847 if ((name_len == 5) && eqi4(name + 1, "ines"))
848 {
849 if (e)
850 {
851 unsigned int ui = 0; // we don't want a negative number of lines
852 mutt_str_atoui(body, &ui);
853 e->lines = ui;
854 }
855
856 matched = true;
857 }
858 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
859 {
860 /* RFC2369 */
861 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
862 {
863 char *mailto = rfc2369_first_mailto(body);
864 if (mailto)
865 {
866 FREE(&env->list_post);
867 env->list_post = mailto;
868 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
869 if (c_auto_subscribe)
871 }
872 }
873 matched = true;
874 }
875 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
876 {
877 /* RFC2369 */
878 char *mailto = rfc2369_first_mailto(body);
879 if (mailto)
880 {
881 FREE(&env->list_subscribe);
882 env->list_subscribe = mailto;
883 }
884 matched = true;
885 }
886 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
887 {
888 /* RFC2369 */
889 char *mailto = rfc2369_first_mailto(body);
890 if (mailto)
891 {
892 FREE(&env->list_unsubscribe);
893 env->list_unsubscribe = mailto;
894 }
895 matched = true;
896 }
897 break;
898
899 case 'm':
900 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
901 {
902 if (e)
903 e->mime = true;
904 matched = true;
905 }
906 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
907 {
908 /* We add a new "Message-ID:" when building a message */
909 FREE(&env->message_id);
910 env->message_id = mutt_extract_message_id(body, NULL);
911 matched = true;
912 }
913 else
914 {
915 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
916 {
917 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
918 {
919 /* override the Reply-To: field */
921 mutt_addrlist_parse(&env->reply_to, body);
922 matched = true;
923 }
924 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
925 {
927 matched = true;
928 }
929 }
930 }
931 break;
932
933 case 'n':
934 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
935 {
936 FREE(&env->newsgroups);
939 matched = true;
940 }
941 break;
942
943 case 'o':
944 /* field 'Organization:' saves only for pager! */
945 if ((name_len == 12) && eqi11(name + 1, "rganization"))
946 {
947 if (!env->organization && !mutt_istr_equal(body, "unknown"))
948 env->organization = mutt_str_dup(body);
949 }
950 break;
951
952 case 'r':
953 if ((name_len == 10) && eqi9(name + 1, "eferences"))
954 {
956 parse_references(&env->references, body);
957 matched = true;
958 }
959 else if ((name_len == 8) && eqi8(name, "reply-to"))
960 {
961 mutt_addrlist_parse(&env->reply_to, body);
962 matched = true;
963 }
964 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
965 {
966 mutt_addrlist_parse(&env->return_path, body);
967 matched = true;
968 }
969 else if ((name_len == 8) && eqi8(name, "received"))
970 {
971 if (e && (e->received == 0))
972 {
973 char *d = strrchr(body, ';');
974 if (d)
975 {
976 d = mutt_str_skip_email_wsp(d + 1);
977 // the caller will check e->received for -1
978 e->received = mutt_date_parse_date(d, NULL);
979 }
980 }
981 }
982 break;
983
984 case 's':
985 if ((name_len == 7) && eqi6(name + 1, "ubject"))
986 {
987 if (!env->subject)
988 mutt_env_set_subject(env, body);
989 matched = true;
990 }
991 else if ((name_len == 6) && eqi5(name + 1, "ender"))
992 {
993 mutt_addrlist_parse(&env->sender, body);
994 matched = true;
995 }
996 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
997 {
998 if (e)
999 {
1000 while (*body)
1001 {
1002 switch (*body)
1003 {
1004 case 'O':
1005 {
1006 e->old = true;
1007 break;
1008 }
1009 case 'R':
1010 e->read = true;
1011 break;
1012 case 'r':
1013 e->replied = true;
1014 break;
1015 }
1016 body++;
1017 }
1018 }
1019 matched = true;
1020 }
1021 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1022 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1023 {
1024 FREE(&env->supersedes);
1025 env->supersedes = mutt_str_dup(body);
1026 }
1027 break;
1028
1029 case 't':
1030 if ((name_len == 2) && eqi1(name + 1, "o"))
1031 {
1032 mutt_addrlist_parse(&env->to, body);
1033 matched = true;
1034 }
1035 break;
1036
1037 case 'x':
1038 if ((name_len == 8) && eqi8(name, "x-status"))
1039 {
1040 if (e)
1041 {
1042 while (*body)
1043 {
1044 switch (*body)
1045 {
1046 case 'A':
1047 e->replied = true;
1048 break;
1049 case 'D':
1050 e->deleted = true;
1051 break;
1052 case 'F':
1053 e->flagged = true;
1054 break;
1055 default:
1056 break;
1057 }
1058 body++;
1059 }
1060 }
1061 matched = true;
1062 }
1063 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1064 {
1065 FREE(&env->x_label);
1066 env->x_label = mutt_str_dup(body);
1067 matched = true;
1068 }
1069 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1070 {
1071 if (!env->x_comment_to)
1072 env->x_comment_to = mutt_str_dup(body);
1073 matched = true;
1074 }
1075 else if ((name_len == 4) && eqi4(name, "xref"))
1076 {
1077 if (!env->xref)
1078 env->xref = mutt_str_dup(body);
1079 matched = true;
1080 }
1081 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1082 {
1084 matched = true;
1085 }
1086 break;
1087
1088 default:
1089 break;
1090 }
1091
1092 /* Keep track of the user-defined headers */
1093 if (!matched && user_hdrs)
1094 {
1095 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1096 char *dup = NULL;
1097 mutt_str_asprintf(&dup, "%s: %s", name, body);
1098
1099 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1100 {
1101 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1102 if (do_2047)
1103 {
1104 rfc2047_decode(&np->data);
1105 }
1106 }
1107 else
1108 {
1109 FREE(&dup);
1110 }
1111 }
1112
1113 return matched;
1114}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1460
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition: atoi.c:244
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:218
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition: parse.c:109
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition: parse.c:572
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition: parse.c:323
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:356
static char * rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from a RFC2369 list.
Definition: parse.c:636
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:401
#define CONTENT_TOO_BIG
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:69
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:205
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:157
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:163
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:122
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:169
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:149
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:187
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:181
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:104
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:143
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:175
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:193
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:76
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:88
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:65
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
#define MIN(a, b)
Definition: memory.h:32
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:716
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:565
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:425
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:551
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 mailto URL, or nothing.
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 mailto URL, or nothing.
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
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
List of recognised Timezones.
Definition: date.h:50
unsigned char zminutes
Minutes away from UTC.
Definition: date.h:53
bool zoccident
True if west of UTC, False if East.
Definition: date.h:54
unsigned char zhours
Hours away from UTC.
Definition: date.h:52
+ 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 1837 of file parse.c.

1838{
1839 int counter = 0;
1840
1841 return rfc822_parse_message(fp, b, &counter);
1842}
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition: parse.c:1695
+ 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 1205 of file parse.c.

1206{
1207 if (!fp)
1208 return NULL;
1209
1210 struct Envelope *env = mutt_env_new();
1211 char *p = NULL;
1212 LOFF_T loc = e ? e->offset : ftello(fp);
1213 if (loc < 0)
1214 {
1215 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1216 loc = 0;
1217 }
1218
1219 struct Buffer *line = buf_pool_get();
1220
1221 if (e)
1222 {
1223 if (!e->body)
1224 {
1225 e->body = mutt_body_new();
1226
1227 /* set the defaults from RFC1521 */
1228 e->body->type = TYPE_TEXT;
1229 e->body->subtype = mutt_str_dup("plain");
1230 e->body->encoding = ENC_7BIT;
1231 e->body->length = -1;
1232
1233 /* RFC2183 says this is arbitrary */
1235 }
1236 }
1237
1238 while (true)
1239 {
1240 LOFF_T line_start_loc = loc;
1241 size_t len = mutt_rfc822_read_line(fp, line);
1242 if (buf_is_empty(line))
1243 {
1244 break;
1245 }
1246 loc += len;
1247 const char *lines = buf_string(line);
1248 p = strpbrk(lines, ": \t");
1249 if (!p || (*p != ':'))
1250 {
1251 char return_path[1024] = { 0 };
1252 time_t t = 0;
1253
1254 /* some bogus MTAs will quote the original "From " line */
1255 if (mutt_str_startswith(lines, ">From "))
1256 {
1257 continue; /* just ignore */
1258 }
1259 else if (is_from(lines, return_path, sizeof(return_path), &t))
1260 {
1261 /* MH sometimes has the From_ line in the middle of the header! */
1262 if (e && (e->received == 0))
1263 e->received = t - mutt_date_local_tz(t);
1264 continue;
1265 }
1266
1267 /* We need to seek back to the start of the body. Note that we
1268 * keep track of loc ourselves, since calling ftello() incurs
1269 * a syscall, which can be expensive to do for every single line */
1270 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1271 break; /* end of header */
1272 }
1273 size_t name_len = p - lines;
1274
1275 char buf[1024] = { 0 };
1276 if (mutt_replacelist_match(&SpamList, buf, sizeof(buf), lines))
1277 {
1278 if (!mutt_regexlist_match(&NoSpamList, lines))
1279 {
1280 /* if spam tag already exists, figure out how to amend it */
1281 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1282 {
1283 /* If `$spam_separator` defined, append with separator */
1284 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1285 if (c_spam_separator)
1286 {
1287 buf_addstr(&env->spam, c_spam_separator);
1288 buf_addstr(&env->spam, buf);
1289 }
1290 else /* overwrite */
1291 {
1292 buf_reset(&env->spam);
1293 buf_addstr(&env->spam, buf);
1294 }
1295 }
1296 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1297 {
1298 /* spam tag is new, and match expr is non-empty; copy */
1299 buf_addstr(&env->spam, buf);
1300 }
1301 else if (buf_is_empty(&env->spam))
1302 {
1303 /* match expr is empty; plug in null string if no existing tag */
1304 buf_addstr(&env->spam, "");
1305 }
1306
1307 if (!buf_is_empty(&env->spam))
1308 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1309 }
1310 }
1311
1312 *p = '\0';
1313 p = mutt_str_skip_email_wsp(p + 1);
1314 if (*p == '\0')
1315 continue; /* skip empty header fields */
1316
1317 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1318 }
1319
1320 buf_pool_release(&line);
1321
1322 if (e)
1323 {
1324 e->body->hdr_offset = e->offset;
1325 e->body->offset = ftello(fp);
1326
1328
1329 if (e->received < 0)
1330 {
1331 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1332 e->received = 0;
1333 }
1334
1335 /* check for missing or invalid date */
1336 if (e->date_sent <= 0)
1337 {
1338 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1339 e->date_sent = e->received;
1340 }
1341
1342#ifdef USE_AUTOCRYPT
1343 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1344 if (c_autocrypt)
1345 {
1347 /* No sense in taking up memory after the header is processed */
1349 }
1350#endif
1351 }
1352
1353 return env;
1354}
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition: autocrypt.c:256
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
struct ReplaceList SpamList
List of regexes to match subscribed mailing lists.
Definition: globals.c:46
struct RegexList NoSpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:44
void mutt_autocrypthdr_free(struct AutocryptHeader **ptr)
Free an AutocryptHeader.
Definition: envelope.c:104
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:778
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
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:219
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition: regex.c:478
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
char * data
Pointer to data.
Definition: buffer.h:37
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 1125 of file parse.c.

1126{
1127 if (!fp || !buf)
1128 return 0;
1129
1130 size_t read = 0;
1131 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1132
1133 buf_reset(buf);
1134 while (true)
1135 {
1136 if (!fgets(line, sizeof(line), fp))
1137 {
1138 return 0;
1139 }
1140
1141 const size_t linelen = mutt_str_len(line);
1142 if (linelen == 0)
1143 {
1144 break;
1145 }
1146
1147 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1148 {
1149 read = linelen;
1150 break;
1151 }
1152
1153 read += linelen;
1154
1155 size_t off = linelen - 1;
1156 if (line[off] == '\n')
1157 {
1158 /* We did get a full line: remove trailing space */
1159 do
1160 {
1161 line[off] = '\0';
1162 } while (off && mutt_str_is_email_wsp(line[--off]));
1163
1164 /* check to see if the next line is a continuation line */
1165 int ch = fgetc(fp);
1166 if ((ch != ' ') && (ch != '\t'))
1167 {
1168 /* next line is a separate header field or EOH */
1169 ungetc(ch, fp);
1170 buf_addstr(buf, line);
1171 break;
1172 }
1173 read++;
1174
1175 /* eat tabs and spaces from the beginning of the continuation line */
1176 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1177 {
1178 read++;
1179 }
1180
1181 ungetc(ch, fp);
1182 line[off + 1] = ' '; /* string is still terminated because we removed
1183 at least one whitespace char above */
1184 }
1185
1186 buf_addstr(buf, line);
1187 }
1188
1189 return read;
1190}
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string2.h:104
+ 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: