File Coverage

blib/lib/Email/Address/Loose/EmailAddress.pm
Criterion Covered Total %
statement 105 119 88.2
branch 34 44 77.2
condition 7 12 58.3
subroutine 18 22 81.8
pod 9 10 90.0
total 173 207 83.5


line stmt bran cond sub pod time code
1             package Email::Address::Loose::EmailAddress;
2              
3             ## no critic
4 11     11   71 use base 'Email::Address'; # for isa("Email::Address");
  11         15  
  11         6489  
5 11     11   7753 use Email::Address::Loose::EmailValidLoose;
  11         31  
  11         400  
6              
7             # Note:
8             # The following code were copied from Email::Address 1.892.
9             # http://search.cpan.org/perldoc?Email::Address
10             # To make same behavior with Email::Address escept local-part.
11              
12              
13              
14              
15 11     11   67 use strict;
  11         20  
  11         411  
16             ## no critic RequireUseWarnings
17             # support pre-5.6
18              
19 11         15962 use vars qw[$VERSION $COMMENT_NEST_LEVEL $STRINGIFY
20             $COLLAPSE_SPACES
21             %PARSE_CACHE %FORMAT_CACHE %NAME_CACHE
22 11     11   53 $addr_spec $angle_addr $name_addr $mailbox];
  11         18  
23              
24             my $NOCACHE;
25              
26             $VERSION = '1.892';
27             $COMMENT_NEST_LEVEL ||= 2;
28             $STRINGIFY ||= 'format';
29             $COLLAPSE_SPACES = 1 unless defined $COLLAPSE_SPACES; # who wants //=? me!
30              
31              
32             my $CTL = q{\x00-\x1F\x7F};
33             my $special = q{()<>\\[\\]:;@\\\\,."};
34              
35             my $text = qr/[^\x0A\x0D]/;
36              
37             my $quoted_pair = qr/\\$text/;
38              
39             my $ctext = qr/(?>[^()\\]+)/;
40             my ($ccontent, $comment) = (q{})x2;
41             for (1 .. $COMMENT_NEST_LEVEL) {
42             $ccontent = qr/$ctext|$quoted_pair|$comment/;
43             $comment = qr/\s*\((?:\s*$ccontent)*\s*\)\s*/;
44             }
45             my $cfws = qr/$comment|\s+/;
46              
47             my $atext = qq/[^$CTL$special\\s]/;
48             my $atom = qr/$cfws*$atext+$cfws*/;
49             my $dot_atom_text = qr/$atext+(?:\.$atext+)*/;
50             my $dot_atom = qr/$cfws*$dot_atom_text$cfws*/;
51              
52             my $qtext = qr/[^\\"]/;
53             my $qcontent = qr/$qtext|$quoted_pair/;
54             my $quoted_string = qr/$cfws*"$qcontent+"$cfws*/;
55              
56             my $word = qr/$atom|$quoted_string/;
57              
58             # XXX: This ($phrase) used to just be: my $phrase = qr/$word+/; It was changed
59             # to resolve bug 22991, creating a significant slowdown. Given current speed
60             # problems. Once 16320 is resolved, this section should be dealt with.
61             # -- rjbs, 2006-11-11
62             #my $obs_phrase = qr/$word(?:$word|\.|$cfws)*/;
63              
64             # XXX: ...and the above solution caused endless problems (never returned) when
65             # examining this address, now in a test:
66             # admin+=E6=96=B0=E5=8A=A0=E5=9D=A1_Weblog-- ATAT --test.socialtext.com
67             # So we disallow the hateful CFWS in this context for now. Of modern mail
68             # agents, only Apple Web Mail 2.0 is known to produce obs-phrase.
69             # -- rjbs, 2006-11-19
70             my $simple_word = qr/$atom|\.|\s*"$qcontent+"\s*/;
71             my $obs_phrase = qr/$simple_word+/;
72              
73             my $phrase = qr/$obs_phrase|(?:$word+)/;
74              
75             my $local_part = qr/$dot_atom|$quoted_string/;
76             $local_part = Email::Address::Loose::EmailValidLoose->peek_local_part; # Note: added by Email::Address::Loose
77              
78             my $dtext = qr/[^\[\]\\]/;
79             my $dcontent = qr/$dtext|$quoted_pair/;
80             my $domain_literal = qr/$cfws*\[(?:\s*$dcontent)*\s*\]$cfws*/;
81             my $domain = qr/$dot_atom|$domain_literal/;
82              
83             my $display_name = $phrase;
84              
85              
86             $addr_spec = qr/$local_part\@$domain/;
87             $angle_addr = qr/$cfws*<$addr_spec>$cfws*/;
88             $name_addr = qr/$display_name?$angle_addr/;
89             $mailbox = qr/(?:$name_addr|$addr_spec)$comment*/;
90              
91             sub _PHRASE () { 0 }
92             sub _ADDRESS () { 1 }
93             sub _COMMENT () { 2 }
94             sub _ORIGINAL () { 3 }
95             sub _IN_CACHE () { 4 }
96              
97              
98             sub __get_cached_parse {
99 113 50   113   304 return if $NOCACHE;
100              
101 113         233 my ($class, $line) = @_;
102              
103 113 100       423 return @{$PARSE_CACHE{$line}} if exists $PARSE_CACHE{$line};
  2         11  
104 111         436 return;
105             }
106              
107             sub __cache_parse {
108 111 50   111   330 return if $NOCACHE;
109            
110 111         444 my ($class, $line, $addrs) = @_;
111              
112 111         441 $PARSE_CACHE{$line} = $addrs;
113             }
114              
115             sub parse {
116 114     114 1 79219 my ($class, $line) = @_;
117 114 100       479 $class = 'Email::Address::Loose' if $class eq 'Email::Address'; # Note: added by Email::Address::Loose
118              
119 114 100       473 return unless $line;
120              
121 113 50       1108 $line =~ s/[ \t]+/ /g if $COLLAPSE_SPACES;
122              
123 113 100       869 if (my @cached = $class->__get_cached_parse($line)) {
124 2         7 return @cached;
125             }
126              
127 111         32523 my (@mailboxes) = ($line =~ /$mailbox/go);
128 111         252 my @addrs;
129 111         284 foreach (@mailboxes) {
130 231         374 my $original = $_;
131              
132 231         1127 my @comments = /($comment)/go;
133 231 100       886 s/$comment//go if @comments;
134              
135 231         1024 my ($user, $host, $com);
136 231 100       4642 ($user, $host) = ($1, $2) if s/<($local_part)\@($domain)>//o;
137 231 100 66     2587 if (! defined($user) || ! defined($host)) {
138 44         2127 s/($local_part)\@($domain)//o;
139 44         216 ($user, $host) = ($1, $2);
140             }
141              
142 231         4996 my ($phrase) = /($display_name)/o;
143              
144 231         640 for ( $phrase, $host, $user, @comments ) {
145 694 100       1617 next unless defined $_;
146 617         1205 s/^\s+//;
147 617         1445 s/\s+$//;
148 617 50       1848 $_ = undef unless length $_;
149             }
150              
151 231         493 my $new_comment = join q{ }, @comments;
152 231         1149 push @addrs,
153             $class->new($phrase, "$user\@$host", $new_comment, $original);
154 231         2239 $addrs[-1]->[_IN_CACHE] = [ \$line, $#addrs ]
155             }
156              
157 111         555 $class->__cache_parse($line, \@addrs);
158 111         664 return @addrs;
159             }
160              
161              
162             sub new {
163 231     231 1 572 my ($class, $phrase, $email, $comment, $orig) = @_;
164 231 100       1122 $phrase =~ s/\A"(.+)"\z/$1/ if $phrase;
165              
166 231         1273 bless [ $phrase, $email, $comment, $orig ] => $class;
167             }
168              
169              
170             sub purge_cache {
171 0     0 1 0 %NAME_CACHE = ();
172 0         0 %FORMAT_CACHE = ();
173 0         0 %PARSE_CACHE = ();
174             }
175              
176              
177             sub disable_cache {
178 0     0 1 0 my ($class) = @_;
179 0         0 $class->purge_cache;
180 0         0 $NOCACHE = 1;
181             }
182              
183             sub enable_cache {
184 0     0 1 0 $NOCACHE = undef;
185             }
186              
187              
188             BEGIN {
189 11     11   76 my %_INDEX = (
190             phrase => _PHRASE,
191             address => _ADDRESS,
192             comment => _COMMENT,
193             original => _ORIGINAL,
194             );
195              
196 11         49 for my $method (keys %_INDEX) {
197 11     11   94 no strict 'refs';
  11         21  
  11         1957  
198 44         87 my $index = $_INDEX{ $method };
199             *$method = sub {
200 11 100   11   5003 if ($_[1]) {
201 1 50       5 if ($_[0][_IN_CACHE]) {
202 1         2 my $replicant = bless [ @{$_[0]} ] => ref $_[0];
  1         4  
203 1         2 $PARSE_CACHE{ ${ $_[0][_IN_CACHE][0] } }[ $_[0][_IN_CACHE][1] ]
  1         4  
204             = $replicant;
205 1         2 $_[0][_IN_CACHE] = undef;
206             }
207 1         3 $_[0]->[ $index ] = $_[1];
208             } else {
209 10         49 $_[0]->[ $index ];
210             }
211 44         10127 };
212             }
213             }
214              
215 0     0 1 0 sub host { ($_[0]->[_ADDRESS] =~ /\@($domain)/o)[0] }
216 5     5 1 235 sub user { ($_[0]->[_ADDRESS] =~ /($local_part)\@/o)[0] }
217              
218              
219             sub format {
220 882     882 1 223192 local $^W = 0; ## no critic
221 882 100       1661 return $FORMAT_CACHE{"@{$_[0]}"} if exists $FORMAT_CACHE{"@{$_[0]}"};
  660         5446  
  882         6375  
222 222         767 $FORMAT_CACHE{"@{$_[0]}"} = $_[0]->_format;
  222         2546  
223             }
224              
225             sub _format {
226 222     222   490 my ($self) = @_;
227              
228 222 50 66     2164 unless (
      33        
      66        
229             defined $self->[_PHRASE] && length $self->[_PHRASE]
230             ||
231             defined $self->[_COMMENT] && length $self->[_COMMENT]
232             ) {
233 68         216 return $self->[_ADDRESS];
234             }
235              
236 154         590 my $format = sprintf q{%s <%s> %s},
237             $self->_enquoted_phrase, $self->[_ADDRESS], $self->[_COMMENT];
238              
239 154         538 $format =~ s/^\s+//;
240 154         821 $format =~ s/\s+$//;
241              
242 154         381 return $format;
243             }
244              
245             sub _enquoted_phrase {
246 154     154   318 my ($self) = @_;
247              
248 154         358 my $phrase = $self->[_PHRASE];
249              
250             # if it's encoded -- rjbs, 2007-02-28
251 154 50       618 return $phrase if $phrase =~ /\A=\?.+\?=\z/;
252              
253 154         319 $phrase =~ s/\A"(.+)"\z/$1/;
254 154         317 $phrase =~ s/\"/\\"/g;
255              
256 154         1457 return qq{"$phrase"};
257             }
258              
259              
260             sub name {
261 220     220 1 903 local $^W = 0;
262 220 50       659 return $NAME_CACHE{"@{$_[0]}"} if exists $NAME_CACHE{"@{$_[0]}"};
  0         0  
  220         1725  
263 220         429 my ($self) = @_;
264 220         748 my $name = q{};
265 220 100       1141 if ( $name = $self->[_PHRASE] ) {
    50          
266 152         298 $name =~ s/^"//;
267 152         325 $name =~ s/"$//;
268 152         328 $name =~ s/($quoted_pair)/substr $1, -1/goe;
  2         11  
269             } elsif ( $name = $self->[_COMMENT] ) {
270 0         0 $name =~ s/^\(//;
271 0         0 $name =~ s/\)$//;
272 0         0 $name =~ s/($quoted_pair)/substr $1, -1/goe;
  0         0  
273 0         0 $name =~ s/$comment/ /go;
274             } else {
275 68         852 ($name) = $self->[_ADDRESS] =~ /($local_part)\@/o;
276             }
277 220         414 $NAME_CACHE{"@{$_[0]}"} = $name;
  220         2021  
278             }
279              
280              
281             sub as_string {
282 660 50   660 0 2330 warn 'altering $Email::Address::STRINGIFY is deprecated; subclass instead'
283             if $STRINGIFY ne 'format';
284              
285 660         3066 $_[0]->can($STRINGIFY)->($_[0]);
286             }
287              
288 11     11   80 use overload '""' => 'as_string';
  11         34  
  11         108  
289              
290              
291             1;
292