diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-12-22 16:27:09 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-12-22 16:27:09 +0000 |
commit | c5a11fc210c77e7b4a6405610bcc4c78bb578581 (patch) | |
tree | 1c12dae04f4e8b23058e71c05361b3bc9f5f7dee | |
parent | 40f6e53fe3c30d5272c1efd4bbd2f527a92990aa (diff) | |
download | rspamd-c5a11fc210c77e7b4a6405610bcc4c78bb578581.tar.gz rspamd-c5a11fc210c77e7b4a6405610bcc4c78bb578581.zip |
[Feature] Add new function to parse mime addresses
-rw-r--r-- | src/libmime/email_addr.c | 197 | ||||
-rw-r--r-- | src/libmime/email_addr.h | 23 |
2 files changed, 219 insertions, 1 deletions
diff --git a/src/libmime/email_addr.c b/src/libmime/email_addr.c index 2c7964f87..75fdd9fc6 100644 --- a/src/libmime/email_addr.c +++ b/src/libmime/email_addr.c @@ -19,6 +19,7 @@ #include "message.h" #include "printf.h" #include "smtp_parsers.h" +#include "mime_headers.h" static void rspamd_email_addr_dtor (struct rspamd_email_address *addr) @@ -31,6 +32,10 @@ rspamd_email_addr_dtor (struct rspamd_email_address *addr) g_free ((void *)addr->user); } + if (addr->name) { + g_free ((gpointer)addr->name); + } + g_slice_free1 (sizeof (*addr), addr); } @@ -113,3 +118,195 @@ rspamd_email_address_unref (struct rspamd_email_address *addr) { REF_RELEASE (addr); } + +static inline void +rspamd_email_address_add (rspamd_mempool_t *pool, + GPtrArray *ar, + struct rspamd_email_address *addr, + GString *name) +{ + struct rspamd_email_address *elt; + guint nlen; + + elt = g_slice_alloc (sizeof (*elt)); + memcpy (elt, addr, sizeof (*addr)); + + if ((elt->flags & RSPAMD_EMAIL_ADDR_QUOTED) && elt->addr[0] == '"') { + if (elt->flags & RSPAMD_EMAIL_ADDR_HAS_BACKSLASH) { + /* We also need to unquote user */ + rspamd_email_address_unescape (elt); + } + + /* We need to unquote addr */ + nlen = elt->domain_len + elt->user_len + 2; + elt->addr = g_malloc (nlen + 1); + elt->addr_len = rspamd_snprintf ((char *)elt->addr, nlen, "%*s@%*s", + (gint)elt->user_len, elt->user, + (gint)elt->domain_len, elt->domain); + elt->flags |= RSPAMD_EMAIL_ADDR_ADDR_ALLOCATED; + } + + REF_INIT_RETAIN (elt, rspamd_email_addr_dtor); + + if (name->len > 0) { + elt->name = rspamd_mime_header_decode (pool, name->str, name->len); + } + + g_ptr_array_add (ar, elt); +} + +GPtrArray * +rspamd_email_address_from_mime (rspamd_mempool_t *pool, + const gchar *hdr, guint len, + GPtrArray *src) +{ + GPtrArray *res = src; + struct rspamd_email_address addr; + const gchar *p = hdr, *end = hdr + len, *c = hdr, *t; + GString *ns; + enum { + parse_name = 0, + parse_quoted, + parse_addr, + skip_spaces + } state = parse_name, next_state = parse_name; + + if (res == NULL) { + res = g_ptr_array_sized_new (2); + rspamd_mempool_add_destructor (pool, rspamd_email_address_list_destroy, + res); + } + + ns = g_string_sized_new (127); + + while (p < end) { + switch (state) { + case parse_name: + if (*p == '"') { + /* We need to strip last spaces and update `ns` */ + if (p > c) { + t = p; + while (t > c && g_ascii_isspace (*t)) { + t --; + } + + g_string_append_len (ns, c, t - c); + } + + state = parse_quoted; + c = p + 1; + } + else if (*p == '<') { + if (p > c) { + t = p; + while (t > c && g_ascii_isspace (*t)) { + t --; + } + + g_string_append_len (ns, c, t - c); + } + + c = p; + state = parse_addr; + } + else if (*p == ',') { + if (p > c) { + /* + * Last token must be the address: + * e.g. Some name name@domain.com + */ + t = p; + while (t > c && g_ascii_isspace (*t)) { + t --; + } + + rspamd_smtp_addr_parse (c, t - c, &addr); + + if (addr.flags & RSPAMD_EMAIL_ADDR_VALID) { + rspamd_email_address_add (pool, res, &addr, ns); + } + + /* Cleanup for the next use */ + g_string_set_size (ns, 0); + } + + state = skip_spaces; + next_state = parse_name; + } + p ++; + break; + case parse_quoted: + if (*p == '"') { + if (p > c) { + g_string_append_len (ns, c, p - c); + } + + state = skip_spaces; + next_state = parse_name; + } + p ++; + break; + case parse_addr: + if (*p == '>') { + rspamd_smtp_addr_parse (c, p - c + 1, &addr); + + if (addr.flags & RSPAMD_EMAIL_ADDR_VALID) { + rspamd_email_address_add (pool, res, &addr, ns); + } + + /* Cleanup for the next use */ + g_string_set_size (ns, 0); + + state = skip_spaces; + next_state = parse_name; + } + p ++; + break; + case skip_spaces: + if (!g_ascii_isspace (*p)) { + c = p; + state = next_state; + } + else { + p ++; + } + break; + } + } + + /* Handle leftover */ + switch (state) { + case parse_name: + case parse_addr: + if (p > c) { + rspamd_smtp_addr_parse (c, p - c + 1, &addr); + + if (addr.flags & RSPAMD_EMAIL_ADDR_VALID) { + rspamd_email_address_add (pool, res, &addr, ns); + } + } + break; + case parse_quoted: + /* Unfinished quoted string */ + break; + default: + /* Do nothing */ + break; + } + + return res; +} + +void +rspamd_email_address_list_destroy (gpointer ptr) +{ + GPtrArray *ar = ptr; + guint i; + struct rspamd_email_address *addr; + + PTR_ARRAY_FOREACH (ar, i, addr) { + REF_RELEASE (addr); + } + + g_ptr_array_free (ar, TRUE); +} diff --git a/src/libmime/email_addr.h b/src/libmime/email_addr.h index de91237db..f6a16d682 100644 --- a/src/libmime/email_addr.h +++ b/src/libmime/email_addr.h @@ -17,7 +17,8 @@ #define SRC_LIBMIME_EMAIL_ADDR_H_ #include "config.h" -#include "ref.h" +#include "libutil/mem_pool.h" +#include "libutil/ref.h" struct rspamd_mime_header; @@ -62,6 +63,26 @@ struct rspamd_email_address { struct rspamd_email_address * rspamd_email_address_from_smtp ( const gchar *str, guint len); +/** + * Parses email address from the mime header, decodes names and return the array + * of `rspamd_email_address`. If `src` is NULL, then this function creates a new + * array and adds a destructor to remove elements when `pool` is destroyed. + * Otherwise, addresses are appended to `src`. + * @param hdr + * @param len + * @return + */ +GPtrArray *rspamd_email_address_from_mime (rspamd_mempool_t *pool, + const gchar *hdr, + guint len, + GPtrArray *src); + +/** + * Destroys list of email addresses + * @param ptr + */ +void rspamd_email_address_list_destroy (gpointer ptr); + struct rspamd_email_address * rspamd_email_address_ref ( struct rspamd_email_address *addr); |