NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
parse.c File Reference

Miscellaneous email parsing routines. More...

#include "config.h"
#include <ctype.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 "globals.h"
#include "mime.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)
 

Functions

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

Detailed Description

Miscellaneous email parsing routines.

Authors
  • Richard Russon
  • Pietro Cerutti

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

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

Definition in file parse.c.

Macro Definition Documentation

◆ CONTENT_TOO_BIG

#define CONTENT_TOO_BIG   (1 << 30)

Definition at line 56 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 1457 of file parse.c.

1458{
1459 if (!fp || !b)
1460 return;
1461
1462 const char *bound = NULL;
1463 static unsigned short recurse_level = 0;
1464
1465 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1466 {
1467 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1468 return;
1469 }
1470 recurse_level++;
1471
1472 switch (b->type)
1473 {
1474 case TYPE_MULTIPART:
1475#ifdef SUN_ATTACHMENT
1476 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1477 bound = "--------";
1478 else
1479#endif
1480 bound = mutt_param_get(&b->parameter, "boundary");
1481
1482 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1483 {
1484 goto bail;
1485 }
1486 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1487 mutt_istr_equal("digest", b->subtype), counter);
1488 break;
1489
1490 case TYPE_MESSAGE:
1491 if (!b->subtype)
1492 break;
1493
1494 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1495 {
1496 goto bail;
1497 }
1498 if (mutt_is_message_type(b->type, b->subtype))
1499 b->parts = rfc822_parse_message(fp, b, counter);
1500 else if (mutt_istr_equal(b->subtype, "external-body"))
1501 b->parts = mutt_read_mime_header(fp, 0);
1502 else
1503 goto bail;
1504 break;
1505
1506 default:
1507 goto bail;
1508 }
1509
1510 /* try to recover from parsing error */
1511 if (!b->parts)
1512 {
1513 b->type = TYPE_TEXT;
1514 mutt_str_replace(&b->subtype, "plain");
1515 }
1516bail:
1517 recurse_level--;
1518}
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:706
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
#define MUTT_MIME_MAX_DEPTH
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:819
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1317
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition: parse.c:1639
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:1530
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1441
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
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:62
char * subtype
content-type subtype
Definition: body.h:60
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 1639 of file parse.c.

1640{
1641 if (!fp || !parent)
1642 return NULL;
1643
1644 parent->email = email_new();
1645 parent->email->offset = ftello(fp);
1646 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1647 struct Body *msg = parent->email->body;
1648
1649 /* ignore the length given in the content-length since it could be wrong
1650 * and we already have the info to calculate the correct length */
1651 /* if (msg->length == -1) */
1652 msg->length = parent->length - (msg->offset - parent->offset);
1653
1654 /* if body of this message is empty, we can end up with a negative length */
1655 if (msg->length < 0)
1656 msg->length = 0;
1657
1658 parse_part(fp, msg, counter);
1659 return msg;
1660}
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1157
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition: parse.c:1457
The body of an email.
Definition: body.h:36
struct Email * email
header information for message/rfc822
Definition: body.h:73
struct Envelope * env
Envelope information.
Definition: email.h:66
struct Body * body
List of MIME parts.
Definition: email.h:67
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
+ 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 1530 of file parse.c.

1532{
1533 if (!fp)
1534 return NULL;
1535
1536 if (!boundary)
1537 {
1538 mutt_error(_("multipart message has no boundary parameter"));
1539 return NULL;
1540 }
1541
1542 char buf[1024] = { 0 };
1543 struct Body *head = NULL, *last = NULL, *new_body = NULL;
1544 bool final = false; /* did we see the ending boundary? */
1545
1546 const size_t blen = mutt_str_len(boundary);
1547 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1548 {
1549 const size_t len = mutt_str_len(buf);
1550
1551 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1552
1553 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1554 {
1555 if (last)
1556 {
1557 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1558 if (last->parts && (last->parts->length == 0))
1559 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1560 /* if the body is empty, we can end up with a -1 length */
1561 if (last->length < 0)
1562 last->length = 0;
1563 }
1564
1565 if (len > 0)
1566 {
1567 /* Remove any trailing whitespace, up to the length of the boundary */
1568 for (size_t i = len - 1; IS_SPACE(buf[i]) && (i >= (blen + 2)); i--)
1569 buf[i] = '\0';
1570 }
1571
1572 /* Check for the end boundary */
1573 if (mutt_str_equal(buf + blen + 2, "--"))
1574 {
1575 final = true;
1576 break; /* done parsing */
1577 }
1578 else if (buf[2 + blen] == '\0')
1579 {
1580 new_body = mutt_read_mime_header(fp, digest);
1581
1582#ifdef SUN_ATTACHMENT
1583 if (mutt_param_get(&new_body->parameter, "content-lines"))
1584 {
1585 int lines = 0;
1586 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1587 for (; lines > 0; lines--)
1588 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1589 break;
1590 }
1591#endif
1592 /* Consistency checking - catch bad attachment end boundaries */
1593 if (new_body->offset > end_off)
1594 {
1595 mutt_body_free(&new_body);
1596 break;
1597 }
1598 if (head)
1599 {
1600 last->next = new_body;
1601 last = new_body;
1602 }
1603 else
1604 {
1605 last = new_body;
1606 head = new_body;
1607 }
1608
1609 /* It seems more intuitive to add the counter increment to
1610 * parse_part(), but we want to stop the case where a multipart
1611 * contains thousands of tiny parts before the memory and data
1612 * structures are allocated. */
1613 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1614 break;
1615 }
1616 }
1617 }
1618
1619 /* in case of missing end boundary, set the length to something reasonable */
1620 if (last && (last->length == 0) && !final)
1621 last->length = end_off - last->offset;
1622
1623 /* parse recursive MIME parts */
1624 for (last = head; last; last = last->next)
1625 parse_part(fp, last, counter);
1626
1627 return head;
1628}
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: atoi.c:179
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
#define mutt_error(...)
Definition: logging.h:87
#define MUTT_MIME_MAX_PARTS
Definition: mime.h:70
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
#define IS_SPACE(ch)
Definition: string2.h:38
+ Here is the call graph for this function:
+ 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 67 of file parse.c.

68{
69 if (!mailto)
70 return;
71
74
76 return;
77
79
80 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
81
82 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
83 {
84 const char *mailbox = TAILQ_FIRST(&lpenv->to)->mailbox;
85 if (mailbox && !mutt_regexlist_match(&SubscribedLists, mailbox) &&
88 {
89 /* mutt_regexlist_add() detects duplicates, so it is safe to
90 * try to add here without any checks. */
91 mutt_regexlist_add(&MailLists, mailbox, REG_ICASE, NULL);
92 mutt_regexlist_add(&SubscribedLists, mailbox, REG_ICASE, NULL);
93 }
94 }
95
96 mutt_env_free(&lpenv);
97}
struct RegexList SubscribedLists
List of regexes to match subscribed mailing lists.
Definition: globals.c:43
struct HashTable * AutoSubscribeCache
Hash Table of auto-subscribed mailing lists.
Definition: globals.c:38
struct RegexList UnSubscribedLists
List of regexes to exclude false matches in SubscribedLists.
Definition: globals.c:40
struct RegexList UnMailLists
List of regexes to exclude false matches in MailLists.
Definition: globals.c:42
struct RegexList MailLists
List of regexes to match mailing lists.
Definition: globals.c:41
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:110
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:109
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:135
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition: regex.c:195
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1670
#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:

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

111{
112 struct Parameter *pnew = NULL;
113 const char *p = NULL;
114 size_t i;
115
116 struct Buffer *buf = mutt_buffer_pool_get();
117 /* allow_value_spaces, especially with autocrypt keydata, can result
118 * in quite large parameter values. avoid frequent reallocs by
119 * pre-sizing */
120 if (allow_value_spaces)
122
123 mutt_debug(LL_DEBUG2, "'%s'\n", s);
124
125 while (*s)
126 {
128
129 p = strpbrk(s, "=;");
130 if (!p)
131 {
132 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
133 goto bail;
134 }
135
136 /* if we hit a ; now the parameter has no value, just skip it */
137 if (*p != ';')
138 {
139 i = p - s;
140 /* remove whitespace from the end of the attribute name */
141 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
142 i--;
143
144 /* the check for the missing parameter token is here so that we can skip
145 * over any quoted value that may be present. */
146 if (i == 0)
147 {
148 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
149 pnew = NULL;
150 }
151 else
152 {
153 pnew = mutt_param_new();
154 pnew->attribute = mutt_strn_dup(s, i);
155 }
156
157 do
158 {
159 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
160
161 if (*s == '"')
162 {
163 bool state_ascii = true;
164 s++;
165 for (; *s; s++)
166 {
167 const struct Slist *const c_assumed_charset =
168 cs_subset_slist(NeoMutt->sub, "assumed_charset");
169 if (c_assumed_charset)
170 {
171 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
172 if (*s == 0x1b)
173 {
174 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
175 state_ascii = true;
176 else
177 state_ascii = false;
178 }
179 }
180 if (state_ascii && (*s == '"'))
181 break;
182 if (*s == '\\')
183 {
184 if (s[1])
185 {
186 s++;
187 /* Quote the next character */
188 mutt_buffer_addch(buf, *s);
189 }
190 }
191 else
192 mutt_buffer_addch(buf, *s);
193 }
194 if (*s)
195 s++; /* skip over the " */
196 }
197 else
198 {
199 for (; *s && *s != ' ' && *s != ';'; s++)
200 mutt_buffer_addch(buf, *s);
201 }
202
203 p = s;
204 } while (allow_value_spaces && (*s == ' '));
205
206 /* if the attribute token was missing, 'new' will be NULL */
207 if (pnew)
208 {
209 pnew->value = mutt_buffer_strdup(buf);
210
211 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
212 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
213
214 /* Add this parameter to the list */
215 TAILQ_INSERT_HEAD(pl, pnew, entries);
216 }
217 }
218 else
219 {
220 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
221 s = p;
222 }
223
224 /* Find the next parameter */
225 if ((*s != ';') && !(s = strchr(s, ';')))
226 break; /* no more parameters */
227
228 do
229 {
230 /* Move past any leading whitespace. the +1 skips over the semicolon */
231 s = mutt_str_skip_email_wsp(s + 1);
232 } while (*s == ';'); /* skip empty parameters */
233 }
234
235bail:
236
239}
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:313
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:485
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:695
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition: rfc2231.c:236
String manipulation buffer.
Definition: buffer.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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
String list.
Definition: slist.h:47
+ 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 ct 
)
static

