aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-12-22 16:27:09 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-12-22 16:27:09 +0000
commitc5a11fc210c77e7b4a6405610bcc4c78bb578581 (patch)
tree1c12dae04f4e8b23058e71c05361b3bc9f5f7dee
parent40f6e53fe3c30d5272c1efd4bbd2f527a92990aa (diff)
downloadrspamd-c5a11fc210c77e7b4a6405610bcc4c78bb578581.tar.gz
rspamd-c5a11fc210c77e7b4a6405610bcc4c78bb578581.zip
[Feature] Add new function to parse mime addresses
-rw-r--r--src/libmime/email_addr.c197
-rw-r--r--src/libmime/email_addr.h23
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);