Skip to content

Commit 7fc46ed

Browse files
Change parse_recipient_value regex
I’ve broken the regular expression to match three distinct types of address 1. Quoted name with address 2. Non-quoted name with address 3. address on it’s own I have also changed to expanded notation to make it a little clearer.
1 parent 43b1f4b commit 7fc46ed

File tree

2 files changed

+92
-24
lines changed

2 files changed

+92
-24
lines changed

lib/mail/parsers/rfc_2822.ex

+42-3
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,49 @@ defmodule Mail.Parsers.RFC2822 do
318318
@spec parse_recipient_value(value :: String.t()) ::
319319
[{String.t(), String.t()} | String.t()]
320320
def parse_recipient_value(value) do
321-
Regex.scan(~r/\s*("?)(.*?)\1\s*?<?([^<\s]+@[^\s>,]+)>?,?/, value)
321+
Regex.scan(
322+
~r/
323+
\s*
324+
(?:
325+
# Quoted name
326+
((?<!\\)")
327+
(?<name>.*?)
328+
\1
329+
\s*
330+
<
331+
(?<email>[^<\s,]+@[^\s>,]+)
332+
>
333+
|
334+
# Non-quoted name
335+
(?<name2>[^\\",@]+?)
336+
\s*?
337+
<
338+
(?<email2>[^<\s,]+@[^\s>,]+)
339+
>
340+
|
341+
# Only email
342+
<?(?<email3>[^<\s,]+@[^\s>,]+)>?
343+
)
344+
,?
345+
/x,
346+
value,
347+
capture: :all_names
348+
)
349+
|> Enum.map(fn
350+
# Scan is matching on named captures sorted alphabetically:
351+
# [email, email2, email3, name, name2]
352+
["", "", address, "", ""] ->
353+
{"", address}
354+
355+
[address, "", "", name, ""] ->
356+
{name, address}
357+
358+
["", address, "", "", name] ->
359+
{name, address}
360+
end)
322361
|> Enum.map(fn
323-
[_, _, "", address] -> address
324-
[_, _, name, address] -> {name, address}
362+
{"", address} -> address
363+
{name, address} -> {String.replace(name, "\\", ""), address}
325364
end)
326365
end
327366

test/mail/parsers/rfc_2822_test.exs

+50-21
Original file line numberDiff line numberDiff line change
@@ -303,30 +303,59 @@ defmodule Mail.Parsers.RFC2822Test do
303303
assert to_datetime("invalid date string") == {:error, "invalid date string"}
304304
end
305305

306-
test "parse_recipient_value retrieves a list of name and addresses" do
307-
recipient =
308-
309-
310-
retrieved_recipients = [
311-
{"The Dude", "[email protected]"},
312-
313-
{"super", "[email protected]"},
314-
315-
]
316-
317-
assert parse_recipient(recipient) == retrieved_recipients
318-
end
306+
describe "parse_recipient_value/1" do
307+
test "parse_recipient_value retrieves a list of name and addresses" do
308+
recipient =
309+
310+
311+
retrieved_recipients = [
312+
{"The Dude", "[email protected]"},
313+
314+
{"super", "[email protected]"},
315+
316+
]
317+
318+
assert parse_recipient(recipient) == retrieved_recipients
319+
end
320+
321+
test "parse_recipient_value retrieves an empty list when recipient is empty" do
322+
assert parse_recipient("") == []
323+
end
324+
325+
test "parse_recipient_value retrieves an empty list when no \"address\" found" do
326+
assert parse_recipient("NoEmail") == []
327+
end
328+
329+
test "parse_recipient_value retrieves a list when only one \"address\" found" do
330+
assert parse_recipient("[email protected]") == ["[email protected]"]
331+
assert parse_recipient("<[email protected]>") == ["[email protected]"]
332+
end
333+
334+
test "parse_recipient_value quoted name" do
335+
assert parse_recipient(~S|"dude" <[email protected]>|) == [{"dude", "[email protected]"}]
336+
337+
assert parse_recipient(~S|"First, Second" <[email protected]>|) == [
338+
{"First, Second", "[email protected]"}
339+
]
340+
end
319341

320-
test "parse_recipient_value retrieves an empty list when recipient is empty" do
321-
assert parse_recipient("") == []
322-
end
342+
test "parse_recipient_value non-quoted name" do
343+
assert parse_recipient(~S|The Dude <[email protected]>|) == [
344+
{"The Dude", "[email protected]"}
345+
]
346+
end
323347

324-
test "parse_recipient_value retrieves an empty list when no \"address\" found" do
325-
assert parse_recipient("NoEmail") == []
326-
end
348+
test "parse_recipient_value extra quoted name" do
349+
assert parse_recipient(~S|"\"dude\"" <[email protected]>|) == [
350+
{"\"dude\"", "[email protected]"}
351+
]
352+
end
327353

328-
test "parse_recipient_value retrieves a list when only one \"address\" found" do
329-
assert parse_recipient("[email protected]") == ["[email protected]"]
354+
test "parse_recipient_value extra test" do
355+
assert parse_recipient(~S|"\"[email protected]\" " <[email protected]>|) == [
356+
357+
]
358+
end
330359
end
331360

332361
test "parses a nested multipart message with encoded part" do

0 commit comments

Comments
 (0)