File Coverage

blib/lib/Email/Valid.pm
Criterion Covered Total %
statement 145 203 71.4
branch 67 140 47.8
condition 29 58 50.0
subroutine 23 27 85.1
pod 6 6 100.0
total 270 434 62.2


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