File Coverage

blib/lib/Email/Valid.pm
Criterion Covered Total %
statement 138 200 69.0
branch 64 138 46.3
condition 24 55 43.6
subroutine 22 27 81.4
pod 6 6 100.0
total 254 426 59.6


line stmt bran cond sub pod time code
1             require 5.006;
2 3     3   86564 use strict;
  3         6  
  3         69  
3 3     3   9 use warnings;
  3         3  
  3         220  
4             package Email::Valid;
5             $Email::Valid::VERSION = '1.202';
6             # ABSTRACT: Check validity of Internet email addresses
7             our (
8             $RFC822PAT,
9             $Details, $Resolver, $Nslookup_Path,
10             $Debug,
11             );
12              
13 3     3   11 use Carp;
  3         4  
  3         170  
14 3     3   1189 use IO::File;
  3         13647  
  3         259  
15 3     3   1106 use Mail::Address;
  3         4973  
  3         77  
16 3     3   15 use File::Spec;
  3         2  
  3         54  
17 3     3   8 use Scalar::Util 'blessed';
  3         4  
  3         1733  
18              
19             our %AUTOLOAD = (
20             allow_ip => 1,
21             fqdn => 1,
22             fudge => 1,
23             mxcheck => 1,
24             tldcheck => 1,
25             local_rules => 1,
26             localpart => 1,
27             );
28              
29             our $NSLOOKUP_PAT = 'preference|serial|expire|mail\s+exchanger';
30             our @NSLOOKUP_PATHS = File::Spec->path();
31              
32             # initialize if already loaded, better in prefork mod_perl environment
33             our $DNS_Method = defined $Net::DNS::VERSION ? 'Net::DNS' : '';
34             unless ($DNS_Method) {
35             __PACKAGE__->_select_dns_method;
36             }
37              
38             # initialize $Resolver if necessary
39             if ($DNS_Method eq 'Net::DNS') {
40             unless (defined $Resolver) {
41             $Resolver = Net::DNS::Resolver->new;
42             }
43             }
44              
45             sub new {
46 1     1 1 12 my $class = shift;
47              
48 1   33     6 $class = ref $class || $class;
49 1         2 bless my $self = {}, $class;
50 1         4 $self->_initialize;
51 1         8 %$self = $self->_rearrange([ keys %AUTOLOAD ], \@_);
52 1         4 return $self;
53             }
54              
55             sub _initialize {
56 5     5   6 my $self = shift;
57              
58 5         12 $self->{mxcheck} = 0;
59 5         3 $self->{tldcheck} = 0;
60 5         7 $self->{fudge} = 0;
61 5         8 $self->{fqdn} = 1;
62 5         5 $self->{allow_ip} = 1;
63 5         7 $self->{local_rules} = 0;
64 5         28 $self->{localpart} = 1;
65 5         10 $self->{details} = $Details = undef;
66             }
67              
68             # Pupose: handles named parameter calling style
69             sub _rearrange {
70 68     68   53 my $self = shift;
71 68         52 my(@names) = @{ shift() };
  68         135  
72 68         52 my(@params) = @{ shift() };
  68         71  
73 68         57 my(%args);
74              
75 68 100       243 ref $self ? %args = %$self : _initialize( \%args );
76 68 100       131 return %args unless @params;
77              
78 67 100 66     294 unless (@params > 1 and $params[0] =~ /^-/) {
79 15         31 while(@params) {
80 15 50       18 croak 'unexpected number of parameters' unless @names;
81 15         49 $args{ lc shift @names } = shift @params;
82             }
83 15         73 return %args;
84             }
85              
86 52         81 while(@params) {
87 70         93 my $param = lc substr(shift @params, 1);
88 70         135 $args{ $param } = shift @params;
89             }
90              
91 52         204 %args;
92             }
93              
94             # Purpose: determine why an address failed a check
95             sub details {
96 19     19 1 22 my $self = shift;
97              
98 19 50       44 return (ref $self ? $self->{details} : $Details) unless @_;
    100          
99 16         19 $Details = shift;
100 16 100       41 $self->{details} = $Details if ref $self;
101 16         141 return undef;
102             }
103              
104             # Purpose: Check whether address conforms to RFC 822 syntax.
105             sub rfc822 {
106 31     31 1 25 my $self = shift;
107 31         63 my %args = $self->_rearrange([qw( address )], \@_);
108              
109 31 50       70 my $addr = $args{address} or return $self->details('rfc822');
110 31 50 33     75 $addr = $addr->address if (blessed($addr) && $addr->isa('Mail::Address'));
111              
112 31 100 100     1209 return $self->details('rfc822')
113 3     3   1435 if $addr =~ /\P{ASCII}/ or $addr !~ m/^$RFC822PAT$/o;
  3         24  
  3         35  
114              
115 28         91 return 1;
116             }
117              
118             # Purpose: attempt to locate the nslookup utility
119             sub _find_nslookup {
120 0     0   0 my $self = shift;
121              
122 0         0 my $ns = 'nslookup';
123 0         0 foreach my $path (@NSLOOKUP_PATHS) {
124 0         0 my $file = File::Spec->catfile($path, $ns);
125 0 0 0     0 return "$file.exe" if ($^O eq 'MSWin32') and -x "$file.exe" and !-d _;
      0        
126 0 0 0     0 return $file if -x $file and !-d _;
127             }
128 0         0 return undef;
129             }
130              
131             sub _select_dns_method {
132             # Configure a global resolver object for DNS queries
133             # if Net::DNS is available
134 2     2   2 eval { require Net::DNS };
  2         841  
135 2 50       113615 return $DNS_Method = 'Net::DNS' unless $@;
136              
137 0         0 $DNS_Method = 'nslookup';
138             }
139              
140             # Purpose: perform DNS query using the Net::DNS module
141             sub _net_dns_query {
142 5     5   5 my $self = shift;
143 5         6 my $host = shift;
144              
145 5 50       15 $Resolver = Net::DNS::Resolver->new unless defined $Resolver;
146              
147 5         19 my @mx_entries = Net::DNS::mx($Resolver, $host);
148              
149             # Check for valid MX records for $host
150 5 100       224214 if (@mx_entries) {
151 2         5 foreach my $mx (@mx_entries) {
152 2         8 my $mxhost = $mx->exchange;
153 2         161 my $query = $Resolver->search($mxhost);
154 2 50       88789 next unless ($query);
155 2         8 foreach my $a_rr ($query->answer) {
156 2 50       19 return 1 unless $a_rr->type ne 'A';
157             }
158             }
159             }
160              
161             # Check for A record for $host
162 3         13 my $ans = $Resolver->query($host, 'A');
163 3 50       199168 my @a_rrs = $ans ? $ans->answer : ();
164              
165 3 50       14 if (@a_rrs) {
166 0         0 foreach my $a_rr (@a_rrs) {
167 0 0       0 return 1 unless $a_rr->type ne 'A';
168             }
169             }
170              
171             # MX Check failed
172 3         15 return $self->details('mx');
173             }
174              
175             # Purpose: perform DNS query using the nslookup utility
176             sub _nslookup_query {
177 0     0   0 my $self = shift;
178 0         0 my $host = shift;
179 0         0 local($/, *OLDERR);
180              
181 0 0       0 unless ($Nslookup_Path) {
182 0 0       0 $Nslookup_Path = $self->_find_nslookup
183             or croak 'unable to locate nslookup';
184             }
185              
186             # Check for an A record
187 0 0       0 return 1 if gethostbyname $host;
188              
189             # Check for an MX record
190 0 0 0     0 if ($^O eq 'MSWin32' or $^O eq 'Cygwin') {
191             # Oh no, we're on Windows!
192 0         0 require IO::CaptureOutput;
193 0         0 my $response = IO::CaptureOutput::capture_exec(
194             $Nslookup_Path, '-query=mx', $host
195             );
196 0 0       0 croak "unable to execute nslookup '$Nslookup_Path': exit $?" if $?;
197 0 0       0 print STDERR $response if $Debug;
198 0 0       0 $response =~ /$NSLOOKUP_PAT/io or return $self->details('mx');
199 0         0 return 1;
200             } else {
201             # phew, we're not on Windows!
202 0 0       0 if (my $fh = IO::File->new('-|')) {
203 0         0 my $response = <$fh>;
204 0 0       0 print STDERR $response if $Debug;
205 0         0 close $fh;
206 0 0       0 $response =~ /$NSLOOKUP_PAT/io or return $self->details('mx');
207 0         0 return 1;
208             } else {
209 0 0       0 open OLDERR, '>&STDERR' or croak "cannot dup stderr: $!";
210 0 0       0 open STDERR, '>&STDOUT' or croak "cannot redirect stderr to stdout: $!";
211             {
212 0         0 exec $Nslookup_Path, '-query=mx', $host;
  0         0  
213             }
214 0         0 open STDERR, ">&OLDERR";
215 0         0 croak "unable to execute nslookup '$Nslookup_Path': $!";
216             }
217             }
218             }
219              
220             # Purpose: Check whether a top level domain is valid for a domain.
221             sub tld {
222 0     0 1 0 my $self = shift;
223 0         0 my %args = $self->_rearrange([qw( address )], \@_);
224              
225 0 0       0 unless (eval {require Net::Domain::TLD; Net::Domain::TLD->VERSION(1.65); 1}) {
  0         0  
  0         0  
  0         0  
226 0         0 die "Net::Domain::TLD not available";
227             }
228              
229 0   0     0 my $host = $self->_host( $args{address} or return $self->details('tld') );
230 0         0 my ($tld) = $host =~ m#\.(\w+)$#;
231              
232 0         0 my %invalid_tlds = map { $_ => 1 } qw(invalid test example localhost);
  0         0  
233              
234 0 0       0 return defined $invalid_tlds{$tld} ? 0 : Net::Domain::TLD::tld_exists($tld);
235             }
236              
237             # Purpose: Check whether a DNS record (A or MX) exists for a domain.
238             sub mx {
239 5     5 1 38 my $self = shift;
240 5         17 my %args = $self->_rearrange([qw( address )], \@_);
241              
242 5 50       18 my $host = $self->_host($args{address}) or return $self->details('mx');
243              
244 5 50       12 $self->_select_dns_method unless $DNS_Method;
245              
246 5 50       11 if ($DNS_Method eq 'Net::DNS') {
    0          
247 5 50       20 print STDERR "using Net::DNS for dns query\n" if $Debug;
248 5         17 return $self->_net_dns_query( $host );
249             } elsif ($DNS_Method eq 'nslookup') {
250 0 0       0 print STDERR "using nslookup for dns query\n" if $Debug;
251 0         0 return $self->_nslookup_query( $host );
252             } else {
253 0         0 croak "unknown DNS method '$DNS_Method'";
254             }
255             }
256              
257             # Purpose: convert address to host
258             # Returns: host
259              
260             sub _host {
261 5     5   6 my $self = shift;
262 5         7 my $addr = shift;
263              
264 5 50 33     17 $addr = $addr->address if (blessed($addr) && $addr->isa('Mail::Address'));
265              
266 5 50       15 my $host = ($addr =~ /^.*@(.*)$/ ? $1 : $addr);
267 5         8 $host =~ s/\s+//g;
268              
269             # REMOVE BRACKETS IF IT'S A DOMAIN-LITERAL
270             # RFC822 3.4.6
271             # Square brackets ("[" and "]") are used to indicate the
272             # presence of a domain-literal, which the appropriate
273             # name-domain is to use directly, bypassing normal
274             # name-resolution mechanisms.
275 5         23 $host =~ s/(^\[)|(\]$)//g;
276 5         12 $host;
277             }
278              
279             # Purpose: Fix common addressing errors
280             # Returns: Possibly modified address
281             sub _fudge {
282 1     1   1 my $self = shift;
283 1         2 my $addr = shift;
284              
285 1 50       7 $addr =~ s/\s+//g if $addr =~ /aol\.com$/i;
286 1 50       4 $addr =~ s/,/./g if $addr =~ /compuserve\.com$/i;
287 1         1 $addr;
288             }
289              
290             # Purpose: Special address restrictions on a per-domain basis.
291             # Caveats: These organizations may change their rules at any time.
292             sub _local_rules {
293 0     0   0 my $self = shift;
294 0         0 my($user, $host) = @_;
295              
296 0         0 1;
297             }
298              
299             sub _valid_local_part {
300 27     27   219 my ($self, $localpart) = @_;
301              
302 27 100 66     96 return 0 unless defined $localpart and length $localpart <= 64;
303              
304 26         45 return 1;
305             }
306              
307             sub _valid_domain_parts {
308 24     24   145 my ($self, $string) = @_;
309              
310 24 50 33     77 return unless $string and length $string <= 255;
311 24 50       40 return if $string =~ /\.\./;
312 24         103 my @labels = split /\./, $string;
313              
314 24         32 for my $label (@labels) {
315 51 100       62 return 0 unless $self->_is_domain_label($label);
316             }
317 21         29 return scalar @labels;
318             }
319              
320             sub _is_domain_label {
321 51     51   43 my ($self, $string) = @_;
322 51 100       143 return unless $string =~ /\A
323             [A-Z0-9] # must start with an alnum
324             (?:
325             [-A-Z0-9]* # then maybe a dash or alnum
326             [A-Z0-9] # finally ending with an alnum
327             )? # lather, rinse, repeat
328             \z/ix;
329 48         85 return 1;
330             }
331              
332             # Purpose: Put an address through a series of checks to determine
333             # whether it should be considered valid.
334             sub address {
335 31     31 1 3692 my $self = shift;
336 31         112 my %args = $self->_rearrange([qw( address fudge mxcheck tldcheck fqdn
337             local_rules )], \@_);
338              
339 31 50       90 my $addr = $args{address} or return $self->details('rfc822');
340 31 100 66     111 $addr = $addr->address if (blessed($addr) && $addr->isa('Mail::Address'));
341              
342 31 100       65 $addr = $self->_fudge( $addr ) if $args{fudge};
343 31 100       74 $self->rfc822( -address => $addr ) or return undef;
344              
345 28         90 ($addr) = Mail::Address->parse( $addr );
346              
347 28 50       3118 $addr or return $self->details('rfc822'); # This should never happen
348              
349 28 100       54 if (length($addr->address) > 254) {
350 1         7 return $self->details('address_too_long');
351             }
352              
353 27 50       165 if ($args{local_rules}) {
354 0 0       0 $self->_local_rules( $addr->user, $addr->host )
355             or return $self->details('local_rules');
356             }
357              
358 27 50       54 if ($args{localpart}) {
359 27 100       49 $self->_valid_local_part($addr->user) > 0
360             or return $self->details('localpart');
361             }
362              
363 26   100     82 my $ip_ok = $args{allow_ip} && $addr->host =~ /\A\[
364             (?:[0-9]{1,3}\.){3}[0-9]{1,3}
365             /x;
366              
367 26 100 66     255 if (! $ip_ok && $args{fqdn}) {
368 24         41 my $domain_parts = $self->_valid_domain_parts($addr->host);
369              
370 24 100 100     107 return $self->details('fqdn')
      33        
371             unless $ip_ok || ($domain_parts && $domain_parts > 1);
372             }
373              
374 21 50 66     56 if (! $ip_ok && $args{tldcheck}) {
375 0 0       0 $self->tld( $addr->host ) or return $self->details('tldcheck');
376             }
377              
378 21 100       30 if ($args{mxcheck}) {
379             # I'm not sure this ->details call is needed, but I'll test for it later.
380             # The whole ->details thing is... weird. -- rjbs, 2006-06-08
381 5 100       14 $self->mx( $addr->host ) or return $self->details('mxcheck');
382             }
383              
384 18 50       114 return (wantarray ? ($addr->address, $addr) : $addr->address);
385             }
386              
387             sub AUTOLOAD {
388 0     0     my $self = shift;
389 0   0       my $type = ref($self) || die "$self is not an object";
390 0           my $name = our $AUTOLOAD;
391              
392 0           $name =~ s/.*://;
393 0 0         return if $name eq 'DESTROY';
394 0 0         die "unknown autoload name '$name'" unless $AUTOLOAD{$name};
395              
396 0 0         return (@_ ? $self->{$name} = shift : $self->{$name});
397             }
398              
399             # Regular expression built using Jeffrey Friedl's example in
400             # _Mastering Regular Expressions_ (http://www.ora.com/catalog/regexp/).
401              
402             $RFC822PAT = <<'EOF';
403             [\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\
404             xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xf
405             f\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\x
406             ff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015
407             "]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\
408             xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80
409             -\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*
410             )*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\
411             \\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\
412             x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x8
413             0-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n
414             \015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x
415             80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
416             \x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
417             \t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([
418             ^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\
419             \\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\
420             x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-
421             \xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()
422             ]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\
423             x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\04
424             0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\
425             n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\
426             015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?!
427             [^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\
428             ]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\
429             x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\01
430             5()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".
431             \\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]
432             )|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^
433             ()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\0
434             15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][
435             ^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\
436             n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\
437             x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?
438             :(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-
439             \xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*
440             (?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015
441             ()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()
442             ]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\0
443             40)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\
444             [^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\
445             xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*
446             )*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80
447             -\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x
448             80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t
449             ]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\
450             \[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])
451             *\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x
452             80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80
453             -\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015(
454             )]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\
455             \x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t
456             ]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\0
457             15()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015
458             ()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(
459             \040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|
460             \\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80
461             -\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()
462             ]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x
463             80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
464             \x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
465             \t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".
466             \\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff
467             ])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\
468             \x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
469             80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015
470             ()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\
471             \\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^
472             (\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-
473             \037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\
474             n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|
475             \([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))
476             [^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff
477             \n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
478             ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(
479             ?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\
480             000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\
481             xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\x
482             ff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)
483             *\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\x
484             ff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-
485             \xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)
486             *(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\
487             ]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\]
488             )[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-
489             \xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\x
490             ff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(
491             ?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80
492             -\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<
493             >@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x8
494             0-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:
495             \([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]
496             *(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)
497             *\)[\040\t]*)*)*>)
498             EOF
499              
500             $RFC822PAT =~ s/\n//g;
501              
502             1;
503              
504              
505             #pod =head1 SYNOPSIS
506             #pod
507             #pod use Email::Valid;
508             #pod my $address = Email::Valid->address('maurice@hevanet.com');
509             #pod print ($address ? 'yes' : 'no');
510             #pod
511             #pod =head1 DESCRIPTION
512             #pod
513             #pod This module determines whether an email address is well-formed, and
514             #pod optionally, whether a mail host exists for the domain.
515             #pod
516             #pod Please note that there is no way to determine whether an
517             #pod address is deliverable without attempting delivery
518             #pod (for details, see L).
519             #pod
520             #pod =head1 PREREQUISITES
521             #pod
522             #pod This module requires perl 5.004 or later and the L module.
523             #pod Either the L module or the nslookup utility is required
524             #pod for DNS checks. The L module is required to check the
525             #pod validity of top level domains.
526             #pod
527             #pod =head1 METHODS
528             #pod
529             #pod Every method which accepts an C<<
>> parameter may
530             #pod be passed either a string or an instance of the Mail::Address
531             #pod class. All errors raise an exception.
532             #pod
533             #pod =over 4
534             #pod
535             #pod =item new ( [PARAMS] )
536             #pod
537             #pod This method is used to construct an Email::Valid object.
538             #pod It accepts an optional list of named parameters to
539             #pod control the behavior of the object at instantiation.
540             #pod
541             #pod The following named parameters are allowed. See the
542             #pod individual methods below for details.
543             #pod
544             #pod -mxcheck
545             #pod -tldcheck
546             #pod -fudge
547             #pod -fqdn
548             #pod -allow_ip
549             #pod -local_rules
550             #pod
551             #pod =item mx (
| )
552             #pod
553             #pod This method accepts an email address or domain name and determines
554             #pod whether a DNS record (A or MX) exists for it.
555             #pod
556             #pod The method returns true if a record is found and undef if not.
557             #pod
558             #pod Either the Net::DNS module or the nslookup utility is required for
559             #pod DNS checks. Using Net::DNS is the preferred method since error
560             #pod handling is improved. If Net::DNS is available, you can modify
561             #pod the behavior of the resolver (e.g. change the default tcp_timeout
562             #pod value) by manipulating the global L instance stored in
563             #pod C<$Email::Valid::Resolver>.
564             #pod
565             #pod =item rfc822 (
)
566             #pod
567             #pod This method determines whether an address conforms to the RFC822
568             #pod specification (except for nested comments). It returns true if it
569             #pod conforms and undef if not.
570             #pod
571             #pod =item fudge ( | )
572             #pod
573             #pod Specifies whether calls to address() should attempt to correct
574             #pod common addressing errors. Currently, this results in the removal of
575             #pod spaces in AOL addresses, and the conversion of commas to periods in
576             #pod Compuserve addresses. The default is false.
577             #pod
578             #pod =item allow_ip ( | )
579             #pod
580             #pod Specifies whether a "domain literal" is acceptable as the domain part. That
581             #pod means addresses like: C
582             #pod
583             #pod The checking for the domain literal is stricter than the RFC and looser than
584             #pod checking for a valid IP address, I.
585             #pod
586             #pod The default is true.
587             #pod
588             #pod =item fqdn ( | )
589             #pod
590             #pod Specifies whether addresses passed to address() must contain a fully
591             #pod qualified domain name (FQDN). The default is true.
592             #pod
593             #pod B FQDN checks only occur for non-domain-literals. In other
594             #pod words, if you have set C and the address ends in a bracketed IP
595             #pod address, the FQDN check will not occur.
596             #pod
597             #pod =item tld (
)
598             #pod
599             #pod This method determines whether the domain part of an address is in a
600             #pod recognized top-level domain.
601             #pod
602             #pod B TLD checks only occur for non-domain-literals. In other
603             #pod words, if you have set C and the address ends in a bracketed IP
604             #pod address, the TLD check will not occur.
605             #pod
606             #pod =item local_rules ( | )
607             #pod
608             #pod Specifies whether addresses passed to address() should be tested
609             #pod for domain specific restrictions. Currently, this is limited to
610             #pod certain AOL restrictions that I'm aware of. The default is false.
611             #pod
612             #pod =item mxcheck ( | )
613             #pod
614             #pod Specifies whether addresses passed to address() should be checked
615             #pod for a valid DNS entry. The default is false.
616             #pod
617             #pod =item tldcheck ( | )
618             #pod
619             #pod Specifies whether addresses passed to address() should be checked
620             #pod for a valid top level domains. The default is false.
621             #pod
622             #pod =item address (
)
623             #pod
624             #pod This is the primary method which determines whether an email
625             #pod address is valid. Its behavior is modified by the values of
626             #pod mxcheck(), tldcheck(), local_rules(), fqdn(), and fudge(). If the address
627             #pod passes all checks, the (possibly modified) address is returned as
628             #pod a string. Otherwise, undef is returned.
629             #pod In a list context, the method also returns an instance of the
630             #pod Mail::Address class representing the email address.
631             #pod
632             #pod =item details ()
633             #pod
634             #pod If the last call to address() returned undef, you can call this
635             #pod method to determine why it failed. Possible values are:
636             #pod
637             #pod rfc822
638             #pod localpart
639             #pod local_rules
640             #pod fqdn
641             #pod mxcheck
642             #pod tldcheck
643             #pod
644             #pod If the class is not instantiated, you can get the same information
645             #pod from the global C<$Email::Valid::Details>.
646             #pod
647             #pod =back
648             #pod
649             #pod =head1 EXAMPLES
650             #pod
651             #pod Let's see if the address 'maurice@hevanet.com' conforms to the
652             #pod RFC822 specification:
653             #pod
654             #pod print (Email::Valid->address('maurice@hevanet.com') ? 'yes' : 'no');
655             #pod
656             #pod Additionally, let's make sure there's a mail host for it:
657             #pod
658             #pod print (Email::Valid->address( -address => 'maurice@hevanet.com',
659             #pod -mxcheck => 1 ) ? 'yes' : 'no');
660             #pod
661             #pod Let's see an example of how the address may be modified:
662             #pod
663             #pod $addr = Email::Valid->address('Alfred Neuman ');
664             #pod print "$addr\n"; # prints Neuman@foo.bar
665             #pod
666             #pod Now let's add the check for top level domains:
667             #pod
668             #pod $addr = Email::Valid->address( -address => 'Neuman@foo.bar',
669             #pod -tldcheck => 1 );
670             #pod print "$addr\n"; # doesn't print anything
671             #pod
672             #pod Need to determine why an address failed?
673             #pod
674             #pod unless(Email::Valid->address('maurice@hevanet')) {
675             #pod print "address failed $Email::Valid::Details check.\n";
676             #pod }
677             #pod
678             #pod If an error is encountered, an exception is raised. This is really
679             #pod only possible when performing DNS queries. Trap any exceptions by
680             #pod wrapping the call in an eval block:
681             #pod
682             #pod eval {
683             #pod $addr = Email::Valid->address( -address => 'maurice@hevanet.com',
684             #pod -mxcheck => 1 );
685             #pod };
686             #pod warn "an error was encountered: $@" if $@;
687             #pod
688             #pod =head1 CREDITS
689             #pod
690             #pod Significant portions of this module are based on the ckaddr program
691             #pod written by Tom Christiansen and the RFC822 address pattern developed
692             #pod by Jeffrey Friedl. Neither were involved in the construction of this
693             #pod module; all errors are mine.
694             #pod
695             #pod Thanks very much to the following people for their suggestions and
696             #pod bug fixes:
697             #pod
698             #pod Otis Gospodnetic
699             #pod Kim Ryan
700             #pod Pete Ehlke
701             #pod Lupe Christoph
702             #pod David Birnbaum
703             #pod Achim
704             #pod Elizabeth Mattijsen (liz@dijkmat.nl)
705             #pod
706             #pod =head1 SEE ALSO
707             #pod
708             #pod L, L, L, L
709             #pod
710             #pod L -
711             #pod standard for the format of ARPA internet text messages.
712             #pod Superseded by L.
713             #pod
714             #pod =cut
715              
716             __END__