NeoMutt  2025-12-11-1009-ga75d9e
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 "alias/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.
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 char * extract_message_id (const char *s, size_t *len)
 Find a message-id in a string.
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_value (const char *body, bool mailto_only)
 Extract the first useful URL from an RFC 2369 list.
size_t mutt_rfc2369_parse (const char *body, struct ListHead *links)
 Extract URLs from an RFC 2369 list header.
char * mutt_rfc2369_first_mailto (const char *body)
 Extract the first mailto: URL from an RFC 2369 list.
static void rfc2369_list_headers_ensure_init (struct Rfc2369ListHeaders *headers)
 Initialise RFC 2369 header lists.
void mutt_rfc2369_list_headers_free (struct Rfc2369ListHeaders *headers)
 Free RFC 2369 mailing-list header values.
void mutt_rfc2369_read_headers (FILE *fp, struct Rfc2369ListHeaders *headers)
 Read RFC 2369 mailing-list headers.
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 62 of file parse.c.

Function Documentation

◆ parse_part()

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

1669{
1670 if (!fp || !b)
1671 return;
1672
1673 const char *bound = NULL;
1674 static unsigned short recurse_level = 0;
1675
1676 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1677 {
1678 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up\n");
1679 return;
1680 }
1681 recurse_level++;
1682
1683 switch (b->type)
1684 {
1685 case TYPE_MULTIPART:
1686 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1687 bound = "--------";
1688 else
1689 bound = mutt_param_get(&b->parameter, "boundary");
1690
1691 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1692 {
1693 goto bail;
1694 }
1695 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1696 mutt_istr_equal("digest", b->subtype), counter);
1697 break;
1698
1699 case TYPE_MESSAGE:
1700 if (!b->subtype)
1701 break;
1702
1703 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1704 {
1705 goto bail;
1706 }
1707 if (mutt_is_message_type(b->type, b->subtype))
1708 b->parts = rfc822_parse_message(fp, b, counter);
1709 else if (mutt_istr_equal(b->subtype, "external-body"))
1710 b->parts = mutt_read_mime_header(fp, 0);
1711 else
1712 goto bail;
1713 break;
1714
1715 default:
1716 goto bail;
1717 }
1718
1719 /* try to recover from parsing error */
1720 if (!b->parts)
1721 {
1722 b->type = TYPE_TEXT;
1723 mutt_str_replace(&b->subtype, "plain");
1724 }
1725bail:
1726 recurse_level--;
1727}
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1516
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1851
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:1739
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1652
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
#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:678
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()

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

1852{
1853 if (!fp || !parent)
1854 return NULL;
1855
1856 parent->email = email_new();
1857 parent->email->offset = ftello(fp);
1858 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1859 struct Body *msg = parent->email->body;
1860
1861 /* ignore the length given in the content-length since it could be wrong
1862 * and we already have the info to calculate the correct length */
1863 /* if (msg->length == -1) */
1864 msg->length = parent->length - (msg->offset - parent->offset);
1865
1866 /* if body of this message is empty, we can end up with a negative length */
1867 if (msg->length < 0)
1868 msg->length = 0;
1869
1870 parse_part(fp, msg, counter);
1871 return msg;
1872}
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:1357
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1668
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()

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

1741{
1742 if (!fp)
1743 return NULL;
1744
1745 if (!boundary)
1746 {
1747 mutt_error(_("multipart message has no boundary parameter"));
1748 return NULL;
1749 }
1750
1751 char buf[1024] = { 0 };
1752 struct Body *head = NULL;
1753 struct Body *last = NULL;
1754 struct Body *new_body = NULL;
1755 bool final = false; /* did we see the ending boundary? */
1756
1757 const size_t blen = mutt_str_len(boundary);
1758 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1759 {
1760 const size_t len = mutt_str_len(buf);
1761
1762 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1763
1764 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1765 {
1766 if (last)
1767 {
1768 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1769 if (last->parts && (last->parts->length == 0))
1770 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1771 /* if the body is empty, we can end up with a -1 length */
1772 if (last->length < 0)
1773 last->length = 0;
1774 }
1775
1776 if (len > 0)
1777 {
1778 /* Remove any trailing whitespace, up to the length of the boundary */
1779 for (size_t i = len - 1; mutt_isspace(buf[i]) && (i >= (blen + 2)); i--)
1780 buf[i] = '\0';
1781 }
1782
1783 /* Check for the end boundary */
1784 if (mutt_str_equal(buf + blen + 2, "--"))
1785 {
1786 final = true;
1787 break; /* done parsing */
1788 }
1789 else if (buf[2 + blen] == '\0')
1790 {
1791 new_body = mutt_read_mime_header(fp, digest);
1792 if (!new_body)
1793 break;
1794
1795 if (mutt_param_get(&new_body->parameter, "content-lines"))
1796 {
1797 int lines = 0;
1798 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1799 for (; lines > 0; lines--)
1800 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1801 break;
1802 }
1803
1804 /* Consistency checking - catch bad attachment end boundaries */
1805 if (new_body->offset > end_off)
1806 {
1807 mutt_body_free(&new_body);
1808 break;
1809 }
1810 if (head)
1811 {
1812 last->next = new_body;
1813 last = new_body;
1814 }
1815 else
1816 {
1817 last = new_body;
1818 head = new_body;
1819 }
1820
1821 /* It seems more intuitive to add the counter increment to
1822 * parse_part(), but we want to stop the case where a multipart
1823 * contains thousands of tiny parts before the memory and data
1824 * structures are allocated. */
1825 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1826 break;
1827 }
1828 }
1829 }
1830
1831 /* in case of missing end boundary, set the length to something reasonable */
1832 if (last && (last->length == 0) && !final)
1833 last->length = end_off - last->offset;
1834
1835 /* parse recursive MIME parts */
1836 for (last = head; last; last = last->next)
1837 parse_part(fp, last, counter);
1838
1839 return head;
1840}
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:666
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:503
struct Body * next
next attachment in the list
Definition body.h:72
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_filter_commandline_header_tag()

void mutt_filter_commandline_header_tag ( char * header)

Sanitise characters in a header tag.

Parameters
headerString to sanitise

Definition at line 73 of file parse.c.

74{
75 if (!header)
76 return;
77
78 for (; (*header != '\0'); header++)
79 {
80 if ((*header < 33) || (*header > 126) || (*header == ':'))
81 *header = '?';
82 }
83}
Here is the caller graph for this function:

◆ mutt_filter_commandline_header_value()

void mutt_filter_commandline_header_value ( char * header)

Sanitise characters in a header value.

Parameters
headerString to sanitise

It might be preferable to use mutt_filter_unprintable() instead. This filter is being lax, but preventing a header injection via an embedded newline.

Definition at line 93 of file parse.c.

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

◆ parse_parameters()

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

117{
118 struct Parameter *pnew = NULL;
119 const char *p = NULL;
120 size_t i;
121
122 struct Buffer *buf = buf_pool_get();
123 /* allow_value_spaces, especially with autocrypt keydata, can result
124 * in quite large parameter values. avoid frequent reallocs by
125 * pre-sizing */
126 if (allow_value_spaces)
127 buf_alloc(buf, mutt_str_len(s));
128
129 mutt_debug(LL_DEBUG2, "'%s'\n", s);
130
131 const bool assumed = !slist_is_empty(cc_assumed_charset());
132 while (*s)
133 {
134 buf_reset(buf);
135
136 p = strpbrk(s, "=;");
137 if (!p)
138 {
139 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
140 goto bail;
141 }
142
143 /* if we hit a ; now the parameter has no value, just skip it */
144 if (*p != ';')
145 {
146 i = p - s;
147 /* remove whitespace from the end of the attribute name */
148 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
149 i--;
150
151 /* the check for the missing parameter token is here so that we can skip
152 * over any quoted value that may be present. */
153 if (i == 0)
154 {
155 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
156 pnew = NULL;
157 }
158 else
159 {
160 pnew = mutt_param_new();
161 pnew->attribute = mutt_strn_dup(s, i);
162 }
163
164 do
165 {
166 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
167
168 if (*s == '"')
169 {
170 bool state_ascii = true;
171 s++;
172 for (; *s; s++)
173 {
174 if (assumed)
175 {
176 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
177 if (*s == 0x1b)
178 {
179 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
180 state_ascii = true;
181 else
182 state_ascii = false;
183 }
184 }
185 if (state_ascii && (*s == '"'))
186 break;
187 if (*s == '\\')
188 {
189 if (s[1])
190 {
191 s++;
192 /* Quote the next character */
193 buf_addch(buf, *s);
194 }
195 }
196 else
197 {
198 buf_addch(buf, *s);
199 }
200 }
201 if (*s)
202 s++; /* skip over the " */
203 }
204 else
205 {
206 for (; *s && *s != ' ' && *s != ';'; s++)
207 buf_addch(buf, *s);
208 }
209
210 p = s;
211 } while (allow_value_spaces && (*s == ' '));
212
213 /* if the attribute token was missing, 'new' will be NULL */
214 if (pnew)
215 {
216 pnew->value = buf_strdup(buf);
217
218 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
219 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
220
221 /* Add this parameter to the list */
222 TAILQ_INSERT_HEAD(pl, pnew, entries);
223 }
224 }
225 else
226 {
227 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
228 s = p;
229 }
230
231 /* Find the next parameter */
232 if ((*s != ';') && !(s = strchr(s, ';')))
233 break; /* no more parameters */
234
235 do
236 {
237 /* Move past any leading whitespace. the +1 skips over the semicolon */
238 s = mutt_str_skip_email_wsp(s + 1);
239 } while (*s == ';'); /* skip empty parameters */
240 }
241
242bail:
243
245 buf_pool_release(&buf);
246}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:342
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:614
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:242
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()

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

256{
257 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
258
259 if (mutt_istr_startswith(s, "inline"))
261 else if (mutt_istr_startswith(s, "form-data"))
263 else
265
266 /* Check to see if a default filename was given */
267 s = strchr(s, ';');
268 if (s)
269 {
270 s = mutt_str_skip_email_wsp(s + 1);
271 parse_parameters(&pl, s, false);
272 s = mutt_param_get(&pl, "filename");
273 if (s)
275 s = mutt_param_get(&pl, "name");
276 if (s)
278 mutt_param_free(&pl);
279 }
280}
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition parse.c:116
@ 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:

◆ extract_message_id()

char * extract_message_id ( const char * s,
size_t * len )
static

Find a message-id in a string.

Parameters
[in]sString to parse
[out]lenNumber of bytes of s parsed (optional)
Return values
ptrMessage id found
NULLNo more message ids

len is an offset into s, so the caller may advance s by it to find the next message-id. The string is scanned verbatim; decoding, if any, is the caller's responsibility.

Note
Caller must free returned string

Definition at line 295 of file parse.c.

296{
297 if (!s)
298 return NULL;
299
300 for (const char *p = s, *beg = NULL; *p; p++)
301 {
302 if (*p == '<')
303 {
304 beg = p;
305 continue;
306 }
307
308 if (beg && (*p == '>'))
309 {
310 if (len)
311 *len = p - s + 1;
312 return mutt_strn_dup(beg, (p + 1) - beg);
313 }
314 }
315
316 return NULL;
317}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_references()

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

325{
326 if (!head)
327 return;
328
329 /* Decode once: mutt_extract_message_id() returns an offset into the decoded
330 * text, so the search and the advance must happen on the same string.
331 * Decoding can lengthen the text (e.g. a single-byte charset expanding to
332 * UTF-8), so applying that offset to the raw header would run off the end. */
333 char *decoded = mutt_str_dup(s);
334 rfc2047_decode(&decoded);
335
336 char *m = NULL;
337 const char *p = decoded;
338 for (size_t off = 0; (m = extract_message_id(p, &off)); p += off)
339 {
340 mutt_list_insert_head(head, m);
341 }
342
343 FREE(&decoded);
344}
static char * extract_message_id(const char *s, size_t *len)
Find a message-id in a string.
Definition parse.c:295
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
#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:679
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_content_language()

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

352{
353 if (!s || !b)
354 return;
355
356 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
358}
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 367 of file parse.c.

368{
370 ASSERT(mod_data);
371
372 return mutt_list_match(s, &mod_data->ignore) && !mutt_list_match(s, &mod_data->unignore);
373}
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes).
Definition list.c:197
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
#define ASSERT(COND)
Definition signal2.h:59
Email private Module data.
Definition module_data.h:32
struct ListHead unignore
Header patterns to unignore.
Definition module_data.h:43
struct ListHead ignore
Header patterns to ignore.
Definition module_data.h:37
Container for Accounts, Notifications.
Definition neomutt.h:41
Here is the call graph for this function:
Here is the caller graph for this function:

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

381{
382 if (mutt_istr_equal("text", s))
383 return TYPE_TEXT;
384 if (mutt_istr_equal("multipart", s))
385 return TYPE_MULTIPART;
386 if (mutt_istr_equal("x-sun-attachment", s))
387 return TYPE_MULTIPART;
388 if (mutt_istr_equal("application", s))
389 return TYPE_APPLICATION;
390 if (mutt_istr_equal("message", s))
391 return TYPE_MESSAGE;
392 if (mutt_istr_equal("image", s))
393 return TYPE_IMAGE;
394 if (mutt_istr_equal("audio", s))
395 return TYPE_AUDIO;
396 if (mutt_istr_equal("video", s))
397 return TYPE_VIDEO;
398 if (mutt_istr_equal("model", s))
399 return TYPE_MODEL;
400 if (mutt_istr_equal("*", s))
401 return TYPE_ANY;
402 if (mutt_istr_equal(".*", s))
403 return TYPE_ANY;
404
405 return TYPE_OTHER;
406}
@ 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 (optional)
Return values
ptrMessage id found
NULLNo more message ids
Note
Caller must free returned string

Definition at line 417 of file parse.c.

418{
419 if (!s || (*s == '\0'))
420 return NULL;
421
422 char *decoded = mutt_str_dup(s);
423 rfc2047_decode(&decoded);
424
425 char *res = extract_message_id(decoded, len);
426
427 FREE(&decoded);
428 return res;
429}
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 436 of file parse.c.

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

WARNING: this function modifies the 's' argument to segregate type, subtype, and parametners, so, e.g., a param of "text/plain" will be changed into "text\0plain".

Definition at line 465 of file parse.c.

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

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

575{
576 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
577 autocrypt->next = head;
578
579 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
580 parse_parameters(&pl, s, true);
581 if (TAILQ_EMPTY(&pl))
582 {
583 autocrypt->invalid = true;
584 goto cleanup;
585 }
586
587 struct Parameter *p = NULL;
588 TAILQ_FOREACH(p, &pl, entries)
589 {
590 if (mutt_istr_equal(p->attribute, "addr"))
591 {
592 if (autocrypt->addr)
593 {
594 autocrypt->invalid = true;
595 goto cleanup;
596 }
597 autocrypt->addr = p->value;
598 p->value = NULL;
599 }
600 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
601 {
602 if (mutt_istr_equal(p->value, "mutual"))
603 autocrypt->prefer_encrypt = true;
604 }
605 else if (mutt_istr_equal(p->attribute, "keydata"))
606 {
607 if (autocrypt->keydata)
608 {
609 autocrypt->invalid = true;
610 goto cleanup;
611 }
612 autocrypt->keydata = p->value;
613 p->value = NULL;
614 }
615 else if (p->attribute && (p->attribute[0] != '_'))
616 {
617 autocrypt->invalid = true;
618 goto cleanup;
619 }
620 }
621
622 /* Checking the addr against From, and for multiple valid headers
623 * occurs later, after all the headers are parsed. */
624 if (!autocrypt->addr || !autocrypt->keydata)
625 autocrypt->invalid = true;
626
627cleanup:
628 mutt_param_free(&pl);
629 return autocrypt;
630}
struct AutocryptHeader * mutt_autocrypthdr_new(void)
Create a new AutocryptHeader.
Definition envelope.c:94
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_EMPTY(head)
Definition queue.h:778
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_value()

char * rfc2369_first_value ( const char * body,
bool mailto_only )
static

Extract the first useful URL from an RFC 2369 list.

Parameters
bodyBody of the header
mailto_onlyIf true, only return mailto: URLs
Return values
ptrFirst matching URL found, or NULL if none was found

Definition at line 639 of file parse.c.

640{
641 struct ListHead links = { 0 };
642 char *value = NULL;
643
644 mutt_rfc2369_parse(body, &links);
645
646 struct ListNode *np = NULL;
647 STAILQ_FOREACH(np, &links, entries)
648 {
649 if (mutt_istr_startswith(np->data, "mailto:"))
650 {
651 FREE(&value);
652 value = mutt_str_dup(np->data);
653 break;
654 }
655
656 if (!mailto_only && !value)
657 value = mutt_str_dup(np->data);
658 }
659
660 mutt_list_free(&links);
661 return value;
662}
size_t mutt_rfc2369_parse(const char *body, struct ListHead *links)
Extract URLs from an RFC 2369 list header.
Definition parse.c:670
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_parse()

size_t mutt_rfc2369_parse ( const char * body,
struct ListHead * links )

Extract URLs from an RFC 2369 list header.

Parameters
bodyBody of the header
linksList of extracted URLs
Return values
numNumber of URLs extracted

Definition at line 670 of file parse.c.

671{
672 if (!body || !links)
673 return 0;
674
675 if (!links->stqh_last)
676 STAILQ_INIT(links);
677
678 size_t count = 0;
679 for (const char *beg = body; (beg = strchr(beg, '<')) != NULL; beg++)
680 {
681 beg++;
682
683 const char *end = strchr(beg, '>');
684 if (!end)
685 break;
686
687 if (end > beg)
688 {
689 mutt_list_insert_tail(links, mutt_strn_dup(beg, end - beg));
690 count++;
691 }
692
693 beg = end;
694 }
695
696 return count;
697}
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
#define STAILQ_INIT(head)
Definition queue.h:410
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_first_mailto()

char * mutt_rfc2369_first_mailto ( const char * body)

Extract the first mailto: URL from an RFC 2369 list.

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

Definition at line 704 of file parse.c.

705{
706 return rfc2369_first_value(body, true);
707}
static char * rfc2369_first_value(const char *body, bool mailto_only)
Extract the first useful URL from an RFC 2369 list.
Definition parse.c:639
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2369_list_headers_ensure_init()

void rfc2369_list_headers_ensure_init ( struct Rfc2369ListHeaders * headers)
static

Initialise RFC 2369 header lists.

Parameters
headersHeaders to initialise

Definition at line 713 of file parse.c.

714{
715 if (!headers)
716 return;
717
718 if (!headers->archive.stqh_last)
719 STAILQ_INIT(&headers->archive);
720 if (!headers->help.stqh_last)
721 STAILQ_INIT(&headers->help);
722 if (!headers->owner.stqh_last)
723 STAILQ_INIT(&headers->owner);
724 if (!headers->post.stqh_last)
725 STAILQ_INIT(&headers->post);
726 if (!headers->subscribe.stqh_last)
727 STAILQ_INIT(&headers->subscribe);
728 if (!headers->unsubscribe.stqh_last)
729 STAILQ_INIT(&headers->unsubscribe);
730}
struct ListHead owner
List-Owner.
Definition parse.h:44
struct ListHead archive
List-Archive.
Definition parse.h:42
struct ListHead post
List-Post.
Definition parse.h:45
struct ListHead unsubscribe
List-Unsubscribe.
Definition parse.h:47
struct ListHead subscribe
List-Subscribe.
Definition parse.h:46
struct ListHead help
List-Help.
Definition parse.h:43
Here is the caller graph for this function:

◆ mutt_rfc2369_list_headers_free()

void mutt_rfc2369_list_headers_free ( struct Rfc2369ListHeaders * headers)

Free RFC 2369 mailing-list header values.

Parameters
headersHeaders to free

Definition at line 736 of file parse.c.

737{
738 if (!headers)
739 return;
740
742
743 mutt_list_free(&headers->archive);
744 mutt_list_free(&headers->help);
745 mutt_list_free(&headers->owner);
746 mutt_list_free(&headers->post);
747 mutt_list_free(&headers->subscribe);
748 mutt_list_free(&headers->unsubscribe);
749}
static void rfc2369_list_headers_ensure_init(struct Rfc2369ListHeaders *headers)
Initialise RFC 2369 header lists.
Definition parse.c:713
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_rfc2369_read_headers()

void mutt_rfc2369_read_headers ( FILE * fp,
struct Rfc2369ListHeaders * headers )

Read RFC 2369 mailing-list headers.

Parameters
fpMessage stream positioned at the start of the headers
headersParsed header values

Definition at line 756 of file parse.c.

757{
758 if (!fp || !headers)
759 return;
760
763
764 struct Buffer *line = buf_pool_get();
765 while (true)
766 {
767 size_t len = mutt_rfc822_read_line(fp, line);
768 if ((len == 0) || buf_is_empty(line))
769 break;
770
771 const char *lines = buf_string(line);
772 const char *p = strpbrk(lines, ": \t");
773 if (!p || (*p != ':'))
774 {
775 time_t t = 0;
776
777 /* Some MTAs quote the original mbox separator. */
778 if (mutt_str_startswith(lines, ">From "))
779 continue;
780 if (is_from(lines, NULL, 0, &t))
781 continue;
782
783 break;
784 }
785
786 const size_t name_len = p - lines;
787 const char *body = mutt_str_skip_whitespace(p + 1);
788 if (*body == '\0')
789 continue;
790
791 struct ListHead *value = NULL;
792 if ((name_len == 12) && eqi12(lines, "List-Archive"))
793 value = &headers->archive;
794 else if ((name_len == 9) && eqi9(lines, "List-Help"))
795 value = &headers->help;
796 else if ((name_len == 10) && eqi10(lines, "List-Owner"))
797 value = &headers->owner;
798 else if ((name_len == 9) && eqi9(lines, "List-Post"))
799 value = &headers->post;
800 else if ((name_len == 14) && eqi14(lines, "List-Subscribe"))
801 value = &headers->subscribe;
802 else if ((name_len == 16) && eqi16(lines, "List-Unsubscribe"))
803 value = &headers->unsubscribe;
804
805 if (!value)
806 continue;
807
808 mutt_list_free(value);
809 mutt_rfc2369_parse(body, value);
810 }
811 buf_pool_release(&line);
812}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition parse.c:1277
void mutt_rfc2369_list_headers_free(struct Rfc2369ListHeaders *headers)
Free RFC 2369 mailing-list header values.
Definition parse.c:736
static bool eqi9(const char *a, const char b[9])
eqi9 - Compare two 9-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:162
static bool eqi10(const char *a, const char b[10])
eqi10 - Compare two 10-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:168
static bool eqi14(const char *a, const char b[14])
eqi14 - Compare two 14-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:192
static bool eqi12(const char *a, const char b[12])
eqi12 - Compare two 12-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:180
static bool eqi16(const char *a, const char b[16])
eqi16 - Compare two 16-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:204
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition string.c:557
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 830 of file parse.c.

833{
834 if (!env || !name)
835 return 0;
836
837 bool matched = false;
838
839 switch (name[0] | 0x20)
840 {
841 case 'a':
842 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
843 {
844 mutt_addrlist_parse(&env->to, body);
845 matched = true;
846 }
847 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
848 {
849 mutt_addrlist_parse(&env->from, body);
850 matched = true;
851 }
852#ifdef USE_AUTOCRYPT
853 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
854 {
855 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
856 if (c_autocrypt)
857 {
858 env->autocrypt = parse_autocrypt(env->autocrypt, body);
859 matched = true;
860 }
861 }
862 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
863 {
864 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
865 if (c_autocrypt)
866 {
868 matched = true;
869 }
870 }
871#endif
872 break;
873
874 case 'b':
875 if ((name_len == 3) && eqi2(name + 1, "cc"))
876 {
877 mutt_addrlist_parse(&env->bcc, body);
878 matched = true;
879 }
880 break;
881
882 case 'c':
883 if ((name_len == 2) && eqi1(name + 1, "c"))
884 {
885 mutt_addrlist_parse(&env->cc, body);
886 matched = true;
887 }
888 else
889 {
890 if ((name_len >= 12) && eqi8(name, "content-"))
891 {
892 if ((name_len == 12) && eqi4(name + 8, "type"))
893 {
894 if (e)
896 matched = true;
897 }
898 else if ((name_len == 16) && eqi8(name + 8, "language"))
899 {
900 if (e)
902 matched = true;
903 }
904 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
905 {
906 if (e)
908 matched = true;
909 }
910 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
911 {
912 if (e)
913 {
914 unsigned long len = 0;
915 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
916 }
917 matched = true;
918 }
919 else if ((name_len == 19) && eqi11(name + 8, "description"))
920 {
921 if (e)
922 {
925 }
926 matched = true;
927 }
928 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
929 {
930 if (e)
932 matched = true;
933 }
934 }
935 }
936 break;
937
938 case 'd':
939 if ((name_len != 4) || !eqi4(name, "date"))
940 break;
941
942 mutt_str_replace(&env->date, body);
943 if (e)
944 {
945 struct Tz tz = { 0 };
946 // the caller will check e->date_sent for -1
947 e->date_sent = mutt_date_parse_date(body, &tz);
948 if (e->date_sent > 0)
949 {
950 e->zhours = tz.zhours;
951 e->zminutes = tz.zminutes;
952 e->zoccident = tz.zoccident;
953 }
954 }
955 matched = true;
956 break;
957
958 case 'e':
959 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
960 {
961 const time_t expired = mutt_date_parse_date(body, NULL);
962 if ((expired != -1) && (expired < mutt_date_now()))
963 {
964 e->expired = true;
965 }
966 }
967 break;
968
969 case 'f':
970 if ((name_len == 4) && eqi4(name, "from"))
971 {
972 mutt_addrlist_parse(&env->from, body);
973 matched = true;
974 }
975 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
976 {
977 if (!env->followup_to)
978 {
981 }
982 matched = true;
983 }
984 break;
985
986 case 'i':
987 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
988 break;
989
991 char *body2 = mutt_str_dup(body); // Create a mutable copy
993 parse_references(&env->in_reply_to, body2);
994 FREE(&body2);
995 matched = true;
996 break;
997
998 case 'l':
999 if ((name_len == 5) && eqi4(name + 1, "ines"))
1000 {
1001 if (e)
1002 {
1003 unsigned int ui = 0; // we don't want a negative number of lines
1004 mutt_str_atoui(body, &ui);
1005 e->lines = ui;
1006 }
1007
1008 matched = true;
1009 }
1010 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
1011 {
1012 /* RFC2369 */
1013 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
1014 {
1015 char *mailto = mutt_rfc2369_first_mailto(body);
1016 if (mailto)
1017 {
1018 FREE(&env->list_post);
1019 env->list_post = mailto;
1020 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
1021 if (c_auto_subscribe)
1023 }
1024 }
1025 matched = true;
1026 }
1027 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
1028 {
1029 /* RFC2369 */
1030 char *uri = rfc2369_first_value(body, false);
1031 if (uri)
1032 {
1033 FREE(&env->list_subscribe);
1034 env->list_subscribe = uri;
1035 }
1036 matched = true;
1037 }
1038 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
1039 {
1040 /* RFC2369 */
1041 char *uri = rfc2369_first_value(body, false);
1042 if (uri)
1043 {
1044 FREE(&env->list_unsubscribe);
1045 env->list_unsubscribe = uri;
1046 }
1047 matched = true;
1048 }
1049 break;
1050
1051 case 'm':
1052 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
1053 {
1054 if (e)
1055 e->mime = true;
1056 matched = true;
1057 }
1058 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
1059 {
1060 /* We add a new "Message-ID:" when building a message */
1061 FREE(&env->message_id);
1062 env->message_id = mutt_extract_message_id(body, NULL);
1063 matched = true;
1064 }
1065 else
1066 {
1067 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
1068 {
1069 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
1070 {
1071 /* override the Reply-To: field */
1073 mutt_addrlist_parse(&env->reply_to, body);
1074 matched = true;
1075 }
1076 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
1077 {
1079 matched = true;
1080 }
1081 }
1082 }
1083 break;
1084
1085 case 'n':
1086 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
1087 {
1088 FREE(&env->newsgroups);
1091 matched = true;
1092 }
1093 break;
1094
1095 case 'o':
1096 /* field 'Organization:' saves only for pager! */
1097 if ((name_len == 12) && eqi11(name + 1, "rganization"))
1098 {
1099 if (!env->organization && !mutt_istr_equal(body, "unknown"))
1100 env->organization = mutt_str_dup(body);
1101 }
1102 break;
1103
1104 case 'r':
1105 if ((name_len == 10) && eqi9(name + 1, "eferences"))
1106 {
1108 parse_references(&env->references, body);
1109 matched = true;
1110 }
1111 else if ((name_len == 8) && eqi8(name, "reply-to"))
1112 {
1113 mutt_addrlist_parse(&env->reply_to, body);
1114 matched = true;
1115 }
1116 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
1117 {
1118 mutt_addrlist_parse(&env->return_path, body);
1119 matched = true;
1120 }
1121 else if ((name_len == 8) && eqi8(name, "received"))
1122 {
1123 if (e && (e->received == 0))
1124 {
1125 const char *d = strrchr(body, ';');
1126 if (d)
1127 {
1128 d = mutt_str_skip_email_wsp(d + 1);
1129 // the caller will check e->received for -1
1130 e->received = mutt_date_parse_date(d, NULL);
1131 }
1132 }
1133 }
1134 break;
1135
1136 case 's':
1137 if ((name_len == 7) && eqi6(name + 1, "ubject"))
1138 {
1139 if (!env->subject)
1140 mutt_env_set_subject(env, body);
1141 matched = true;
1142 }
1143 else if ((name_len == 6) && eqi5(name + 1, "ender"))
1144 {
1145 mutt_addrlist_parse(&env->sender, body);
1146 matched = true;
1147 }
1148 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
1149 {
1150 if (e)
1151 {
1152 while (*body)
1153 {
1154 switch (*body)
1155 {
1156 case 'O':
1157 {
1158 e->old = true;
1159 break;
1160 }
1161 case 'R':
1162 e->read = true;
1163 break;
1164 case 'r':
1165 e->replied = true;
1166 break;
1167 }
1168 body++;
1169 }
1170 }
1171 matched = true;
1172 }
1173 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1174 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1175 {
1176 FREE(&env->supersedes);
1177 env->supersedes = mutt_str_dup(body);
1178 }
1179 break;
1180
1181 case 't':
1182 if ((name_len == 2) && eqi1(name + 1, "o"))
1183 {
1184 mutt_addrlist_parse(&env->to, body);
1185 matched = true;
1186 }
1187 break;
1188
1189 case 'x':
1190 if ((name_len == 8) && eqi8(name, "x-status"))
1191 {
1192 if (e)
1193 {
1194 while (*body)
1195 {
1196 switch (*body)
1197 {
1198 case 'A':
1199 e->replied = true;
1200 break;
1201 case 'D':
1202 e->deleted = true;
1203 break;
1204 case 'F':
1205 e->flagged = true;
1206 break;
1207 default:
1208 break;
1209 }
1210 body++;
1211 }
1212 }
1213 matched = true;
1214 }
1215 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1216 {
1217 FREE(&env->x_label);
1218 env->x_label = mutt_str_dup(body);
1219 matched = true;
1220 }
1221 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1222 {
1223 if (!env->x_comment_to)
1224 env->x_comment_to = mutt_str_dup(body);
1225 matched = true;
1226 }
1227 else if ((name_len == 4) && eqi4(name, "xref"))
1228 {
1229 if (!env->xref)
1230 env->xref = mutt_str_dup(body);
1231 matched = true;
1232 }
1233 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1234 {
1236 matched = true;
1237 }
1238 break;
1239
1240 default:
1241 break;
1242 }
1243
1244 /* Keep track of the user-defined headers */
1245 if (!matched && user_hdrs)
1246 {
1247 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1248 char *dup = NULL;
1249 mutt_str_asprintf(&dup, "%s: %s", name, body);
1250
1251 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1252 {
1253 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1254 if (do_2047)
1255 {
1256 rfc2047_decode(&np->data);
1257 }
1258 }
1259 else
1260 {
1261 FREE(&dup);
1262 }
1263 }
1264
1265 return matched;
1266}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1475
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:481
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition commands.c:49
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition atoi.c:243
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:465
char * mutt_rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from an RFC 2369 list.
Definition parse.c:704
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition parse.c:574
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition parse.c:324
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:367
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition parse.c:351
static void parse_content_disposition(const char *s, struct Body *b)
Parse a content disposition.
Definition parse.c:255
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:417
void mutt_filter_commandline_header_value(char *header)
Sanitise characters in a header value.
Definition parse.c:93
#define CONTENT_TOO_BIG
Maximum reasonable Content-Length value (1 GiB) to prevent overflow.
Definition parse.c:62
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
static bool eqi17(const char *a, const char b[17])
eqi17 - Compare two 17-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:210
static bool eqi8(const char *a, const char b[8])
Compare two 8-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:126
static bool eqi11(const char *a, const char b[11])
eqi11 - Compare two 11-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:174
static bool eqi6(const char *a, const char b[6])
eqi6 - Compare two 6-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:154
static bool eqi13(const char *a, const char b[13])
eqi13 - Compare two 13-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:186
static bool eqi4(const char *a, const char b[4])
Compare two 4-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:107
static bool eqi5(const char *a, const char b[5])
eqi5 - Compare two 5-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:148
static bool eqi15(const char *a, const char b[15])
eqi15 - Compare two 15-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:198
static bool eqi1(const char *a, const char b[1])
Compare two 1-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:78
static bool eqi2(const char *a, const char b[2])
Compare two 2-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:90
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:459
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:719
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:571
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:809
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 * 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 list URI, preferring mailto.
Definition envelope.h:68
struct AddressList return_path
Return path for the Email.
Definition envelope.h:58
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
struct AutocryptHeader * autocrypt_gossip
Autocrypt Gossip header.
Definition envelope.h:88
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList sender
Email's sender.
Definition envelope.h:63
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct AutocryptHeader * autocrypt
Autocrypt header.
Definition envelope.h:87
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
char * xref
List of cross-references.
Definition envelope.h:79
char * organization
Organisation header.
Definition envelope.h:77
char * x_label
X-Label.
Definition envelope.h:76
char * list_post
This stores a mailto URL, or nothing.
Definition envelope.h:67
char * date
Sent date.
Definition envelope.h:75
char * list_unsubscribe
This stores a list URI, preferring mailto.
Definition envelope.h:69
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
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 1277 of file parse.c.

1278{
1279 if (!fp || !buf)
1280 return 0;
1281
1282 size_t read = 0;
1283 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1284
1285 buf_reset(buf);
1286 while (true)
1287 {
1288 if (!fgets(line, sizeof(line), fp))
1289 {
1290 return 0;
1291 }
1292
1293 const size_t linelen = mutt_str_len(line);
1294 if (linelen == 0)
1295 {
1296 break;
1297 }
1298
1299 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1300 {
1301 read = linelen;
1302 break;
1303 }
1304
1305 read += linelen;
1306
1307 size_t off = linelen - 1;
1308 if (line[off] == '\n')
1309 {
1310 /* We did get a full line: remove trailing space */
1311 do
1312 {
1313 line[off] = '\0';
1314 } while (off && mutt_str_is_email_wsp(line[--off]));
1315
1316 /* check to see if the next line is a continuation line */
1317 int ch = fgetc(fp);
1318 if ((ch != ' ') && (ch != '\t'))
1319 {
1320 /* next line is a separate header field or EOH */
1321 ungetc(ch, fp);
1322 buf_addstr(buf, line);
1323 break;
1324 }
1325 read++;
1326
1327 /* eat tabs and spaces from the beginning of the continuation line */
1328 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1329 {
1330 read++;
1331 }
1332
1333 ungetc(ch, fp);
1334 line[off + 1] = ' '; /* string is still terminated because we removed
1335 at least one whitespace char above */
1336 }
1337
1338 buf_addstr(buf, line);
1339 }
1340
1341 return read;
1342}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
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 1357 of file parse.c.

1358{
1359 if (!fp)
1360 return NULL;
1361
1362 struct Envelope *env = mutt_env_new();
1363 char *p = NULL;
1364 LOFF_T loc = ftello(fp);
1365 if (loc < 0)
1366 {
1367 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1368 loc = 0;
1369 }
1370
1371 struct Buffer *line = buf_pool_get();
1372
1373 if (e)
1374 {
1375 if (!e->body)
1376 {
1377 e->body = mutt_body_new();
1378
1379 /* set the defaults from RFC1521 */
1380 e->body->type = TYPE_TEXT;
1381 e->body->subtype = mutt_str_dup("plain");
1382 e->body->encoding = ENC_7BIT;
1383 e->body->length = -1;
1384
1385 /* RFC2183 says this is arbitrary */
1387 }
1388 }
1389
1391 ASSERT(mod_data);
1392
1393 while (true)
1394 {
1395 LOFF_T line_start_loc = loc;
1396 size_t len = mutt_rfc822_read_line(fp, line);
1397 if (buf_is_empty(line))
1398 {
1399 break;
1400 }
1401 loc += len;
1402 const char *lines = buf_string(line);
1403 p = strpbrk(lines, ": \t");
1404 if (!p || (*p != ':'))
1405 {
1406 time_t t = 0;
1407
1408 /* some bogus MTAs will quote the original "From " line */
1409 if (mutt_str_startswith(lines, ">From "))
1410 {
1411 continue; /* just ignore */
1412 }
1413 else if (is_from(lines, NULL, 0, &t))
1414 {
1415 /* MH sometimes has the From_ line in the middle of the header! */
1416 if (e && (e->received == 0))
1417 e->received = t - mutt_date_local_tz(t);
1418 continue;
1419 }
1420
1421 /* We need to seek back to the start of the body. Note that we
1422 * keep track of loc ourselves, since calling ftello() incurs
1423 * a syscall, which can be expensive to do for every single line */
1424 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1425 break; /* end of header */
1426 }
1427 size_t name_len = p - lines;
1428
1429 char buf[1024] = { 0 };
1430 if (mutt_replacelist_match(&mod_data->spam, buf, sizeof(buf), lines))
1431 {
1432 if (!mutt_regexlist_match(&mod_data->no_spam, lines))
1433 {
1434 /* if spam tag already exists, figure out how to amend it */
1435 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1436 {
1437 /* If `$spam_separator` defined, append with separator */
1438 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1439 if (c_spam_separator)
1440 {
1441 buf_addstr(&env->spam, c_spam_separator);
1442 buf_addstr(&env->spam, buf);
1443 }
1444 else /* overwrite */
1445 {
1446 buf_reset(&env->spam);
1447 buf_addstr(&env->spam, buf);
1448 }
1449 }
1450 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1451 {
1452 /* spam tag is new, and match expr is non-empty; copy */
1453 buf_addstr(&env->spam, buf);
1454 }
1455 else if (buf_is_empty(&env->spam))
1456 {
1457 /* match expr is empty; plug in null string if no existing tag */
1458 buf_addstr(&env->spam, "");
1459 }
1460
1461 if (!buf_is_empty(&env->spam))
1462 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1463 }
1464 }
1465
1466 *p = '\0';
1467 p = mutt_str_skip_email_wsp(p + 1);
1468 if (*p == '\0')
1469 continue; /* skip empty header fields */
1470
1471 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1472 }
1473
1474 buf_pool_release(&line);
1475
1476 if (e)
1477 {
1478 e->body->hdr_offset = e->offset;
1479 e->body->offset = ftello(fp);
1480
1482
1483 if (e->received < 0)
1484 {
1485 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1486 e->received = 0;
1487 }
1488
1489 /* check for missing or invalid date */
1490 if (e->date_sent <= 0)
1491 {
1492 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1493 e->date_sent = e->received;
1494 }
1495
1496#ifdef USE_AUTOCRYPT
1497 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1498 if (c_autocrypt)
1499 {
1501 /* No sense in taking up memory after the header is processed */
1503 }
1504#endif
1505 }
1506
1507 return env;
1508}
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition autocrypt.c:263
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:830
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
void mutt_autocrypthdr_free(struct AutocryptHeader **ptr)
Free an AutocryptHeader.
Definition envelope.c:103
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition regex.c:481
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition regex.c:201
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition rfc2047.c:850
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:40
struct RegexList no_spam
Regexes to identify non-spam emails.
Definition module_data.h:39
The header of an Email.
Definition envelope.h:57
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 1516 of file parse.c.

1517{
1518 if (!fp)
1519 return NULL;
1520
1521 struct Body *b = mutt_body_new();
1522 struct Envelope *env = mutt_env_new();
1523 char *c = NULL;
1524 struct Buffer *buf = buf_pool_get();
1525 bool matched = false;
1526
1527 b->hdr_offset = ftello(fp);
1528
1529 b->encoding = ENC_7BIT; /* default from RFC1521 */
1530 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1532
1533 while (mutt_rfc822_read_line(fp, buf) != 0)
1534 {
1535 const char *line = buf_string(buf);
1536 /* Find the value of the current header */
1537 c = strchr(line, ':');
1538 if (c)
1539 {
1540 *c = '\0';
1541 c = mutt_str_skip_email_wsp(c + 1);
1542 if (*c == '\0')
1543 {
1544 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1545 continue;
1546 }
1547 }
1548 else
1549 {
1550 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1551 break;
1552 }
1553
1554 size_t plen = mutt_istr_startswith(line, "content-");
1555 if (plen != 0)
1556 {
1557 if (mutt_istr_equal("type", line + plen))
1558 {
1560 }
1561 else if (mutt_istr_equal("language", line + plen))
1562 {
1564 }
1565 else if (mutt_istr_equal("transfer-encoding", line + plen))
1566 {
1568 }
1569 else if (mutt_istr_equal("disposition", line + plen))
1570 {
1572 }
1573 else if (mutt_istr_equal("description", line + plen))
1574 {
1577 }
1578 else if (mutt_istr_equal("id", line + plen))
1579 {
1580 // strip <angle braces> from Content-ID: header
1581 char *id = c;
1582 int cid_len = mutt_str_len(c);
1583 if (cid_len > 2)
1584 {
1585 if (id[0] == '<')
1586 {
1587 id++;
1588 cid_len--;
1589 }
1590 if (id[cid_len - 1] == '>')
1591 id[cid_len - 1] = '\0';
1592 }
1594 }
1595 }
1596 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1597 {
1598 if (mutt_istr_equal("data-type", line + plen))
1599 {
1601 }
1602 else if (mutt_istr_equal("encoding-info", line + plen))
1603 {
1605 }
1606 else if (mutt_istr_equal("content-lines", line + plen))
1607 {
1608 mutt_param_set(&b->parameter, "content-lines", c);
1609 }
1610 else if (mutt_istr_equal("data-description", line + plen))
1611 {
1614 }
1615 }
1616 else
1617 {
1618 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1619 {
1620 matched = true;
1621 }
1622 }
1623 }
1624 b->offset = ftello(fp); /* Mark the start of the real data */
1625 if ((b->type == TYPE_TEXT) && !b->subtype)
1626 b->subtype = mutt_str_dup("plain");
1627 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1628 b->subtype = mutt_str_dup("rfc822");
1629
1630 buf_pool_release(&buf);
1631
1632 if (matched)
1633 {
1634 b->mime_headers = env;
1636 }
1637 else
1638 {
1639 mutt_env_free(&env);
1640 }
1641
1642 return b;
1643}
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
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 1652 of file parse.c.

1653{
1654 if (type != TYPE_MESSAGE)
1655 return false;
1656
1657 subtype = NONULL(subtype);
1658 return (mutt_istr_equal(subtype, "rfc822") ||
1659 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1660}
#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()

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

1889{
1890 if (!h)
1891 return false;
1892
1893 struct ListNode *np = NULL;
1894 STAILQ_FOREACH(np, h, entries)
1895 {
1896 if ((*np->data == '*') || mutt_istr_equal(s, np->data))
1897 return true;
1898 }
1899 return false;
1900}
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 1910 of file parse.c.

1911{
1912 if (!env || !src)
1913 return false;
1914
1915 struct Url *url = url_parse(src);
1916 if (!url)
1917 return false;
1918
1919 if (url->host)
1920 {
1921 /* this is not a path-only URL */
1922 url_free(&url);
1923 return false;
1924 }
1925
1926 mutt_addrlist_parse(&env->to, url->path);
1927
1929 ASSERT(mod_data);
1930
1931 struct UrlQuery *np = NULL;
1932 STAILQ_FOREACH(np, &url->query_strings, entries)
1933 {
1935 const char *tag = np->name;
1936 char *value = np->value;
1937 /* Determine if this header field is on the allowed list. Since NeoMutt
1938 * interprets some header fields specially (such as
1939 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1940 * only safe fields are allowed.
1941 *
1942 * RFC2368, "4. Unsafe headers"
1943 * The user agent interpreting a mailto URL SHOULD choose not to create
1944 * a message if any of the headers are considered dangerous; it may also
1945 * choose to create a message with only a subset of the headers given in
1946 * the URL. */
1947 if (mailto_header_allowed(tag, &mod_data->mail_to_allow))
1948 {
1949 if (mutt_istr_equal(tag, "body"))
1950 {
1951 if (body)
1952 mutt_str_replace(body, value);
1953 }
1954 else
1955 {
1956 char *scratch = NULL;
1957 size_t taglen = mutt_str_len(tag);
1958
1960 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1961 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1962 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1963 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1964 FREE(&scratch);
1965 }
1966 }
1967 }
1968
1969 /* RFC2047 decode after the RFC822 parsing */
1971
1972 url_free(&url);
1973 return true;
1974}
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1888
void mutt_filter_commandline_header_tag(char *header)
Sanitise characters in a header tag.
Definition parse.c:73
struct ListHead mail_to_allow
Permitted fields in a mailto: url.
Definition module_data.h:38
Parsed Query String.
Definition url.h:58
char * name
Query name.
Definition url.h:59
char * value
Query value.
Definition url.h:60
A parsed URL proto://user:password@host:port/path?a=1&b=2.
Definition url.h:69
struct UrlQueryList query_strings
List of query strings.
Definition url.h:76
char * host
Host.
Definition url.h:73
char * src
Raw URL string.
Definition url.h:77
char * path
Path.
Definition url.h:75
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
Here is the call graph for this function:
Here is the caller graph for this function:

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

1982{
1983 int counter = 0;
1984
1985 parse_part(fp, b, &counter);
1986}
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 1996 of file parse.c.

1997{
1998 int counter = 0;
1999
2000 return rfc822_parse_message(fp, b, &counter);
2001}
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 2012 of file parse.c.

2013{
2014 int counter = 0;
2015
2016 return parse_multipart(fp, boundary, end_off, digest, &counter);
2017}
Here is the call graph for this function:
Here is the caller graph for this function: