NeoMutt  2025-12-11-276-g10b23b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c File Reference

Miscellaneous email parsing routines. More...

#include "config.h"
#include <errno.h>
#include <string.h>
#include <time.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "parse.h"
#include "body.h"
#include "email.h"
#include "envelope.h"
#include "from.h"
#include "mime.h"
#include "module_data.h"
#include "parameter.h"
#include "rfc2047.h"
#include "rfc2231.h"
#include "url.h"
#include "autocrypt/lib.h"
+ Include dependency graph for parse.c:

Go to the source code of this file.

Macros

#define CONTENT_TOO_BIG   (1 << 30)
 Maximum reasonable Content-Length value (1 GiB) to prevent overflow.
 

Functions

static void parse_part (FILE *fp, struct Body *b, int *counter)
 Parse a MIME part.
 
static struct Bodyrfc822_parse_message (FILE *fp, struct Body *parent, int *counter)
 Parse a Message/RFC822 body.
 
static struct Bodyparse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, bool digest, int *counter)
 Parse a multipart structure.
 
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.
 
void mutt_auto_subscribe (const char *mailto)
 Check if user is subscribed to mailing list.
 
static void parse_parameters (struct ParameterList *pl, const char *s, bool allow_value_spaces)
 Parse a list of Parameters.
 
static void parse_content_disposition (const char *s, struct Body *b)
 Parse a content disposition.
 
static void parse_references (struct ListHead *head, const char *s)
 Parse references from an email header.
 
static void parse_content_language (const char *s, struct Body *b)
 Read the content's language.
 
bool mutt_matches_ignore (const char *s)
 Does the string match the ignore list.
 
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.
 
int mutt_check_encoding (const char *c)
 Check the encoding type.
 
void mutt_parse_content_type (const char *s, struct Body *b)
 Parse a content type.
 
static struct AutocryptHeaderparse_autocrypt (struct AutocryptHeader *head, const char *s)
 Parse an Autocrypt header line.
 
static char * rfc2369_first_mailto (const char *body)
 Extract the first mailto: URL from a RFC2369 list.
 
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.
 
size_t mutt_rfc822_read_line (FILE *fp, struct Buffer *buf)
 Read a header line from a file.
 
struct Envelopemutt_rfc822_read_header (FILE *fp, struct Email *e, bool user_hdrs, bool weed)
 Parses an RFC822 header.
 
struct Bodymutt_read_mime_header (FILE *fp, bool digest)
 Parse a MIME header.
 
bool mutt_is_message_type (int type, const char *subtype)
 Determine if a mime type matches a message or not.
 
static bool mailto_header_allowed (const char *s, struct ListHead *h)
 Is the string in the list.
 
bool mutt_parse_mailto (struct Envelope *env, char **body, const char *src)
 Parse a mailto:// url.
 
void mutt_parse_part (FILE *fp, struct Body *b)
 Parse a MIME part.
 
struct Bodymutt_rfc822_parse_message (FILE *fp, struct Body *b)
 Parse a Message/RFC822 body.
 
struct Bodymutt_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
 Parse a multipart structure.
 

Detailed Description

Miscellaneous email parsing routines.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Federico Kircheis
  • Ian Zimmerman
  • Christian Ludwig
  • David Purton
  • Steinar H Gunderson
  • Thomas Klausner

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.c.

Macro Definition Documentation

◆ CONTENT_TOO_BIG

#define CONTENT_TOO_BIG   (1 << 30)

Maximum reasonable Content-Length value (1 GiB) to prevent overflow.

Definition at line 61 of file parse.c.

Function Documentation

◆ parse_part()

static void parse_part ( FILE * fp,
struct Body * b,
int * counter )
static

Parse a MIME part.

Parameters
fpFile to read from
bBody to store the results in
counterNumber of parts processed so far

Definition at line 1522 of file parse.c.

