NeoMutt
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 *ct)
 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 *parent)
 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.
 

Detailed Description

Miscellaneous email parsing routines.

Authors
  • Richard Russon

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

69{
70 if (!mailto)
71 return;
72
75
77 return;
78
80
81 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
82
83 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
84 {
85 const char *mailbox = buf_string(TAILQ_FIRST(&lpenv->to)->mailbox);
86 if (mailbox && !mutt_regexlist_match(&SubscribedLists, mailbox) &&
89 {
90 /* mutt_regexlist_add() detects duplicates, so it is safe to
91 * try to add here without any checks. */
92 mutt_regexlist_add(&MailLists, mailbox, REG_ICASE, NULL);
93 mutt_regexlist_add(&SubscribedLists, mailbox, REG_ICASE, NULL);
94 }
95 }
96
97 mutt_env_free(&lpenv);
98}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
struct RegexList SubscribedLists
List of header patterns to unignore (see)
Definition: globals.c:47
struct HashTable * AutoSubscribeCache
< Hash Table: "mailto:" -> AutoSubscribeCache
Definition: globals.c:35
struct RegexList UnSubscribedLists
Definition: globals.c:53
struct RegexList UnMailLists
List of regexes to exclude false matches in SubscribedLists.
Definition: globals.c:51
struct RegexList MailLists
List of permitted fields in a mailto: url.
Definition: globals.c:39
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1713
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:43
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:112
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:111
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:136
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition: regex.c:196
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_EMPTY(head)
Definition: queue.h:721
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 398 of file parse.c.

399{
400 if (mutt_istr_startswith(c, "7bit"))
401 return ENC_7BIT;
402 if (mutt_istr_startswith(c, "8bit"))
403 return ENC_8BIT;
404 if (mutt_istr_startswith(c, "binary"))
405 return ENC_BINARY;
406 if (mutt_istr_startswith(c, "quoted-printable"))
408 if (mutt_istr_startswith(c, "base64"))
409 return ENC_BASE64;
410 if (mutt_istr_startswith(c, "x-uuencode"))
411 return ENC_UUENCODED;
412#ifdef SUN_ATTACHMENT
413 if (mutt_istr_startswith(c, "uuencode"))
414 return ENC_UUENCODED;
415#endif
416 return ENC_OTHER;
417}
@ 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:240
+ 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 325 of file parse.c.

326{
327 if (mutt_istr_equal("text", s))
328 return TYPE_TEXT;
329 if (mutt_istr_equal("multipart", s))
330 return TYPE_MULTIPART;
331#ifdef SUN_ATTACHMENT
332 if (mutt_istr_equal("x-sun-attachment", s))
333 return TYPE_MULTIPART;
334#endif
335 if (mutt_istr_equal("application", s))
336 return TYPE_APPLICATION;
337 if (mutt_istr_equal("message", s))
338 return TYPE_MESSAGE;
339 if (mutt_istr_equal("image", s))
340 return TYPE_IMAGE;
341 if (mutt_istr_equal("audio", s))
342 return TYPE_AUDIO;
343 if (mutt_istr_equal("video", s))
344 return TYPE_VIDEO;
345 if (mutt_istr_equal("model", s))
346 return TYPE_MODEL;
347 if (mutt_istr_equal("*", s))
348 return TYPE_ANY;
349 if (mutt_istr_equal(".*", s))
350 return TYPE_ANY;
351
352 return TYPE_OTHER;
353}
@ 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:810
+ 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 362 of file parse.c.

363{
364 if (!s || (*s == '\0'))
365 return NULL;
366
367 char *decoded = mutt_str_dup(s);
368 rfc2047_decode(&decoded);
369
370 char *res = NULL;
371
372 for (const char *p = decoded, *beg = NULL; *p; p++)
373 {
374 if (*p == '<')
375 {
376 beg = p;
377 continue;
378 }
379
380 if (beg && (*p == '>'))
381 {
382 if (len)
383 *len = p - decoded + 1;
384 res = mutt_strn_dup(beg, (p + 1) - beg);
385 break;
386 }
387 }
388
389 FREE(&decoded);
390 return res;
391}
#define FREE(x)
Definition: memory.h:45
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:452
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:659
+ 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 1482 of file parse.c.

1483{
1484 if (type != TYPE_MESSAGE)
1485 return false;
1486
1487 subtype = NONULL(subtype);
1488 return (mutt_istr_equal(subtype, "rfc822") ||
1489 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1490}
#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 315 of file parse.c.

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

Parse a content type.

Parameters
sString to parse
ctBody to save the result

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

Definition at line 426 of file parse.c.

427{
428 if (!s || !ct)
429 return;
430
431 FREE(&ct->subtype);
433
434 /* First extract any existing parameters */
435 char *pc = strchr(s, ';');
436 if (pc)
437 {
438 *pc++ = 0;
439 while (*pc && isspace(*pc))
440 pc++;
441 parse_parameters(&ct->parameter, pc, false);
442
443 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
444 * but if a filename has already been set in the content-disposition,
445 * let that take precedence, and don't set it here */
446 pc = mutt_param_get(&ct->parameter, "name");
447 if (pc && !ct->filename)
448 ct->filename = mutt_str_dup(pc);
449
450#ifdef SUN_ATTACHMENT
451 /* this is deep and utter perversion */
452 pc = mutt_param_get(&ct->parameter, "conversions");
453 if (pc)
455#endif
456 }
457
458 /* Now get the subtype */
459 char *subtype = strchr(s, '/');
460 if (subtype)
461 {
462 *subtype++ = '\0';
463 for (pc = subtype; *pc && !isspace(*pc) && (*pc != ';'); pc++)
464 ; // do nothing
465
466 *pc = '\0';
467 mutt_str_replace(&ct->subtype, subtype);
468 }
469
470 /* Finally, get the major type */
471 ct->type = mutt_check_mime_type(s);
472
473#ifdef SUN_ATTACHMENT
474 if (mutt_istr_equal("x-sun-attachment", s))
475 mutt_str_replace(&ct->subtype, "x-sun-attachment");
476#endif
477
478 if (ct->type == TYPE_OTHER)
479 {
480 mutt_str_replace(&ct->xtype, s);
481 }
482
483 if (!ct->subtype)
484 {
485 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
486 * field, so we can attempt to convert the type to Body here. */
487 if (ct->type == TYPE_TEXT)
488 {
489 ct->subtype = mutt_str_dup("plain");
490 }
491 else if (ct->type == TYPE_AUDIO)
492 {
493 ct->subtype = mutt_str_dup("basic");
494 }
495 else if (ct->type == TYPE_MESSAGE)
496 {
497 ct->subtype = mutt_str_dup("rfc822");
498 }
499 else if (ct->type == TYPE_OTHER)
500 {
501 char buf[128] = { 0 };
502
504 snprintf(buf, sizeof(buf), "x-%s", s);
505 ct->subtype = mutt_str_dup(buf);
506 }
507 else
508 {
509 ct->subtype = mutt_str_dup("x-unknown");
510 }
511 }
512
513 /* Default character set for text types. */
514 if (ct->type == TYPE_TEXT)
515 {
516 pc = mutt_param_get(&ct->parameter, "charset");
517 if (pc)
518 {
519 /* Microsoft Outlook seems to think it is necessary to repeat
520 * charset=, strip it off not to confuse ourselves */
521 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
522 mutt_param_set(&ct->parameter, "charset", pc + (sizeof("charset=") - 1));
523 }
524 else
525 {
526 mutt_param_set(&ct->parameter, "charset",
528 }
529 }
530}
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
Definition: config_cache.c:100
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:325
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition: parse.c:398
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition: parse.c:111
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition: charset.c:460
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:525
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
char * xtype
content-type if x-unknown
Definition: body.h:61
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
char * subtype
content-type subtype
Definition: body.h:60
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:58
+ 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 1713 of file parse.c.

1714{
1715 if (!env || !src)
1716 return false;
1717
1718 struct Url *url = url_parse(src);
1719 if (!url)
1720 return false;
1721
1722 if (url->host)
1723 {
1724 /* this is not a path-only URL */
1725 url_free(&url);
1726 return false;
1727 }
1728
1729 mutt_addrlist_parse(&env->to, url->path);
1730
1731 struct UrlQuery *np;
1732 STAILQ_FOREACH(np, &url->query_strings, entries)
1733 {
1734 const char *tag = np->name;
1735 char *value = np->value;
1736 /* Determine if this header field is on the allowed list. Since NeoMutt
1737 * interprets some header fields specially (such as
1738 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1739 * only safe fields are allowed.
1740 *
1741 * RFC2368, "4. Unsafe headers"
1742 * The user agent interpreting a mailto URL SHOULD choose not to create
1743 * a message if any of the headers are considered dangerous; it may also
1744 * choose to create a message with only a subset of the headers given in
1745 * the URL. */
1746 if (mutt_list_match(tag, &MailToAllow))
1747 {
1748 if (mutt_istr_equal(tag, "body"))
1749 {
1750 if (body)
1751 mutt_str_replace(body, value);
1752 }
1753 else
1754 {
1755 char *scratch = NULL;
1756 size_t taglen = mutt_str_len(tag);
1757
1758 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1759 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1760 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1761 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1762 FREE(&scratch);
1763 }
1764 }
1765 }
1766
1767 /* RFC2047 decode after the RFC822 parsing */
1769
1770 url_free(&url);
1771 return true;
1772}
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:478
struct ListHead MailToAllow
List of regexes to identify non-spam emails.
Definition: globals.c:41
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:645
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:680
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
#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:828
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:238
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
+ 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 1810 of file parse.c.

1811{
1812 int counter = 0;
1813
1814 return parse_multipart(fp, boundary, end_off, digest, &counter);
1815}
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:1571
+ 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 1779 of file parse.c.

1780{
1781 int counter = 0;
1782
1783 parse_part(fp, b, &counter);
1784}
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition: parse.c:1498
+ 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 1344 of file parse.c.

1345{
1346 if (!fp)
1347 return NULL;
1348
1349 struct Body *p = mutt_body_new();
1350 struct Envelope *env = mutt_env_new();
1351 char *c = NULL;
1352 struct Buffer *buf = buf_pool_get();
1353 bool matched = false;
1354
1355 p->hdr_offset = ftello(fp);
1356
1357 p->encoding = ENC_7BIT; /* default from RFC1521 */
1358 p->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1360
1361 while (mutt_rfc822_read_line(fp, buf) != 0)
1362 {
1363 const char *line = buf_string(buf);
1364 /* Find the value of the current header */
1365 c = strchr(line, ':');
1366 if (c)
1367 {
1368 *c = '\0';
1369 c = mutt_str_skip_email_wsp(c + 1);
1370 if (*c == '\0')
1371 {
1372 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1373 continue;
1374 }
1375 }
1376 else
1377 {
1378 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1379 break;
1380 }
1381
1382 size_t plen = mutt_istr_startswith(line, "content-");
1383 if (plen != 0)
1384 {
1385 if (mutt_istr_equal("type", line + plen))
1386 {
1388 }
1389 else if (mutt_istr_equal("language", line + plen))
1390 {
1392 }
1393 else if (mutt_istr_equal("transfer-encoding", line + plen))
1394 {
1396 }
1397 else if (mutt_istr_equal("disposition", line + plen))
1398 {
1400 }
1401 else if (mutt_istr_equal("description", line + plen))
1402 {
1405 }
1406 else if (mutt_istr_equal("id", line + plen))
1407 {
1408 // strip <angle braces> from Content-ID: header
1409 char *id = c;
1410 int cid_len = mutt_str_len(c);
1411 if (cid_len > 2)
1412 {
1413 if (id[0] == '<')
1414 {
1415 id++;
1416 cid_len--;
1417 }
1418 if (id[cid_len - 1] == '>')
1419 id[cid_len - 1] = '\0';
1420 }
1421 mutt_param_set(&p->parameter, "content-id", id);
1422 }
1423 }
1424#ifdef SUN_ATTACHMENT
1425 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1426 {
1427 if (mutt_istr_equal("data-type", line + plen))
1428 {
1430 }
1431 else if (mutt_istr_equal("encoding-info", line + plen))
1432 {
1434 }
1435 else if (mutt_istr_equal("content-lines", line + plen))
1436 {
1437 mutt_param_set(&p->parameter, "content-lines", c);
1438 }
1439 else if (mutt_istr_equal("data-description", line + plen))
1440 {
1443 }
1444 }
1445#endif
1446 else
1447 {
1448 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1449 {
1450 matched = true;
1451 }
1452 }
1453 }
1454 p->offset = ftello(fp); /* Mark the start of the real data */
1455 if ((p->type == TYPE_TEXT) && !p->subtype)
1456 p->subtype = mutt_str_dup("plain");
1457 else if ((p->type == TYPE_MESSAGE) && !p->subtype)
1458 p->subtype = mutt_str_dup("rfc822");
1459
1460 buf_pool_release(&buf);
1461
1462 if (matched)
1463 {
1464 p->mime_headers = env;
1466 }
1467 else
1468 {
1469 mutt_env_free(&env);
1470 }
1471
1472 return p;
1473}
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition: parse.c:1090
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:426
static void parse_content_language(const char *s, struct Body *ct)
Read the content's language.
Definition: parse.c:299
static void parse_content_disposition(const char *s, struct Body *ct)
Parse a content disposition.
Definition: parse.c:250
#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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
The body of an email.
Definition: body.h:36
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
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:80
String manipulation buffer.
Definition: buffer.h:34
+ 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 645 of file parse.c.

648{
649 if (!env || !name)
650 return 0;
651
652 bool matched = false;
653
654 switch (name[0] | 0x20)
655 {
656 case 'a':
657 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
658 {
659 mutt_addrlist_parse(&env->to, body);
660 matched = true;
661 }
662 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
663 {
664 mutt_addrlist_parse(&env->from, body);
665 matched = true;
666 }
667#ifdef USE_AUTOCRYPT
668 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
669 {
670 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
671 if (c_autocrypt)
672 {
673 env->autocrypt = parse_autocrypt(env->autocrypt, body);
674 matched = true;
675 }
676 }
677 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
678 {
679 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
680 if (c_autocrypt)
681 {
683 matched = true;
684 }
685 }
686#endif
687 break;
688
689 case 'b':
690 if ((name_len == 3) && eqi2(name + 1, "cc"))
691 {
692 mutt_addrlist_parse(&env->bcc, body);
693 matched = true;
694 }
695 break;
696
697 case 'c':
698 if ((name_len == 2) && eqi1(name + 1, "c"))
699 {
700 mutt_addrlist_parse(&env->cc, body);
701 matched = true;
702 }
703 else
704 {
705 if ((name_len >= 12) && eqi8(name, "content-"))
706 {
707 if ((name_len == 12) && eqi4(name + 8, "type"))
708 {
709 if (e)
711 matched = true;
712 }
713 else if ((name_len == 16) && eqi8(name + 8, "language"))
714 {
715 if (e)
717 matched = true;
718 }
719 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
720 {
721 if (e)
723 matched = true;
724 }
725 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
726 {
727 if (e)
728 {
729 unsigned long len = 0;
730 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
731 }
732 matched = true;
733 }
734 else if ((name_len == 19) && eqi11(name + 8, "description"))
735 {
736 if (e)
737 {
740 }
741 matched = true;
742 }
743 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
744 {
745 if (e)
747 matched = true;
748 }
749 }
750 }
751 break;
752
753 case 'd':
754 if ((name_len != 4) || !eqi4(name, "date"))
755 break;
756
757 mutt_str_replace(&env->date, body);
758 if (e)
759 {
760 struct Tz tz = { 0 };
761 e->date_sent = mutt_date_parse_date(body, &tz);
762 if (e->date_sent > 0)
763 {
764 e->zhours = tz.zhours;
765 e->zminutes = tz.zminutes;
766 e->zoccident = tz.zoccident;
767 }
768 }
769 matched = true;
770 break;
771
772 case 'e':
773 if ((name_len == 7) && eqi6(name + 1, "xpires") && e &&
774 (mutt_date_parse_date(body, NULL) < mutt_date_now()))
775 {
776 e->expired = true;
777 }
778 break;
779
780 case 'f':
781 if ((name_len == 4) && eqi4(name, "from"))
782 {
783 mutt_addrlist_parse(&env->from, body);
784 matched = true;
785 }
786#ifdef USE_NNTP
787 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
788 {
789 if (!env->followup_to)
790 {
793 }
794 matched = true;
795 }
796#endif
797 break;
798
799 case 'i':
800 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
801 break;
802
804 parse_references(&env->in_reply_to, body);
805 matched = true;
806 break;
807
808 case 'l':
809 if ((name_len == 5) && eqi4(name + 1, "ines"))
810 {
811 if (e)
812 {
813 unsigned int ui = 0; // we don't want a negative number of lines
814 mutt_str_atoui(body, &ui);
815 e->lines = ui;
816 }
817
818 matched = true;
819 }
820 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
821 {
822 /* RFC2369 */
823 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
824 {
825 char *mailto = rfc2369_first_mailto(body);
826 if (mailto)
827 {
828 FREE(&env->list_post);
829 env->list_post = mailto;
830 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
831 if (c_auto_subscribe)
833 }
834 }
835 matched = true;
836 }
837 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
838 {
839 /* RFC2369 */
840 char *mailto = rfc2369_first_mailto(body);
841 if (mailto)
842 {
843 FREE(&env->list_subscribe);
844 env->list_subscribe = mailto;
845 }
846 matched = true;
847 }
848 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
849 {
850 /* RFC2369 */
851 char *mailto = rfc2369_first_mailto(body);
852 if (mailto)
853 {
854 FREE(&env->list_unsubscribe);
855 env->list_unsubscribe = mailto;
856 }
857 matched = true;
858 }
859 break;
860
861 case 'm':
862 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
863 {
864 if (e)
865 e->mime = true;
866 matched = true;
867 }
868 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
869 {
870 /* We add a new "Message-ID:" when building a message */
871 FREE(&env->message_id);
872 env->message_id = mutt_extract_message_id(body, NULL);
873 matched = true;
874 }
875 else
876 {
877 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
878 {
879 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
880 {
881 /* override the Reply-To: field */
883 mutt_addrlist_parse(&env->reply_to, body);
884 matched = true;
885 }
886 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
887 {
889 matched = true;
890 }
891 }
892 }
893 break;
894
895#ifdef USE_NNTP
896 case 'n':
897 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
898 {
899 FREE(&env->newsgroups);
902 matched = true;
903 }
904 break;
905#endif
906
907 case 'o':
908 /* field 'Organization:' saves only for pager! */
909 if ((name_len == 12) && eqi11(name + 1, "rganization"))
910 {
911 if (!env->organization && !mutt_istr_equal(body, "unknown"))
912 env->organization = mutt_str_dup(body);
913 }
914 break;
915
916 case 'r':
917 if ((name_len == 10) && eqi9(name + 1, "eferences"))
918 {
920 parse_references(&env->references, body);
921 matched = true;
922 }
923 else if ((name_len == 8) && eqi8(name, "reply-to"))
924 {
925 mutt_addrlist_parse(&env->reply_to, body);
926 matched = true;
927 }
928 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
929 {
930 mutt_addrlist_parse(&env->return_path, body);
931 matched = true;
932 }
933 else if ((name_len == 8) && eqi8(name, "received"))
934 {
935 if (e && (e->received == 0))
936 {
937 char *d = strrchr(body, ';');
938 if (d)
939 {
940 d = mutt_str_skip_email_wsp(d + 1);
941 e->received = mutt_date_parse_date(d, NULL);
942 }
943 }
944 }
945 break;
946
947 case 's':
948 if ((name_len == 7) && eqi6(name + 1, "ubject"))
949 {
950 if (!env->subject)
951 env->subject = mutt_str_dup(body);
952 matched = true;
953 }
954 else if ((name_len == 6) && eqi5(name + 1, "ender"))
955 {
956 mutt_addrlist_parse(&env->sender, body);
957 matched = true;
958 }
959 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
960 {
961 if (e)
962 {
963 while (*body)
964 {
965 switch (*body)
966 {
967 case 'O':
968 {
969 e->old = true;
970 break;
971 }
972 case 'R':
973 e->read = true;
974 break;
975 case 'r':
976 e->replied = true;
977 break;
978 }
979 body++;
980 }
981 }
982 matched = true;
983 }
984 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
985 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
986 {
987 FREE(&env->supersedes);
988 env->supersedes = mutt_str_dup(body);
989 }
990 break;
991
992 case 't':
993 if ((name_len == 2) && eqi1(name + 1, "o"))
994 {
995 mutt_addrlist_parse(&env->to, body);
996 matched = true;
997 }
998 break;
999
1000 case 'x':
1001 if ((name_len == 8) && eqi8(name, "x-status"))
1002 {
1003 if (e)
1004 {
1005 while (*body)
1006 {
1007 switch (*body)
1008 {
1009 case 'A':
1010 e->replied = true;
1011 break;
1012 case 'D':
1013 e->deleted = true;
1014 break;
1015 case 'F':
1016 e->flagged = true;
1017 break;
1018 default:
1019 break;
1020 }
1021 body++;
1022 }
1023 }
1024 matched = true;
1025 }
1026 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1027 {
1028 FREE(&env->x_label);
1029 env->x_label = mutt_str_dup(body);
1030 matched = true;
1031 }
1032#ifdef USE_NNTP
1033 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1034 {
1035 if (!env->x_comment_to)
1036 env->x_comment_to = mutt_str_dup(body);
1037 matched = true;
1038 }
1039 else if ((name_len == 4) && eqi4(name, "xref"))
1040 {
1041 if (!env->xref)
1042 env->xref = mutt_str_dup(body);
1043 matched = true;
1044 }
1045#endif
1046 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1047 {
1049 matched = true;
1050 }
1051 break;
1052
1053 default:
1054 break;
1055 }
1056
1057 /* Keep track of the user-defined headers */
1058 if (!matched && user_hdrs)
1059 {
1060 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1061 char *dup = NULL;
1062 mutt_str_asprintf(&dup, "%s: %s", name, body);
1063
1064 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1065 {
1066 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1067 if (do_2047)
1068 {
1069 rfc2047_decode(&np->data);
1070 }
1071 }
1072 else
1073 {
1074 FREE(&dup);
1075 }
1076 }
1077
1078 return matched;
1079}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1461
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition: atoi.c:239
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:213
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition: parse.c:68
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition: parse.c:539
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition: parse.c:282
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:315
static char * rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from a RFC2369 list.
Definition: parse.c:603
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:362
#define CONTENT_TOO_BIG
Definition: parse.c:57
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:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
#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:446
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:686
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:637
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:497
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:623
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
bool read
Email is read.
Definition: email.h:48
unsigned int zminutes
Minutes away from UTC.
Definition: email.h:55
bool mime
Has a MIME-Version header?
Definition: email.h:46
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
bool old
Email is seen, but unread.
Definition: email.h:47
bool zoccident
True, if west of UTC, False if east.
Definition: email.h:56
bool flagged
Marked important?
Definition: email.h:45
unsigned int zhours
Hours away from UTC.
Definition: email.h:54
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
bool replied
Email has been replied to.
Definition: email.h:49
bool expired
Already expired?
Definition: email.h:44
bool deleted
Email is deleted.
Definition: email.h:76
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
struct ListHead userhdrs
user defined headers
Definition: envelope.h:87
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 * followup_to
List of 'followup-to' fields.
Definition: envelope.h:81
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:82
struct AddressList x_original_to
Email's 'X-Orig-to'.
Definition: envelope.h:66
struct AutocryptHeader * autocrypt_gossip
Autocrypt Gossip header.
Definition: envelope.h:90
char * newsgroups
List of newsgroups.
Definition: envelope.h:79
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:85
struct AutocryptHeader * autocrypt
Autocrypt header.
Definition: envelope.h:89
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:86
char * subject
Email's subject.
Definition: envelope.h:70
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:62
char * xref
List of cross-references.
Definition: envelope.h:80
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:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
List of recognised Timezones.
Definition: date.h:47
unsigned char zminutes
Minutes away from UTC.
Definition: date.h:50
bool zoccident
True if west of UTC, False if East.
Definition: date.h:51
unsigned char zhours
Hours away from UTC.
Definition: date.h:49
+ 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 parent 
)

Parse a Message/RFC822 body.

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

Definition at line 1794 of file parse.c.

1795{
1796 int counter = 0;
1797
1798 return rfc822_parse_message(fp, parent, &counter);
1799}
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition: parse.c:1682
+ 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 1170 of file parse.c.

1171{
1172 if (!fp)
1173 return NULL;
1174
1175 struct Envelope *env = mutt_env_new();
1176 char *p = NULL;
1177 LOFF_T loc = e ? e->offset : ftello(fp);
1178 if (loc < 0)
1179 {
1180 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1181 loc = 0;
1182 }
1183
1184 struct Buffer *line = buf_pool_get();
1185
1186 if (e)
1187 {
1188 if (!e->body)
1189 {
1190 e->body = mutt_body_new();
1191
1192 /* set the defaults from RFC1521 */
1193 e->body->type = TYPE_TEXT;
1194 e->body->subtype = mutt_str_dup("plain");
1195 e->body->encoding = ENC_7BIT;
1196 e->body->length = -1;
1197
1198 /* RFC2183 says this is arbitrary */
1200 }
1201 }
1202
1203 while (true)
1204 {
1205 LOFF_T line_start_loc = loc;
1206 size_t len = mutt_rfc822_read_line(fp, line);
1207 if (buf_len(line) == 0)
1208 {
1209 break;
1210 }
1211 loc += len;
1212 const char *lines = buf_string(line);
1213 p = strpbrk(lines, ": \t");
1214 if (!p || (*p != ':'))
1215 {
1216 char return_path[1024] = { 0 };
1217 time_t t = 0;
1218
1219 /* some bogus MTAs will quote the original "From " line */
1220 if (mutt_str_startswith(lines, ">From "))
1221 {
1222 continue; /* just ignore */
1223 }
1224 else if (is_from(lines, return_path, sizeof(return_path), &t))
1225 {
1226 /* MH sometimes has the From_ line in the middle of the header! */
1227 if (e && (e->received == 0))
1228 e->received = t - mutt_date_local_tz(t);
1229 continue;
1230 }
1231
1232 /* We need to seek back to the start of the body. Note that we
1233 * keep track of loc ourselves, since calling ftello() incurs
1234 * a syscall, which can be expensive to do for every single line */
1235 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1236 break; /* end of header */
1237 }
1238 size_t name_len = p - lines;
1239
1240 char buf[1024] = { 0 };
1241 if (mutt_replacelist_match(&SpamList, buf, sizeof(buf), lines))
1242 {
1243 if (!mutt_regexlist_match(&NoSpamList, lines))
1244 {
1245 /* if spam tag already exists, figure out how to amend it */
1246 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1247 {
1248 /* If `$spam_separator` defined, append with separator */
1249 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1250 if (c_spam_separator)
1251 {
1252 buf_addstr(&env->spam, c_spam_separator);
1253 buf_addstr(&env->spam, buf);
1254 }
1255 else /* overwrite */
1256 {
1257 buf_reset(&env->spam);
1258 buf_addstr(&env->spam, buf);
1259 }
1260 }
1261 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1262 {
1263 /* spam tag is new, and match expr is non-empty; copy */
1264 buf_addstr(&env->spam, buf);
1265 }
1266 else if (buf_is_empty(&env->spam))
1267 {
1268 /* match expr is empty; plug in null string if no existing tag */
1269 buf_addstr(&env->spam, "");
1270 }
1271
1272 if (!buf_is_empty(&env->spam))
1273 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1274 }
1275 }
1276
1277 *p = '\0';
1278 p = mutt_str_skip_email_wsp(p + 1);
1279 if (*p == '\0')
1280 continue; /* skip empty header fields */
1281
1282 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1283 }
1284
1285 buf_pool_release(&line);
1286
1287 if (e)
1288 {
1289 e->body->hdr_offset = e->offset;
1290 e->body->offset = ftello(fp);
1291
1293
1294 if (env->subject)
1295 {
1296 regmatch_t pmatch[1];
1297
1298 const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex");
1299 if (mutt_regex_capture(c_reply_regex, env->subject, 1, pmatch))
1300 {
1301 env->real_subj = env->subject + pmatch[0].rm_eo;
1302 if (env->real_subj[0] == '\0')
1303 env->real_subj = NULL;
1304 }
1305 else
1306 {
1307 env->real_subj = env->subject;
1308 }
1309 }
1310
1311 if (e->received < 0)
1312 {
1313 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1314 e->received = 0;
1315 }
1316
1317 /* check for missing or invalid date */
1318 if (e->date_sent <= 0)
1319 {
1320 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1321 e->date_sent = e->received;
1322 }
1323
1324#ifdef USE_AUTOCRYPT
1325 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1326 if (c_autocrypt)
1327 {
1329 /* No sense in taking up memory after the header is processed */
1331 }
1332#endif
1333 }
1334
1335 return env;
1336}
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition: autocrypt.c:256
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
struct ReplaceList SpamList
List of regexes to match subscribed mailing lists.
Definition: globals.c:45
struct RegexList NoSpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:43
void mutt_autocrypthdr_free(struct AutocryptHeader **ptr)
Free an AutocryptHeader.
Definition: envelope.c:75
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:733
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
@ 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:209
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:619
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition: regex.c:500
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
char * data
Pointer to data.
Definition: buffer.h:35
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
struct Buffer spam
Spam header.
Definition: envelope.h:84
char * real_subj
Offset of the real subject.
Definition: envelope.h:71
Cached regular expression.
Definition: regex3.h:89
+ 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 1090 of file parse.c.

1091{
1092 if (!fp || !buf)
1093 return 0;
1094
1095 size_t read = 0;
1096 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1097
1098 buf_reset(buf);
1099 while (true)
1100 {
1101 if (!fgets(line, sizeof(line), fp))
1102 {
1103 return 0;
1104 }
1105
1106 const size_t linelen = mutt_str_len(line);
1107 if (linelen == 0)
1108 {
1109 break;
1110 }
1111
1112 if (isspace(line[0]) && buf_is_empty(buf))
1113 {
1114 read = linelen;
1115 break;
1116 }
1117
1118 read += linelen;
1119
1120 size_t off = linelen - 1;
1121 if (line[off] == '\n')
1122 {
1123 /* We did get a full line: remove trailing space */
1124 do
1125 {
1126 line[off] = '\0';
1127 } while (off && isspace(line[--off]));
1128
1129 /* check to see if the next line is a continuation line */
1130 int ch = fgetc(fp);
1131 if ((ch != ' ') && (ch != '\t'))
1132 {
1133 /* next line is a separate header field or EOH */
1134 ungetc(ch, fp);
1135 buf_addstr(buf, line);
1136 break;
1137 }
1138 read++;
1139
1140 /* eat tabs and spaces from the beginning of the continuation line */
1141 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1142 {
1143 read++;
1144 }
1145
1146 ungetc(ch, fp);
1147 line[off + 1] = ' '; /* string is still terminated because we removed
1148 at least one whitespace char above */
1149 }
1150
1151 buf_addstr(buf, line);
1152 }
1153
1154 return read;
1155}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: