Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions lib/Email/Address.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package Email::Address;
our $COMMENT_NEST_LEVEL ||= 1;
our $STRINGIFY ||= 'format';
our $COLLAPSE_SPACES = 1 unless defined $COLLAPSE_SPACES; # I miss //=
our $UNICODE ||= 0;

=head1 SYNOPSIS

Expand Down Expand Up @@ -172,9 +173,10 @@ collapse multiple spaces into a single space, which avoids this problem. To
prevent this behavior, set C<$Email::Address::COLLAPSE_SPACES> to zero. This
variable will go away when the bug is resolved properly.

In accordance with RFC 822 and its descendants, this module demands that email
addresses be ASCII only. Any non-ASCII content in the parsed addresses will
cause the parser to return no results.
By default, this module mandates that email addresses be ASCII only, and any
non-ASCII content will cause a blank result. This matches RFCs 822, 2822, and
5322. If you wish to allow UTF-8 characters in email, as per RFCs 5335 and
6532, set C<$Email:Address::UNICODE> to 1.

=cut

Expand Down Expand Up @@ -223,8 +225,10 @@ sub parse {
($user, $host) = ($1, $2);
}

next if $user =~ /\P{ASCII}/;
next if $host =~ /\P{ASCII}/;
unless ($UNICODE) {
next if $user =~ /\P{ASCII}/;
next if $host =~ /\P{ASCII}/;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect, RFC 6532 talks about UTF-8 which is subspace of 8bit sequences. But \P{ASCII} matches also ordinals above 8bits. UTF-8 != \P{ASCII} and also UTF-8 != $UNICODE. This code absolutely does not match documentation which is written above and also does not confirm to the RFC 6532.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matching UTF-8 sequences by perl regexes is not easy and there is no \P abbrev for it. Even [^\x00-\xFF] is incorrect as it would not match invalid UTF-8 sequences too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I am not saying that with this change, the code would validate the data as UTF-8, it would not. But currently, it is impossible to use this module with UTF-8 addresses at all, and this change would at least allow you to use them (assuming you would do such checking that it is valid UTF-8 data yourself). This option allows you to remove some code that currently exists in the module – as an option for backwards compatibility (I assumed I could not simply remove the two lines, plus #12 said some people may want the current behaviour). Please don't let the perfect be the enemy of the good :)


my ($phrase) = /($display_name)/o;

Expand Down
83 changes: 48 additions & 35 deletions t/ascii.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,63 @@ my $ascii = q{admin@mozilla.org};
my $utf_8 = q{аdmin@mozilla.org};
my $text = decode('utf-8', $utf_8, Encode::LEAVE_SRC);

my $ok_mixed = qq{"$text" <$ascii>};
my $bad_mixed = qq{"$text" <$text>};
my $ascii_mixed = qq{"$text" <$ascii>};
my $utf8_mixed = qq{"$text" <$text>};

{
my (@addr) = Email::Address->parse($ascii);
is(@addr, 1, "an ascii address is a-ok");
for (0..1) {
local $Email::Address::UNICODE = $_;

# ok( $ascii =~ $Email::Address::addr_spec, "...it =~ addr_spec");
}
{
my (@addr) = Email::Address->parse($ascii);
is(@addr, 1, "an ascii address is a-ok");

{
my (@addr) = Email::Address->parse($ok_mixed);
is(@addr, 1, "a quoted non-ascii phrase is a-ok with ascii email");
}
# ok( $ascii =~ $Email::Address::addr_spec, "...it =~ addr_spec");
}

{
my (@addr) = Email::Address->parse($bad_mixed);
is(@addr, 0, "a quoted non-ascii phrase is not okay with non-ascii email");
}
{
my (@addr) = Email::Address->parse($ascii_mixed);
is(@addr, 1, "a quoted non-ascii phrase is a-ok with ascii email");
}

{
my (@addr) = Email::Address->parse($utf_8);
is(@addr, 0, "utf-8 octet address: not ok");
{
my (@addr) = Email::Address->parse($utf8_mixed);
is(@addr, $Email::Address::UNICODE, "a quoted non-ascii phrase with non-ascii email");
}

# ok( $utf_8 !~ $Email::Address::addr_spec, "...it !~ addr_spec");
}
{
my (@addr) = Email::Address->parse($utf_8);
is(@addr, $Email::Address::UNICODE, "utf-8 octet address");

{
my (@addr) = Email::Address->parse($text);
is(@addr, 0, "unicode (decoded) address: not ok");
# ok( $utf_8 !~ $Email::Address::addr_spec, "...it !~ addr_spec");
}

# ok( $text =~ $Email::Address::addr_spec, "...it !~ addr_spec");
}
{
my (@addr) = Email::Address->parse($text);
is(@addr, $Email::Address::UNICODE, "unicode (decoded) address");

# ok( $text =~ $Email::Address::addr_spec, "...it !~ addr_spec");
}

{
my @addr = Email::Address->parse(qq{
"Not ascii phras\x{e9}" <good\@email>,
b\x{e3}d\@user,
bad\@d\x{f6}main,
not.bad\@again
});
is scalar @addr, $Email::Address::UNICODE ? 4 : 2, "correct number of good emails";
is "$addr[0]", qq{"Not ascii phras\x{e9}" <good\@email>}, "expected email";
if ($Email::Address::UNICODE) {
is "$addr[1]", qq{b\x{e3}d\@user}, "expected email";
is "$addr[2]", qq{bad\@d\x{f6}main}, "expected email";
is "$addr[3]", qq{not.bad\@again}, "expected email";
} else {
is "$addr[1]", qq{not.bad\@again}, "expected email";
}
}

Email::Address->purge_cache;

{
my @addr = Email::Address->parse(qq{
"Not ascii phras\x{e9}" <good\@email>,
b\x{e3}d\@user,
bad\@d\x{f6}main,
not.bad\@again
});
is scalar @addr, 2, "correct number of good emails";
is "$addr[0]", qq{"Not ascii phras\x{e9}" <good\@email>}, "expected email";
is "$addr[1]", qq{not.bad\@again}, "expected email";
}

done_testing;