Parse a content disposition.

Parameters
sString to parse
ctBody to save the result

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

Definition at line 248 of file parse.c.

249{
250 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
251
252 if (mutt_istr_startswith(s, "inline"))
254 else if (mutt_istr_startswith(s, "form-data"))
256 else
258
259 /* Check to see if a default filename was given */
260 s = strchr(s, ';');
261 if (s)
262 {
263 s = mutt_str_skip_email_wsp(s + 1);
264 parse_parameters(&pl, s, false);
265 s = mutt_param_get(&pl, "filename");
266 if (s)
267 mutt_str_replace(&ct->filename, s);
268 s = mutt_param_get(&pl, "name");
269 if (s)
271 mutt_param_free(&pl);
272 }
273}
@ 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:239
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition: parse.c:110
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:42
char * form_name
Content-Disposition form-data name param.
Definition: body.h:59
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:

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

281{
282 if (!head)
283 return;
284
285 char *m = NULL;
286 for (size_t off = 0; (m = mutt_extract_message_id(s, &off)); s += off)
287 {
288 mutt_list_insert_head(head, m);
289 }
290}
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:360
+ 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 ct 
)
static

Read the content's language.

Parameters
sLanguage string
ctBody of the email

Definition at line 297 of file parse.c.

298{
299 if (!s || !ct)
300 return;
301
302 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
303 mutt_str_replace(&ct->language, s);
304}
char * language
content-language (RFC8255)
Definition: body.h:77
+ 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 313 of file parse.c.

314{
315 return mutt_list_match(s, &Ignore) && !mutt_list_match(s, &UnIgnore);
316}
struct ListHead Ignore
List of header patterns to ignore.
Definition: globals.c:35
struct ListHead UnIgnore
List of header patterns to unignore (see)
Definition: globals.c:36
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_check_mime_type()

enum ContentType mutt_check_mime_type ( const char *  s)

Check a MIME type string.

Parameters
sString to check
Return values
numMIME type, e.g. TYPE_TEXT

Definition at line 323 of file parse.c.

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

361{
362 if (!s || (*s == '\0'))
363 return NULL;
364
365 char *decoded = mutt_str_dup(s);
366 rfc2047_decode(&decoded);
367
368 char *res = NULL;
369
370 for (const char *p = decoded, *beg = NULL; *p; p++)
371 {
372 if (*p == '<')
373 {
374 beg = p;
375 continue;
376 }
377
378 if (beg && (*p == '>'))
379 {
380 if (len)
381 *len = p - decoded + 1;
382 res = mutt_strn_dup(beg, (p + 1) - beg);
383 break;
384 }
385 }
386
387 FREE(&decoded);
388 return res;
389}
#define FREE(x)
Definition: memory.h:43
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:649
+ 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
numEncoding type, e.g. ENC_QUOTED_PRINTABLE

Definition at line 396 of file parse.c.

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

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

530{
531 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
532 autocrypt->next = head;
533
534 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
535 parse_parameters(&pl, s, true);
536 if (TAILQ_EMPTY(&pl))
537 {
538 autocrypt->invalid = true;
539 goto cleanup;
540 }
541
542 struct Parameter *p = NULL;
543 TAILQ_FOREACH(p, &pl, entries)
544 {
545 if (mutt_istr_equal(p->attribute, "addr"))
546 {
547 if (autocrypt->addr)
548 {
549 autocrypt->invalid = true;
550 goto cleanup;
551 }
552 autocrypt->addr = p->value;
553 p->value = NULL;
554 }
555 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
556 {
557 if (mutt_istr_equal(p->value, "mutual"))
558 autocrypt->prefer_encrypt = true;
559 }
560 else if (mutt_istr_equal(p->attribute, "keydata"))
561 {
562 if (autocrypt->keydata)
563 {
564 autocrypt->invalid = true;
565 goto cleanup;
566 }
567 autocrypt->keydata = p->value;
568 p->value = NULL;
569 }
570 else if (p->attribute && (p->attribute[0] != '_'))
571 {
572 autocrypt->invalid = true;
573 goto cleanup;
574 }
575 }
576
577 /* Checking the addr against From, and for multiple valid headers
578 * occurs later, after all the headers are parsed. */
579 if (!autocrypt->addr || !autocrypt->keydata)
580 autocrypt->invalid = true;
581
582cleanup:
583 mutt_param_free(&pl);
584 return autocrypt;
585}
struct AutocryptHeader * mutt_autocrypthdr_new(void)
Create a new AutocryptHeader.
Definition: envelope.c:66
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
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 593 of file parse.c.

594{
595 for (const char *beg = body, *end = NULL; beg; beg = strchr(end, ','))
596 {
597 beg = strchr(beg, '<');
598 if (!beg)
599 {
600 break;
601 }
602 beg++;
603 end = strchr(beg, '>');
604 if (!end)
605 {
606 break;
607 }
608
609 char *mlist = mutt_strn_dup(beg, end - beg);
610 if (url_check_scheme(mlist) == U_MAILTO)
611 {
612 return mlist;
613 }
614 FREE(&mlist);
615 }
616 return NULL;
617}
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
@ 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,
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'
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 634 of file parse.c.

636{
637 if (!env || !name)
638 return 0;
639
640 bool matched = false;
641
642 switch (tolower(name[0]))
643 {
644 case 'a':
645 if (mutt_istr_equal(name + 1, "pparently-to"))
646 {
647 mutt_addrlist_parse(&env->to, body);
648 matched = true;
649 }
650 else if (mutt_istr_equal(name + 1, "pparently-from"))
651 {
652 mutt_addrlist_parse(&env->from, body);
653 matched = true;
654 }
655#ifdef USE_AUTOCRYPT
656 else if (mutt_istr_equal(name + 1, "utocrypt"))
657 {
658 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
659 if (c_autocrypt)
660 {
661 env->autocrypt = parse_autocrypt(env->autocrypt, body);
662 matched = true;
663 }
664 }
665 else if (mutt_istr_equal(name + 1, "utocrypt-gossip"))
666 {
667 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
668 if (c_autocrypt)
669 {
671 matched = true;
672 }
673 }
674#endif
675 break;
676
677 case 'b':
678 if (mutt_istr_equal(name + 1, "cc"))
679 {
680 mutt_addrlist_parse(&env->bcc, body);
681 matched = true;
682 }
683 break;
684
685 case 'c':
686 if (mutt_istr_equal(name + 1, "c"))
687 {
688 mutt_addrlist_parse(&env->cc, body);
689 matched = true;
690 }
691 else
692 {
693 size_t plen = mutt_istr_startswith(name + 1, "ontent-");
694 if (plen != 0)
695 {
696 if (mutt_istr_equal(name + 1 + plen, "type"))
697 {
698 if (e)
700 matched = true;
701 }
702 else if (mutt_istr_equal(name + 1 + plen, "language"))
703 {
704 if (e)
706 matched = true;
707 }
708 else if (mutt_istr_equal(name + 1 + plen, "transfer-encoding"))
709 {
710 if (e)
712 matched = true;
713 }
714 else if (mutt_istr_equal(name + 1 + plen, "length"))
715 {
716 if (e)
717 {
718 unsigned long len = 0;
719 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
720 }
721 matched = true;
722 }
723 else if (mutt_istr_equal(name + 1 + plen, "description"))
724 {
725 if (e)
726 {
729 }
730 matched = true;
731 }
732 else if (mutt_istr_equal(name + 1 + plen, "disposition"))
733 {
734 if (e)
736 matched = true;
737 }
738 }
739 }
740 break;
741
742 case 'd':
743 if (!mutt_istr_equal("ate", name + 1))
744 break;
745
746 mutt_str_replace(&env->date, body);
747 if (e)
748 {
749 struct Tz tz;
750 e->date_sent = mutt_date_parse_date(body, &tz);
751 if (e->date_sent > 0)
752 {
753 e->zhours = tz.zhours;
754 e->zminutes = tz.zminutes;
755 e->zoccident = tz.zoccident;
756 }
757 }
758 matched = true;
759 break;
760
761 case 'e':
762 if (mutt_istr_equal("xpires", name + 1) && e &&
763 (mutt_date_parse_date(body, NULL) < mutt_date_now()))
764 {
765 e->expired = true;
766 }
767 break;
768
769 case 'f':
770 if (mutt_istr_equal("rom", name + 1))
771 {
772 mutt_addrlist_parse(&env->from, body);
773 matched = true;
774 }
775#ifdef USE_NNTP
776 else if (mutt_istr_equal(name + 1, "ollowup-to"))
777 {
778 if (!env->followup_to)
779 {
782 }
783 matched = true;
784 }
785#endif
786 break;
787
788 case 'i':
789 if (!mutt_istr_equal(name + 1, "n-reply-to"))
790 break;
791
793 parse_references(&env->in_reply_to, body);
794 matched = true;
795 break;
796
797 case 'l':
798 if (mutt_istr_equal(name + 1, "ines"))
799 {
800 if (e)
801 {
802 unsigned int ui = 0; // we don't want a negative number of lines
803 mutt_str_atoui(body, &ui);
804 e->lines = ui;
805 }
806
807 matched = true;
808 }
809 else if (mutt_istr_equal(name + 1, "ist-Post"))
810 {
811 /* RFC2369 */
812 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
813 {
814 char *mailto = rfc2369_first_mailto(body);
815 if (mailto)
816 {
817 FREE(&env->list_post);
818 env->list_post = mailto;
819 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
820 if (c_auto_subscribe)
822 }
823 }
824 matched = true;
825 }
826 else if (mutt_istr_equal(name + 1, "ist-Subscribe"))
827 {
828 /* RFC2369 */
829 char *mailto = rfc2369_first_mailto(body);
830 if (mailto)
831 {
832 FREE(&env->list_subscribe);
833 env->list_subscribe = mailto;
834 }
835 matched = true;
836 }
837 else if (mutt_istr_equal(name + 1, "ist-Unsubscribe"))
838 {
839 /* RFC2369 */
840 char *mailto = rfc2369_first_mailto(body);
841 if (mailto)
842 {
843 FREE(&env->list_unsubscribe);
844 env->list_unsubscribe = mailto;
845 }
846 matched = true;
847 }
848 break;
849
850 case 'm':
851 if (mutt_istr_equal(name + 1, "ime-version"))
852 {
853 if (e)
854 e->mime = true;
855 matched = true;
856 }
857 else if (mutt_istr_equal(name + 1, "essage-id"))
858 {
859 /* We add a new "Message-ID:" when building a message */
860 FREE(&env->message_id);
861 env->message_id = mutt_extract_message_id(body, NULL);
862 matched = true;
863 }
864 else
865 {
866 size_t plen = mutt_istr_startswith(name + 1, "ail-");
867 if (plen != 0)
868 {
869 if (mutt_istr_equal(name + 1 + plen, "reply-to"))
870 {
871 /* override the Reply-To: field */
873 mutt_addrlist_parse(&env->reply_to, body);
874 matched = true;
875 }
876 else if (mutt_istr_equal(name + 1 + plen, "followup-to"))
877 {
879 matched = true;
880 }
881 }
882 }
883 break;
884
885#ifdef USE_NNTP
886 case 'n':
887 if (mutt_istr_equal(name + 1, "ewsgroups"))
888 {
889 FREE(&env->newsgroups);
892 matched = true;
893 }
894 break;
895#endif
896
897 case 'o':
898 /* field 'Organization:' saves only for pager! */
899 if (mutt_istr_equal(name + 1, "rganization"))
900 {
901 if (!env->organization && !mutt_istr_equal(body, "unknown"))
902 env->organization = mutt_str_dup(body);
903 }
904 break;
905
906 case 'r':
907 if (mutt_istr_equal(name + 1, "eferences"))
908 {
910 parse_references(&env->references, body);
911 matched = true;
912 }
913 else if (mutt_istr_equal(name + 1, "eply-to"))
914 {
915 mutt_addrlist_parse(&env->reply_to, body);
916 matched = true;
917 }
918 else if (mutt_istr_equal(name + 1, "eturn-path"))
919 {
920 mutt_addrlist_parse(&env->return_path, body);
921 matched = true;
922 }
923 else if (mutt_istr_equal(name + 1, "eceived"))
924 {
925 if (e && (e->received == 0))
926 {
927 char *d = strrchr(body, ';');
928 if (d)
929 {
930 d = mutt_str_skip_email_wsp(d + 1);
931 e->received = mutt_date_parse_date(d, NULL);
932 }
933 }
934 }
935 break;
936
937 case 's':
938 if (mutt_istr_equal(name + 1, "ubject"))
939 {
940 if (!env->subject)
941 env->subject = mutt_str_dup(body);
942 matched = true;
943 }
944 else if (mutt_istr_equal(name + 1, "ender"))
945 {
946 mutt_addrlist_parse(&env->sender, body);
947 matched = true;
948 }
949 else if (mutt_istr_equal(name + 1, "tatus"))
950 {
951 if (e)
952 {
953 while (*body)
954 {
955 switch (*body)
956 {
957 case 'O':
958 {
959 e->old = true;
960 break;
961 }
962 case 'R':
963 e->read = true;
964 break;
965 case 'r':
966 e->replied = true;
967 break;
968 }
969 body++;
970 }
971 }
972 matched = true;
973 }
974 else if (e && (mutt_istr_equal("upersedes", name + 1) ||
975 mutt_istr_equal("upercedes", name + 1)))
976 {
977 FREE(&env->supersedes);
978 env->supersedes = mutt_str_dup(body);
979 }
980 break;
981
982 case 't':
983 if (mutt_istr_equal(name + 1, "o"))
984 {
985 mutt_addrlist_parse(&env->to, body);
986 matched = true;
987 }
988 break;
989
990 case 'x':
991 if (mutt_istr_equal(name + 1, "-status"))
992 {
993 if (e)
994 {
995 while (*body)
996 {
997 switch (*body)
998 {
999 case 'A':
1000 e->replied = true;
1001 break;
1002 case 'D':
1003 e->deleted = true;
1004 break;
1005 case 'F':
1006 e->flagged = true;
1007 break;
1008 default:
1009 break;
1010 }
1011 body++;
1012 }
1013 }
1014 matched = true;
1015 }
1016 else if (mutt_istr_equal(name + 1, "-label"))
1017 {
1018 FREE(&env->x_label);
1019 env->x_label = mutt_str_dup(body);
1020 matched = true;
1021 }
1022#ifdef USE_NNTP
1023 else if (mutt_istr_equal(name + 1, "-comment-to"))
1024 {
1025 if (!env->x_comment_to)
1026 env->x_comment_to = mutt_str_dup(body);
1027 matched = true;
1028 }
1029 else if (mutt_istr_equal(name + 1, "ref"))
1030 {
1031 if (!env->xref)
1032 env->xref = mutt_str_dup(body);
1033 matched = true;
1034 }
1035#endif
1036 else if (mutt_istr_equal(name + 1, "-original-to"))
1037 {
1039 matched = true;
1040 }
1041 break;
1042
1043 default:
1044 break;
1045 }
1046
1047 /* Keep track of the user-defined headers */
1048 if (!matched && user_hdrs)
1049 {
1050 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1051 char *dup = NULL;
1052 mutt_str_asprintf(&dup, "%s: %s", name, body);
1053
1054 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1055 {
1056 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1057 if (do_2047)
1058 {
1059 rfc2047_decode(&np->data);
1060 }
1061 }
1062 else
1063 {
1064 FREE(&dup);
1065 }
1066 }
1067
1068 return matched;
1069}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1435
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition: atoi.c:227
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:203
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:457
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:31
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
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:496
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:622
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition: parse.c:67
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:424
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition: parse.c:529
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition: parse.c:280
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:313
static char * rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from a RFC2369 list.
Definition: parse.c:593
static void parse_content_language(const char *s, struct Body *ct)
Read the content's language.
Definition: parse.c:297
static void parse_content_disposition(const char *s, struct Body *ct)
Parse a content disposition.
Definition: parse.c:248
#define CONTENT_TOO_BIG
Definition: parse.c:56
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
char * description
content-description
Definition: body.h:55
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
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
List of recognised Timezones.
Definition: date.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc822_read_line()

char * mutt_rfc822_read_line ( FILE *  fp,
char *  line,
size_t *  linelen 
)

Read a header line from a file.

Parameters
fpFile to read from
lineBuffer to store the result
linelenLength of buffer
Return values
ptrLine read from file

Reads an arbitrarily long header field, and looks ahead for continuation lines. "line" must point to a dynamically allocated string; it is increased if more space is required to fit the whole line.

Definition at line 1082 of file parse.c.

1083{
1084 if (!fp || !line || !linelen)
1085 return NULL;
1086
1087 char *buf = line;
1088 int ch;
1089 size_t offset = 0;
1090
1091 while (true)
1092 {
1093 if (!fgets(buf, *linelen - offset, fp) || /* end of file or */
1094 (IS_SPACE(*line) && !offset)) /* end of headers */
1095 {
1096 *line = '\0';
1097 return line;
1098 }
1099
1100 const size_t len = mutt_str_len(buf);
1101 if (len == 0)
1102 return line;
1103
1104 buf += len - 1;
1105 if (*buf == '\n')
1106 {
1107 /* we did get a full line. remove trailing space */
1108 while (IS_SPACE(*buf))
1109 {
1110 *buf-- = '\0'; /* we can't come beyond line's beginning because
1111 * it begins with a non-space */
1112 }
1113
1114 /* check to see if the next line is a continuation line */
1115 ch = fgetc(fp);
1116 if ((ch != ' ') && (ch != '\t'))
1117 {
1118 ungetc(ch, fp);
1119 return line; /* next line is a separate header field or EOH */
1120 }
1121
1122 /* eat tabs and spaces from the beginning of the continuation line */
1123 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1124 ; // do nothing
1125
1126 ungetc(ch, fp);
1127 *++buf = ' '; /* string is still terminated because we removed
1128 at least one whitespace char above */
1129 }
1130
1131 buf++;
1132 offset = buf - line;
1133 if (*linelen < (offset + 256))
1134 {
1135 /* grow the buffer */
1136 *linelen += 256;
1137 mutt_mem_realloc(&line, *linelen);
1138 buf = line + offset;
1139 }
1140 }
1141 /* not reached */
1142}
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
+ 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 1157 of file parse.c.

1158{
1159 if (!fp)
1160 return NULL;
1161
1162 struct Envelope *env = mutt_env_new();
1163 char *p = NULL;
1164 LOFF_T loc;
1165 size_t linelen = 1024;
1166 char *line = mutt_mem_malloc(linelen);
1167 char buf[linelen + 1];
1168
1169 if (e)
1170 {
1171 if (!e->body)
1172 {
1173 e->body = mutt_body_new();
1174
1175 /* set the defaults from RFC1521 */
1176 e->body->type = TYPE_TEXT;
1177 e->body->subtype = mutt_str_dup("plain");
1178 e->body->encoding = ENC_7BIT;
1179 e->body->length = -1;
1180
1181 /* RFC2183 says this is arbitrary */
1183 }
1184 }
1185
1186 while ((loc = ftello(fp)) != -1)
1187 {
1188 line = mutt_rfc822_read_line(fp, line, &linelen);
1189 if (*line == '\0')
1190 break;
1191 p = strpbrk(line, ": \t");
1192 if (!p || (*p != ':'))
1193 {
1194 char return_path[1024] = { 0 };
1195 time_t t = 0;
1196
1197 /* some bogus MTAs will quote the original "From " line */
1198 if (mutt_str_startswith(line, ">From "))
1199 continue; /* just ignore */
1200 else if (is_from(line, return_path, sizeof(return_path), &t))
1201 {
1202 /* MH sometimes has the From_ line in the middle of the header! */
1203 if (e && (e->received == 0))
1204 e->received = t - mutt_date_local_tz(t);
1205 continue;
1206 }
1207
1208 (void) mutt_file_seek(fp, loc, SEEK_SET);
1209 break; /* end of header */
1210 }
1211
1212 *buf = '\0';
1213
1214 if (mutt_replacelist_match(&SpamList, buf, sizeof(buf), line))
1215 {
1216 if (!mutt_regexlist_match(&NoSpamList, line))
1217 {
1218 /* if spam tag already exists, figure out how to amend it */
1219 if ((!mutt_buffer_is_empty(&env->spam)) && (*buf != '\0'))
1220 {
1221 /* If `$spam_separator` defined, append with separator */
1222 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1223 if (c_spam_separator)
1224 {
1225 mutt_buffer_addstr(&env->spam, c_spam_separator);
1226 mutt_buffer_addstr(&env->spam, buf);
1227 }
1228 else /* overwrite */
1229 {
1230 mutt_buffer_reset(&env->spam);
1231 mutt_buffer_addstr(&env->spam, buf);
1232 }
1233 }
1234
1235 /* spam tag is new, and match expr is non-empty; copy */
1236 else if (mutt_buffer_is_empty(&env->spam) && (*buf != '\0'))
1237 {
1238 mutt_buffer_addstr(&env->spam, buf);
1239 }
1240
1241 /* match expr is empty; plug in null string if no existing tag */
1242 else if (mutt_buffer_is_empty(&env->spam))
1243 {
1244 mutt_buffer_addstr(&env->spam, "");
1245 }
1246
1247 if (!mutt_buffer_is_empty(&env->spam))
1248 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1249 }
1250 }
1251
1252 *p = '\0';
1253 p = mutt_str_skip_email_wsp(p + 1);
1254 if (*p == '\0')
1255 continue; /* skip empty header fields */
1256
1257 mutt_rfc822_parse_line(env, e, line, p, user_hdrs, weed, true);
1258 }
1259
1260 FREE(&line);
1261
1262 if (e)
1263 {
1264 e->body->hdr_offset = e->offset;
1265 e->body->offset = ftello(fp);
1266
1268
1269 if (env->subject)
1270 {
1271 regmatch_t pmatch[1];
1272
1273 const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex");
1274 if (mutt_regex_capture(c_reply_regex, env->subject, 1, pmatch))
1275 {
1276 env->real_subj = env->subject + pmatch[0].rm_eo;
1277 if (env->real_subj[0] == '\0')
1278 env->real_subj = NULL;
1279 }
1280 else
1281 env->real_subj = env->subject;
1282 }
1283
1284 if (e->received < 0)
1285 {
1286 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1287 e->received = 0;
1288 }
1289
1290 /* check for missing or invalid date */
1291 if (e->date_sent <= 0)
1292 {
1293 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1294 e->date_sent = e->received;
1295 }
1296
1297#ifdef USE_AUTOCRYPT
1298 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1299 if (c_autocrypt)
1300 {
1302 /* No sense in taking up memory after the header is processed */
1304 }
1305#endif
1306 }
1307
1308 return env;
1309}
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition: autocrypt.c:265
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:206
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
struct ReplaceList SpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:34
struct RegexList NoSpamList
List of regexes to identify non-spam emails.
Definition: globals.c:33
void mutt_autocrypthdr_free(struct AutocryptHeader **p)
Free an AutocryptHeader.
Definition: envelope.c:75
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: logging.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
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:614
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition: regex.c:495
char * mutt_rfc822_read_line(FILE *fp, char *line, size_t *linelen)
Read a header line from a file.
Definition: parse.c:1082
int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, const char *name, const char *body, bool user_hdrs, bool weed, bool do_2047)
Parse an email header.
Definition: parse.c:634
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:793
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:80
char * data
Pointer to data.
Definition: buffer.h:35
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_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 1317 of file parse.c.