1523{
1524 if (!fp || !b)
1525 return;
1526
1527 const char *bound = NULL;
1528 static unsigned short recurse_level = 0;
1529
1530 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1531 {
1532 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up\n");
1533 return;
1534 }
1535 recurse_level++;
1536
1537 switch (b->type)
1538 {
1539 case TYPE_MULTIPART:
1540 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1541 bound = "--------";
1542 else
1543 bound = mutt_param_get(&b->parameter, "boundary");
1544
1545 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1546 {
1547 goto bail;
1548 }
1549 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1550 mutt_istr_equal("digest", b->subtype), counter);
1551 break;
1552
1553 case TYPE_MESSAGE:
1554 if (!b->subtype)
1555 break;
1556
1557 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1558 {
1559 goto bail;
1560 }
1561 if (mutt_is_message_type(b->type, b->subtype))
1562 b->parts = rfc822_parse_message(fp, b, counter);
1563 else if (mutt_istr_equal(b->subtype, "external-body"))
1564 b->parts = mutt_read_mime_header(fp, 0);
1565 else
1566 goto bail;
1567 break;
1568
1569 default:
1570 goto bail;
1571 }
1572
1573 /* try to recover from parsing error */
1574 if (!b->parts)
1575 {
1576 b->type = TYPE_TEXT;
1577 mutt_str_replace(&b->subtype, "plain");
1578 }
1579bail:
1580 recurse_level--;
1581}
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1370
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1703
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:1593
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1506
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define MUTT_MIME_MAX_DEPTH
Maximum nesting depth for MIME parts to prevent stack overflow.
Definition mime.h:69
@ TYPE_MESSAGE
Type: 'message/*'.
Definition mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition parameter.c:85
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
LOFF_T offset
offset where the actual data begins
Definition body.h:52
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
char * subtype
content-type subtype
Definition body.h:61
unsigned int type
content-type primary type, ContentType
Definition body.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rfc822_parse_message()

static struct Body * rfc822_parse_message ( FILE * fp,
struct Body * parent,
int * counter )
static

Parse a Message/RFC822 body.

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

Definition at line 1703 of file parse.c.

1704{
1705 if (!fp || !parent)
1706 return NULL;
1707
1708 parent->email = email_new();
1709 parent->email->offset = ftello(fp);
1710 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1711 struct Body *msg = parent->email->body;
1712
1713 /* ignore the length given in the content-length since it could be wrong
1714 * and we already have the info to calculate the correct length */
1715 /* if (msg->length == -1) */
1716 msg->length = parent->length - (msg->offset - parent->offset);
1717
1718 /* if body of this message is empty, we can end up with a negative length */
1719 if (msg->length < 0)
1720 msg->length = 0;
1721
1722 parse_part(fp, msg, counter);
1723 return msg;
1724}
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1210
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1522
The body of an email.
Definition body.h:36
struct Email * email
header information for message/rfc822
Definition body.h:74
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
LOFF_T offset
Where in the stream does this message begin?
Definition email.h:71
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_multipart()

static struct Body * parse_multipart ( FILE * fp,
const char * boundary,
LOFF_T end_off,
bool digest,
int * counter )
static

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
counterNumber of parts processed so far
Return values
ptrNew Body containing parsed structure

Definition at line 1593 of file parse.c.

1595{
1596 if (!fp)
1597 return NULL;
1598
1599 if (!boundary)
1600 {
1601 mutt_error(_("multipart message has no boundary parameter"));
1602 return NULL;
1603 }
1604
1605 char buf[1024] = { 0 };
1606 struct Body *head = NULL, *last = NULL, *new_body = NULL;
1607 bool final = false; /* did we see the ending boundary? */
1608
1609 const size_t blen = mutt_str_len(boundary);
1610 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1611 {
1612 const size_t len = mutt_str_len(buf);
1613
1614 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1615
1616 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1617 {
1618 if (last)
1619 {
1620 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1621 if (last->parts && (last->parts->length == 0))
1622 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1623 /* if the body is empty, we can end up with a -1 length */
1624 if (last->length < 0)
1625 last->length = 0;
1626 }
1627
1628 if (len > 0)
1629 {
1630 /* Remove any trailing whitespace, up to the length of the boundary */
1631 for (size_t i = len - 1; mutt_isspace(buf[i]) && (i >= (blen + 2)); i--)
1632 buf[i] = '\0';
1633 }
1634
1635 /* Check for the end boundary */
1636 if (mutt_str_equal(buf + blen + 2, "--"))
1637 {
1638 final = true;
1639 break; /* done parsing */
1640 }
1641 else if (buf[2 + blen] == '\0')
1642 {
1643 new_body = mutt_read_mime_header(fp, digest);
1644 if (!new_body)
1645 break;
1646
1647 if (mutt_param_get(&new_body->parameter, "content-lines"))
1648 {
1649 int lines = 0;
1650 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1651 for (; lines > 0; lines--)
1652 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1653 break;
1654 }
1655
1656 /* Consistency checking - catch bad attachment end boundaries */
1657 if (new_body->offset > end_off)
1658 {
1659 mutt_body_free(&new_body);
1660 break;
1661 }
1662 if (head)
1663 {
1664 last->next = new_body;
1665 last = new_body;
1666 }
1667 else
1668 {
1669 last = new_body;
1670 head = new_body;
1671 }
1672
1673 /* It seems more intuitive to add the counter increment to
1674 * parse_part(), but we want to stop the case where a multipart
1675 * contains thousands of tiny parts before the memory and data
1676 * structures are allocated. */
1677 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1678 break;
1679 }
1680 }
1681 }
1682
1683 /* in case of missing end boundary, set the length to something reasonable */
1684 if (last && (last->length == 0) && !final)
1685 last->length = end_off - last->offset;
1686
1687 /* parse recursive MIME parts */
1688 for (last = head; last; last = last->next)
1689 parse_part(fp, last, counter);
1690
1691 return head;
1692}
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition atoi.c:191
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
#define mutt_error(...)
Definition logging2.h:94
#define MUTT_MIME_MAX_PARTS
Maximum number of MIME parts to process to prevent DoS.
Definition mime.h:71
#define _(a)
Definition message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
+ 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 72 of file parse.c.

73{
74 if (!header)
75 return;
76
77 for (; (*header != '\0'); header++)
78 {
79 if ((*header < 33) || (*header > 126) || (*header == ':'))
80 *header = '?';
81 }
82}
+ 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 92 of file parse.c.

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

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

109{
110 if (!mailto)
111 return;
112
114 ASSERT(md);
115
116 if (!md->auto_subscribe_cache)
118
119 if (mutt_hash_find(md->auto_subscribe_cache, mailto))
120 return;
121
123
124 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
125
126 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
127 {
128 const char *mailbox = buf_string(TAILQ_FIRST(&lpenv->to)->mailbox);
129 if (mailbox && !mutt_regexlist_match(&md->subscribed, mailbox) &&
130 !mutt_regexlist_match(&md->unmail, mailbox) &&
131 !mutt_regexlist_match(&md->unsubscribed, mailbox))
132 {
133 /* mutt_regexlist_add() detects duplicates, so it is safe to
134 * try to add here without any checks. */
135 mutt_regexlist_add(&md->mail, mailbox, REG_ICASE, NULL);
136 mutt_regexlist_add(&md->subscribed, mailbox, REG_ICASE, NULL);
137 }
138 }
139
140 mutt_env_free(&lpenv);
141}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1762
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
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:337
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:364
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:261
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:113
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition hash.h:112
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
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
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:582
#define TAILQ_FIRST(head)
Definition queue.h:780
#define TAILQ_EMPTY(head)
Definition queue.h:778
#define ASSERT(COND)
Definition signal2.h:59
Email private Module data.
Definition module_data.h:32
struct RegexList unsubscribed
Regexes to exclude false matches in subscribed.
Definition module_data.h:47
struct RegexList subscribed
Regexes to match subscribed mailing lists.
Definition module_data.h:42
struct RegexList unmail
Regexes to exclude false matches in mail.
Definition module_data.h:46
struct HashTable * auto_subscribe_cache
Hash Table: "mailto:" (no value)
Definition module_data.h:34
struct RegexList mail
Regexes to match mailing lists.
Definition module_data.h:38
The header of an Email.
Definition envelope.h:57
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
Container for Accounts, Notifications.
Definition neomutt.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_parameters()

static void parse_parameters ( struct ParameterList * pl,
const char * s,
bool allow_value_spaces )
static

Parse a list of Parameters.

Parameters
plParameter list for the results
sString to parse
allow_value_spacesAllow values with spaces

Autocrypt defines an irregular parameter format that doesn't follow the rfc. It splits keydata across multiple lines without parameter continuations. The allow_value_spaces parameter allows parsing those values which are split by spaces when unfolded.

Definition at line 154 of file parse.c.

155{
156 struct Parameter *pnew = NULL;
157 const char *p = NULL;
158 size_t i;
159
160 struct Buffer *buf = buf_pool_get();
161 /* allow_value_spaces, especially with autocrypt keydata, can result
162 * in quite large parameter values. avoid frequent reallocs by
163 * pre-sizing */
164 if (allow_value_spaces)
165 buf_alloc(buf, mutt_str_len(s));
166
167 mutt_debug(LL_DEBUG2, "'%s'\n", s);
168
169 const bool assumed = !slist_is_empty(cc_assumed_charset());
170 while (*s)
171 {
172 buf_reset(buf);
173
174 p = strpbrk(s, "=;");
175 if (!p)
176 {
177 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
178 goto bail;
179 }
180
181 /* if we hit a ; now the parameter has no value, just skip it */
182 if (*p != ';')
183 {
184 i = p - s;
185 /* remove whitespace from the end of the attribute name */
186 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
187 i--;
188
189 /* the check for the missing parameter token is here so that we can skip
190 * over any quoted value that may be present. */
191 if (i == 0)
192 {
193 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
194 pnew = NULL;
195 }
196 else
197 {
198 pnew = mutt_param_new();
199 pnew->attribute = mutt_strn_dup(s, i);
200 }
201
202 do
203 {
204 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
205
206 if (*s == '"')
207 {
208 bool state_ascii = true;
209 s++;
210 for (; *s; s++)
211 {
212 if (assumed)
213 {
214 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
215 if (*s == 0x1b)
216 {
217 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
218 state_ascii = true;
219 else
220 state_ascii = false;
221 }
222 }
223 if (state_ascii && (*s == '"'))
224 break;
225 if (*s == '\\')
226 {
227 if (s[1])
228 {
229 s++;
230 /* Quote the next character */
231 buf_addch(buf, *s);
232 }
233 }
234 else
235 {
236 buf_addch(buf, *s);
237 }
238 }
239 if (*s)
240 s++; /* skip over the " */
241 }
242 else
243 {
244 for (; *s && *s != ' ' && *s != ';'; s++)
245 buf_addch(buf, *s);
246 }
247
248 p = s;
249 } while (allow_value_spaces && (*s == ' '));
250
251 /* if the attribute token was missing, 'new' will be NULL */
252 if (pnew)
253 {
254 pnew->value = buf_strdup(buf);
255
256 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
257 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
258
259 /* Add this parameter to the list */
260 TAILQ_INSERT_HEAD(pl, pnew, entries);
261 }
262 }
263 else
264 {
265 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
266 s = p;
267 }
268
269 /* Find the next parameter */
270 if ((*s != ';') && !(s = strchr(s, ';')))
271 break; /* no more parameters */
272
273 do
274 {
275 /* Move past any leading whitespace. the +1 skips over the semicolon */
276 s = mutt_str_skip_email_wsp(s + 1);
277 } while (*s == ';'); /* skip empty parameters */
278 }
279
280bail:
281
283 buf_pool_release(&buf);
284}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition slist.c:140
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition parameter.c:40
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition queue.h:853
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition rfc2231.c:241
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition string2.h:111
String manipulation buffer.
Definition buffer.h:36
Attribute associated with a MIME part.
Definition parameter.h:33
char * attribute
Parameter name.
Definition parameter.h:34
char * value
Parameter value.
Definition parameter.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_content_disposition()

static void parse_content_disposition ( const char * s,
struct Body * b )
static

Parse a content disposition.

Parameters
sString to parse
bBody to save the result

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

Definition at line 293 of file parse.c.

294{
295 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
296
297 if (mutt_istr_startswith(s, "inline"))
299 else if (mutt_istr_startswith(s, "form-data"))
301 else
303
304 /* Check to see if a default filename was given */
305 s = strchr(s, ';');
306 if (s)
307 {
308 s = mutt_str_skip_email_wsp(s + 1);
309 parse_parameters(&pl, s, false);
310 s = mutt_param_get(&pl, "filename");
311 if (s)
313 s = mutt_param_get(&pl, "name");
314 if (s)
316 mutt_param_free(&pl);
317 }
318}
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition parse.c:154
@ DISP_ATTACH
Content is attached.
Definition mime.h:63
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ DISP_FORM_DATA
Content is form-data.
Definition mime.h:64
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition parameter.c:62
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
char * form_name
Content-Disposition form-data name param.
Definition body.h:60
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:

◆ parse_references()

static void parse_references ( struct ListHead * head,
const char * s )
static

Parse references from an email header.

Parameters
headList to receive the references
sString to parse

Definition at line 325 of file parse.c.

326{
327 if (!head)
328 return;
329
330 char *m = NULL;
331 for (size_t off = 0; (m = mutt_extract_message_id(s, &off)); s += off)
332 {
333 mutt_list_insert_head(head, m);
334 }
335}
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition parse.c:406
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_content_language()

static void parse_content_language ( const char * s,
struct Body * b )
static

Read the content's language.

Parameters
sLanguage string
bBody of the email

Definition at line 342 of file parse.c.

343{
344 if (!s || !b)
345 return;
346
347 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
349}
char * language
content-language (RFC8255)
Definition body.h:78
+ 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 358 of file parse.c.

359{
361 ASSERT(md);
362
363 return mutt_list_match(s, &md->ignore) && !mutt_list_match(s, &md->unignore);
364}
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes)
Definition list.c:194
struct ListHead unignore
Header patterns to unignore.
Definition module_data.h:45
struct ListHead ignore
Header patterns to ignore.
Definition module_data.h:37
+ 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 371 of file parse.c.

372{
373 if (mutt_istr_equal("text", s))
374 return TYPE_TEXT;
375 if (mutt_istr_equal("multipart", s))
376 return TYPE_MULTIPART;
377 if (mutt_istr_equal("x-sun-attachment", s))
378 return TYPE_MULTIPART;
379 if (mutt_istr_equal("application", s))
380 return TYPE_APPLICATION;
381 if (mutt_istr_equal("message", s))
382 return TYPE_MESSAGE;
383 if (mutt_istr_equal("image", s))
384 return TYPE_IMAGE;
385 if (mutt_istr_equal("audio", s))
386 return TYPE_AUDIO;
387 if (mutt_istr_equal("video", s))
388 return TYPE_VIDEO;
389 if (mutt_istr_equal("model", s))
390 return TYPE_MODEL;
391 if (mutt_istr_equal("*", s))
392 return TYPE_ANY;
393 if (mutt_istr_equal(".*", s))
394 return TYPE_ANY;
395
396 return TYPE_OTHER;
397}
@ 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_MODEL
Type: 'model/*'.
Definition mime.h:36
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
@ TYPE_ANY
Type: '' or '.'.
Definition mime.h:40
@ TYPE_VIDEO
Type: 'video/*'.
Definition mime.h:39
+ 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 406 of file parse.c.

407{
408 if (!s || (*s == '\0'))
409 return NULL;
410
411 char *decoded = mutt_str_dup(s);
412 rfc2047_decode(&decoded);
413
414 char *res = NULL;
415
416 for (const char *p = decoded, *beg = NULL; *p; p++)
417 {
418 if (*p == '<')
419 {
420 beg = p;
421 continue;
422 }
423
424 if (beg && (*p == '>'))
425 {
426 if (len)
427 *len = p - decoded + 1;
428 res = mutt_strn_dup(beg, (p + 1) - beg);
429 break;
430 }
431 }
432
433 FREE(&decoded);
434 return res;
435}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:665
+ 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 442 of file parse.c.

443{
444 if (mutt_istr_startswith(c, "7bit"))
445 return ENC_7BIT;
446 if (mutt_istr_startswith(c, "8bit"))
447 return ENC_8BIT;
448 if (mutt_istr_startswith(c, "binary"))
449 return ENC_BINARY;
450 if (mutt_istr_startswith(c, "quoted-printable"))
452 if (mutt_istr_startswith(c, "base64"))
453 return ENC_BASE64;
454 if (mutt_istr_startswith(c, "x-uuencode"))
455 return ENC_UUENCODED;
456 if (mutt_istr_startswith(c, "uuencode"))
457 return ENC_UUENCODED;
458 return ENC_OTHER;
459}
@ 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
+ 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 468 of file parse.c.

469{
470 if (!s || !b)
471 return;
472
473 FREE(&b->subtype);
475
476 /* First extract any existing parameters */
477 char *pc = strchr(s, ';');
478 if (pc)
479 {
480 *pc++ = 0;
481 while (*pc && mutt_isspace(*pc))
482 pc++;
483 parse_parameters(&b->parameter, pc, false);
484
485 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
486 * but if a filename has already been set in the content-disposition,
487 * let that take precedence, and don't set it here */
488 pc = mutt_param_get(&b->parameter, "name");
489 if (pc && !b->filename)
490 b->filename = mutt_str_dup(pc);
491
492 /* this is deep and utter perversion */
493 pc = mutt_param_get(&b->parameter, "conversions");
494 if (pc)
496 }
497
498 /* Now get the subtype */
499 char *subtype = strchr(s, '/');
500 if (subtype)
501 {
502 *subtype++ = '\0';
503 for (pc = subtype; *pc && !mutt_isspace(*pc) && (*pc != ';'); pc++)
504 ; // do nothing
505
506 *pc = '\0';
507 mutt_str_replace(&b->subtype, subtype);
508 }
509
510 /* Finally, get the major type */
512
513 if (mutt_istr_equal("x-sun-attachment", s))
514 mutt_str_replace(&b->subtype, "x-sun-attachment");
515
516 if (b->type == TYPE_OTHER)
517 {
518 mutt_str_replace(&b->xtype, s);
519 }
520
521 if (!b->subtype)
522 {
523 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
524 * field, so we can attempt to convert the type to Body here. */
525 if (b->type == TYPE_TEXT)
526 {
527 b->subtype = mutt_str_dup("plain");
528 }
529 else if (b->type == TYPE_AUDIO)
530 {
531 b->subtype = mutt_str_dup("basic");
532 }
533 else if (b->type == TYPE_MESSAGE)
534 {
535 b->subtype = mutt_str_dup("rfc822");
536 }
537 else if (b->type == TYPE_OTHER)
538 {
539 char buf[128] = { 0 };
540
542 snprintf(buf, sizeof(buf), "x-%s", s);
543 b->subtype = mutt_str_dup(buf);
544 }
545 else
546 {
547 b->subtype = mutt_str_dup("x-unknown");
548 }
549 }
550
551 /* Default character set for text types. */
552 if (b->type == TYPE_TEXT)
553 {
554 pc = mutt_param_get(&b->parameter, "charset");
555 if (pc)
556 {
557 /* Microsoft Outlook seems to think it is necessary to repeat
558 * charset=, strip it off not to confuse ourselves */
559 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
560 mutt_param_set(&b->parameter, "charset", pc + (sizeof("charset=") - 1));
561 }
562 else
563 {
564 mutt_param_set(&b->parameter, "charset",
566 }
567 }
568}
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:371
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:442
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition charset.c:451
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition string.c:457
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition parameter.c:111
char * xtype
content-type if x-unknown
Definition body.h:62
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_autocrypt()

static struct AutocryptHeader * parse_autocrypt ( struct AutocryptHeader * head,
const char * s )
static

Parse an Autocrypt header line.

Parameters
headAutocrypt header to insert before
sHeader string to parse
Return values
ptrNew AutocryptHeader inserted before head

Definition at line 577 of file parse.c.

578{
579 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
580 autocrypt->next = head;
581
582 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
583 parse_parameters(&pl, s, true);
584 if (TAILQ_EMPTY(&pl))
585 {
586 autocrypt->invalid = true;
587 goto cleanup;
588 }
589
590 struct Parameter *p = NULL;
591 TAILQ_FOREACH(p, &pl, entries)
592 {
593 if (mutt_istr_equal(p->attribute, "addr"))
594 {
595 if (autocrypt->addr)
596 {
597 autocrypt->invalid = true;
598 goto cleanup;
599 }
600 autocrypt->addr = p->value;
601 p->value = NULL;
602 }
603 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
604 {
605 if (mutt_istr_equal(p->value, "mutual"))
606 autocrypt->prefer_encrypt = true;
607 }
608 else if (mutt_istr_equal(p->attribute, "keydata"))
609 {
610 if (autocrypt->keydata)
611 {
612 autocrypt->invalid = true;
613 goto cleanup;
614 }
615 autocrypt->keydata = p->value;
616 p->value = NULL;
617 }
618 else if (p->attribute && (p->attribute[0] != '_'))
619 {
620 autocrypt->invalid = true;
621 goto cleanup;
622 }
623 }
624
625 /* Checking the addr against From, and for multiple valid headers
626 * occurs later, after all the headers are parsed. */
627 if (!autocrypt->addr || !autocrypt->keydata)
628 autocrypt->invalid = true;
629
630cleanup:
631 mutt_param_free(&pl);
632 return autocrypt;
633}
struct AutocryptHeader * mutt_autocrypthdr_new(void)
Create a new AutocryptHeader.
Definition envelope.c:94
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
Parse Autocrypt header info.
Definition envelope.h:44
bool invalid
Header is invalid.
Definition envelope.h:48
struct AutocryptHeader * next
Linked list.
Definition envelope.h:49
char * keydata
PGP Key data.
Definition envelope.h:46
bool prefer_encrypt
User prefers encryption.
Definition envelope.h:47
char * addr
Email address.
Definition envelope.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rfc2369_first_mailto()

static char * rfc2369_first_mailto ( const char * body)
static

Extract the first mailto: URL from a RFC2369 list.

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

Definition at line 641 of file parse.c.

642{
643 for (const char *beg = body, *end = NULL; beg; beg = strchr(end, ','))
644 {
645 beg = strchr(beg, '<');
646 if (!beg)
647 {
648 break;
649 }
650 beg++;
651 end = strchr(beg, '>');
652 if (!end)
653 {
654 break;
655 }
656
657 char *mlist = mutt_strn_dup(beg, end - beg);
658 if (url_check_scheme(mlist) == U_MAILTO)
659 {
660 return mlist;
661 }
662 FREE(&mlist);
663 }
664 return NULL;
665}
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition url.c:226
@ U_MAILTO
Url is mailto://.
Definition url.h:45
+ 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 683 of file parse.c.

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

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

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

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

1371{
1372 if (!fp)
1373 return NULL;
1374
1375 struct Body *b = mutt_body_new();
1376 struct Envelope *env = mutt_env_new();
1377 char *c = NULL;
1378 struct Buffer *buf = buf_pool_get();
1379 bool matched = false;
1380
1381 b->hdr_offset = ftello(fp);
1382
1383 b->encoding = ENC_7BIT; /* default from RFC1521 */
1384 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1386
1387 while (mutt_rfc822_read_line(fp, buf) != 0)
1388 {
1389 const char *line = buf_string(buf);
1390 /* Find the value of the current header */
1391 c = strchr(line, ':');
1392 if (c)
1393 {
1394 *c = '\0';
1395 c = mutt_str_skip_email_wsp(c + 1);
1396 if (*c == '\0')
1397 {
1398 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1399 continue;
1400 }
1401 }
1402 else
1403 {
1404 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1405 break;
1406 }
1407
1408 size_t plen = mutt_istr_startswith(line, "content-");
1409 if (plen != 0)
1410 {
1411 if (mutt_istr_equal("type", line + plen))
1412 {
1414 }
1415 else if (mutt_istr_equal("language", line + plen))
1416 {
1418 }
1419 else if (mutt_istr_equal("transfer-encoding", line + plen))
1420 {
1422 }
1423 else if (mutt_istr_equal("disposition", line + plen))
1424 {
1426 }
1427 else if (mutt_istr_equal("description", line + plen))
1428 {
1431 }
1432 else if (mutt_istr_equal("id", line + plen))
1433 {
1434 // strip <angle braces> from Content-ID: header
1435 char *id = c;
1436 int cid_len = mutt_str_len(c);
1437 if (cid_len > 2)
1438 {
1439 if (id[0] == '<')
1440 {
1441 id++;
1442 cid_len--;
1443 }
1444 if (id[cid_len - 1] == '>')
1445 id[cid_len - 1] = '\0';
1446 }
1448 }
1449 }
1450 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1451 {
1452 if (mutt_istr_equal("data-type", line + plen))
1453 {
1455 }
1456 else if (mutt_istr_equal("encoding-info", line + plen))
1457 {
1459 }
1460 else if (mutt_istr_equal("content-lines", line + plen))
1461 {
1462 mutt_param_set(&b->parameter, "content-lines", c);
1463 }
1464 else if (mutt_istr_equal("data-description", line + plen))
1465 {
1468 }
1469 }
1470 else
1471 {
1472 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1473 {
1474 matched = true;
1475 }
1476 }
1477 }
1478 b->offset = ftello(fp); /* Mark the start of the real data */
1479 if ((b->type == TYPE_TEXT) && !b->subtype)
1480 b->subtype = mutt_str_dup("plain");
1481 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1482 b->subtype = mutt_str_dup("rfc822");
1483
1484 buf_pool_release(&buf);
1485
1486 if (matched)
1487 {
1488 b->mime_headers = env;
1490 }
1491 else
1492 {
1493 mutt_env_free(&env);
1494 }
1495
1496 return b;
1497}
char * content_id
Content-Id (RFC2392)
Definition body.h:58
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
+ 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 1506 of file parse.c.

1507{
1508 if (type != TYPE_MESSAGE)
1509 return false;
1510
1511 subtype = NONULL(subtype);
1512 return (mutt_istr_equal(subtype, "rfc822") ||
1513 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1514}
#define NONULL(x)
Definition string2.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mailto_header_allowed()

static bool mailto_header_allowed ( const char * s,
struct ListHead * h )
static

Is the string in the list.

Parameters
sString to match
hHead of the List
Return values
trueString matches a List item (or List contains "*")

This is a very specific function. It searches a List of strings looking for a match. If the list contains a string "*", then it match any input string.

This is similar to mutt_list_match(), except that it doesn't allow prefix matches.

Note
The case of the strings is ignored.

Definition at line 1740 of file parse.c.

1741{
1742 if (!h)
1743 return false;
1744
1745 struct ListNode *np = NULL;
1746 STAILQ_FOREACH(np, h, entries)
1747 {
1748 if ((*np->data == '*') || mutt_istr_equal(s, np->data))
1749 return true;
1750 }
1751 return false;
1752}
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
+ 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 1762 of file parse.c.

1763{
1764 if (!env || !src)
1765 return false;
1766
1767 struct Url *url = url_parse(src);
1768 if (!url)
1769 return false;
1770
1771 if (url->host)
1772 {
1773 /* this is not a path-only URL */
1774 url_free(&url);
1775 return false;
1776 }
1777
1778 mutt_addrlist_parse(&env->to, url->path);
1779
1781 ASSERT(md);
1782
1783 struct UrlQuery *np;
1784 STAILQ_FOREACH(np, &url->query_strings, entries)
1785 {
1787 const char *tag = np->name;
1788 char *value = np->value;
1789 /* Determine if this header field is on the allowed list. Since NeoMutt
1790 * interprets some header fields specially (such as
1791 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1792 * only safe fields are allowed.
1793 *
1794 * RFC2368, "4. Unsafe headers"
1795 * The user agent interpreting a mailto URL SHOULD choose not to create
1796 * a message if any of the headers are considered dangerous; it may also
1797 * choose to create a message with only a subset of the headers given in
1798 * the URL. */
1799 if (mailto_header_allowed(tag, &md->mail_to_allow))
1800 {
1801 if (mutt_istr_equal(tag, "body"))
1802 {
1803 if (body)
1804 mutt_str_replace(body, value);
1805 }
1806 else
1807 {
1808 char *scratch = NULL;
1809 size_t taglen = mutt_str_len(tag);
1810
1812 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1813 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1814 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1815 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1816 FREE(&scratch);
1817 }
1818 }
1819 }
1820
1821 /* RFC2047 decode after the RFC822 parsing */
1823
1824 url_free(&url);
1825 return true;
1826}
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1740
void mutt_filter_commandline_header_tag(char *header)
Sanitise characters in a header tag.
Definition parse.c:72
struct ListHead mail_to_allow
Permitted fields in a mailto: url.
Definition module_data.h:39
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_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 1833 of file parse.c.

1834{
1835 int counter = 0;
1836
1837 parse_part(fp, b, &counter);
1838}
+ 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 1848 of file parse.c.

1849{
1850 int counter = 0;
1851
1852 return rfc822_parse_message(fp, b, &counter);
1853}
+ 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 1864 of file parse.c.

1865{
1866 int counter = 0;
1867
1868 return parse_multipart(fp, boundary, end_off, digest, &counter);
1869}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: