diff --git a/lib/mail/parsers/rfc_2822.ex b/lib/mail/parsers/rfc_2822.ex index 220e20d..49b4741 100644 --- a/lib/mail/parsers/rfc_2822.ex +++ b/lib/mail/parsers/rfc_2822.ex @@ -318,10 +318,49 @@ defmodule Mail.Parsers.RFC2822 do @spec parse_recipient_value(value :: String.t()) :: [{String.t(), String.t()} | String.t()] def parse_recipient_value(value) do - Regex.scan(~r/\s*("?)(.*?)\1\s*?,]+)>?,?/, value) + Regex.scan( + ~r/ + \s* + (?: + # Quoted name + ((?.*?) + \1 + \s* + < + (?[^<\s,]+@[^\s>,]+) + > + | + # Non-quoted name + (?[^\\",@]+?) + \s*? + < + (?[^<\s,]+@[^\s>,]+) + > + | + # Only email + [^<\s,]+@[^\s>,]+)>? + ) + ,? + /x, + value, + capture: :all_names + ) + |> Enum.map(fn + # Scan is matching on named captures sorted alphabetically: + # [email, email2, email3, name, name2] + ["", "", address, "", ""] -> + {"", address} + + [address, "", "", name, ""] -> + {name, address} + + ["", address, "", "", name] -> + {name, address} + end) |> Enum.map(fn - [_, _, "", address] -> address - [_, _, name, address] -> {name, address} + {"", address} -> address + {name, address} -> {String.replace(name, "\\", ""), address} end) end diff --git a/test/mail/parsers/rfc_2822_test.exs b/test/mail/parsers/rfc_2822_test.exs index 4bf58dd..495fff6 100644 --- a/test/mail/parsers/rfc_2822_test.exs +++ b/test/mail/parsers/rfc_2822_test.exs @@ -303,30 +303,59 @@ defmodule Mail.Parsers.RFC2822Test do assert to_datetime("invalid date string") == {:error, "invalid date string"} end - test "parse_recipient_value retrieves a list of name and addresses" do - recipient = - "The Dude , batman@example.com, super, \"an@email.com\" " - - retrieved_recipients = [ - {"The Dude", "dude@example.com"}, - "batman@example.com", - {"super", "compact@recipi.ent"}, - {"an@email.com", "an@email.com"} - ] - - assert parse_recipient(recipient) == retrieved_recipients - end + describe "parse_recipient_value/1" do + test "parse_recipient_value retrieves a list of name and addresses" do + recipient = + ~S|The Dude , batman@example.com, super, "an@email.com" | + + retrieved_recipients = [ + {"The Dude", "dude@example.com"}, + "batman@example.com", + {"super", "compact@recipi.ent"}, + {"an@email.com", "an@email.com"} + ] + + assert parse_recipient(recipient) == retrieved_recipients + end + + test "parse_recipient_value retrieves an empty list when recipient is empty" do + assert parse_recipient("") == [] + end + + test "parse_recipient_value retrieves an empty list when no \"address\" found" do + assert parse_recipient("NoEmail") == [] + end + + test "parse_recipient_value retrieves a list when only one \"address\" found" do + assert parse_recipient("dude@example.com") == ["dude@example.com"] + assert parse_recipient("") == ["dude@example.com"] + end + + test "parse_recipient_value quoted name" do + assert parse_recipient(~S|"dude" |) == [{"dude", "dude@example.com"}] + + assert parse_recipient(~S|"First, Second" |) == [ + {"First, Second", "dude@example.com"} + ] + end - test "parse_recipient_value retrieves an empty list when recipient is empty" do - assert parse_recipient("") == [] - end + test "parse_recipient_value non-quoted name" do + assert parse_recipient(~S|The Dude |) == [ + {"The Dude", "dude@example.com"} + ] + end - test "parse_recipient_value retrieves an empty list when no \"address\" found" do - assert parse_recipient("NoEmail") == [] - end + test "parse_recipient_value extra quoted name" do + assert parse_recipient(~S|"\"dude\"" |) == [ + {"\"dude\"", "dude@example.com"} + ] + end - test "parse_recipient_value retrieves a list when only one \"address\" found" do - assert parse_recipient("dude@example.com") == ["dude@example.com"] + test "parse_recipient_value extra test" do + assert parse_recipient(~S|"\"service@service.com\" " |) == [ + {~S|"service@service.com" |, "service@service.com"} + ] + end end test "parses a nested multipart message with encoded part" do @@ -570,6 +599,19 @@ defmodule Mail.Parsers.RFC2822Test do assert message.headers["from"] == {"Lastname, First Names", "me@example.com"} end + test "address name is an e-mail address with additiongal quotes" do + message = + parse_email(""" + To: "User, Test" + From: ""me@example.com"" + Date: Fri, 1 Jan 2016 00:00:00 +0000 + Subject: Blank body + + """) + + assert message.headers["from"] == {"\"me@example.com\"", "me@example.com"} + end + # See https://tools.ietf.org/html/rfc2047 test "parses headers with encoded word syntax" do message =