1318{
1319 if (!fp)
1320 return NULL;
1321
1322 struct Body *p = mutt_body_new();
1323 struct Envelope *env = mutt_env_new();
1324 char *c = NULL;
1325 size_t linelen = 1024;
1326 char *line = mutt_mem_malloc(linelen);
1327 bool matched = false;
1328
1329 p->hdr_offset = ftello(fp);
1330
1331 p->encoding = ENC_7BIT; /* default from RFC1521 */
1332 p->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1334
1335 while (*(line = mutt_rfc822_read_line(fp, line, &linelen)) != 0)
1336 {
1337 /* Find the value of the current header */
1338 c = strchr(line, ':');
1339 if (c)
1340 {
1341 *c = '\0';
1342 c = mutt_str_skip_email_wsp(c + 1);
1343 if (*c == '\0')
1344 {
1345 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1346 continue;
1347 }
1348 }
1349 else
1350 {
1351 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1352 break;
1353 }
1354
1355 size_t plen = mutt_istr_startswith(line, "content-");
1356 if (plen != 0)
1357 {
1358 if (mutt_istr_equal("type", line + plen))
1360 else if (mutt_istr_equal("language", line + plen))
1362 else if (mutt_istr_equal("transfer-encoding", line + plen))
1364 else if (mutt_istr_equal("disposition", line + plen))
1366 else if (mutt_istr_equal("description", line + plen))
1367 {
1370 }
1371 else if (mutt_istr_equal("id", line + plen))
1372 {
1373 // strip <angle braces> from Content-ID: header
1374 char *id = c;
1375 int cid_len = mutt_str_len(c);
1376 if (cid_len > 2)
1377 {
1378 if (id[0] == '<')
1379 {
1380 id++;
1381 cid_len--;
1382 }
1383 if (id[cid_len - 1] == '>')
1384 id[cid_len - 1] = '\0';
1385 }
1386 mutt_param_set(&p->parameter, "content-id", id);
1387 }
1388 }
1389#ifdef SUN_ATTACHMENT
1390 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1391 {
1392 if (mutt_istr_equal("data-type", line + plen))
1394 else if (mutt_istr_equal("encoding-info", line + plen))
1396 else if (mutt_istr_equal("content-lines", line + plen))
1397 mutt_param_set(&p->parameter, "content-lines", c);
1398 else if (mutt_istr_equal("data-description", line + plen))
1399 {
1402 }
1403 }
1404#endif
1405 else
1406 {
1407 if (mutt_rfc822_parse_line(env, NULL, line, c, false, false, false))
1408 {
1409 matched = true;
1410 }
1411 }
1412 }
1413 p->offset = ftello(fp); /* Mark the start of the real data */
1414 if ((p->type == TYPE_TEXT) && !p->subtype)
1415 p->subtype = mutt_str_dup("plain");
1416 else if ((p->type == TYPE_MESSAGE) && !p->subtype)
1417 p->subtype = mutt_str_dup("rfc822");
1418
1419 FREE(&line);
1420
1421 if (matched)
1422 {
1423 p->mime_headers = env;
1425 }
1426 else
1427 {
1428 mutt_env_free(&env);
1429 }
1430
1431 return p;
1432}
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
+ 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 1441 of file parse.c.

1442{
1443 if (type != TYPE_MESSAGE)
1444 return false;
1445
1446 subtype = NONULL(subtype);
1447 return (mutt_istr_equal(subtype, "rfc822") ||
1448 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1449}
#define NONULL(x)
Definition: string2.h:37
+ 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 1670 of file parse.c.

1671{
1672 if (!env || !src)
1673 return false;
1674
1675 struct Url *url = url_parse(src);
1676 if (!url)
1677 return false;
1678
1679 if (url->host)
1680 {
1681 /* this is not a path-only URL */
1682 url_free(&url);
1683 return false;
1684 }
1685
1686 mutt_addrlist_parse(&env->to, url->path);
1687
1688 struct UrlQuery *np;
1689 STAILQ_FOREACH(np, &url->query_strings, entries)
1690 {
1691 const char *tag = np->name;
1692 char *value = np->value;
1693 /* Determine if this header field is on the allowed list. Since NeoMutt
1694 * interprets some header fields specially (such as
1695 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1696 * only safe fields are allowed.
1697 *
1698 * RFC2368, "4. Unsafe headers"
1699 * The user agent interpreting a mailto URL SHOULD choose not to create
1700 * a message if any of the headers are considered dangerous; it may also
1701 * choose to create a message with only a subset of the headers given in
1702 * the URL. */
1703 if (mutt_list_match(tag, &MailToAllow))
1704 {
1705 if (mutt_istr_equal(tag, "body"))
1706 {
1707 if (body)
1708 mutt_str_replace(body, value);
1709 }
1710 else
1711 {
1712 char *scratch = NULL;
1713 size_t taglen = mutt_str_len(tag);
1714
1715 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1716 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1717 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1718 mutt_rfc822_parse_line(env, NULL, scratch, value, true, false, true);
1719 FREE(&scratch);
1720 }
1721 }
1722 }
1723
1724 /* RFC2047 decode after the RFC822 parsing */
1726
1727 url_free(&url);
1728 return true;
1729}
struct ListHead MailToAllow
List of permitted fields in a mailto: url.
Definition: globals.c:37
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
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:234
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_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 1736 of file parse.c.

1737{
1738 int counter = 0;
1739
1740 parse_part(fp, b, &counter);
1741}
+ 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 1751 of file parse.c.

1752{
1753 int counter = 0;
1754
1755 return rfc822_parse_message(fp, parent, &counter);
1756}
+ 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 1767 of file parse.c.

1768{
1769 int counter = 0;
1770
1771 return parse_multipart(fp, boundary, end_off, digest, &counter);
1772}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: