/*
- Copyright 2009 Dominic Sayers
- Dominic_sayers@hotmail.com)
- Http://www.dominicsayers.com)
This source file is subject to the Common Public Attribution License Version 1.0 (CPAL) license.
- The license terms are available through the world-wide-web at http://www.opensource.org/licenses/cpal_1.0
- */
- Function is_email ($ email, $ checkDNS = false ){
- // Check that $ email is a valid address
- // (Http://tools.ietf.org/html/rfc3696)
- // (Http://tools.ietf.org/html/rfc5322#section-3.4.1)
- // (Http://tools.ietf.org/html/rfc5321#section-4.1.3)
- // (Http://tools.ietf.org/html/rfc4291#section-2.2)
- // (Http://tools.ietf.org/html/rfc1123#section-2.1)
-
- // Contemporary email addresses consist of a "local part" separated from
- // A "domain part" (a fully-qualified domain name) by an at-sign ("@").
- // (Http://tools.ietf.org/html/rfc3696#section-3)
- $ Index = strrpos ($ email ,'@');
If ($ index = false) return false; // No at-sign
- If ($ index = 0) return false; // No local part
- If ($ index> 64) return false; // Local part too long
$ LocalPart = substr ($ email, 0, $ index );
- $ Domain = substr ($ email, $ index + 1 );
- $ DomainLength = strlen ($ domain );
-
- If ($ domainLength = 0) return false; // No domain part
- If ($ domainLength> 255) return false; // Domain part too long
// Let's check the local part for RFC compliance...
- //
- // Period (".") may... appear, but may not be used to start or end
- // Local part, nor may two or more consecutive periods appear.
- // (Http://tools.ietf.org/html/rfc3696#section-3)
- If (preg_match ('/^ \. | \. \. | \. $/', $ localPart)> 0) return false; // Dots in wrong place
// Any ASCII graphic (printing) character other than
- // At-sign ("@"), backslash, double quote, comma, or square brackets may
- // Appear without quoting. If any of that list of excluded characters
- // Are to appear, they must be quoted
- // (Http://tools.ietf.org/html/rfc3696#section-3)
- If (preg_match ('/^ "(? :.) * "$/', $ LocalPart)> 0 ){
- // Local part is a quoted string
- If (preg_match ('/(? :.) + [^ \] "(? :.) +/', $ LocalPart)> 0) return false; // Unescaped quote character inside quoted string
- } Else {
- If (preg_match ('/[@ \ [\] \ ",]/', $ localPart)> 0)
- // Check all excluded characters are escaped
- $ Stripped = preg_replace ('/\\\ [\ [\\] \\\ ",]/','', $ localPart );
- If (preg_match ('/[@ \ [\] \ ",]/', $ stripped)> 0) return false; // Unquoted excluded characters
- }
// Now let's check the domain part...
// The domain name can also be replaced by an IP address in square brackets
- // (Http://tools.ietf.org/html/rfc3696#section-3)
- // (Http://tools.ietf.org/html/rfc5321#section-4.1.3)
- // (Http://tools.ietf.org/html/rfc4291#section-2.2)
- If (preg_match ('/^ \ [(.) +] $/', $ domain) === 1 ){
- // It's an address-literal
- $ AddressLiteral = substr ($ domain, 1, $ domainLength-2 );
- $ MatchesIP = array ();
-
- // Extract IPv4 part from the end of the address-literal (if there is one)
- If (preg_match ('/\ B (? :(? : 25 [0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) \.) {3 }(? : 25 [0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) $/', $ AddressLiteral, $ matchesIP)> 0 ){
- $ Index = strrpos ($ addressLiteral, $ matchesIP [0]);
-
- If ($ index = 0 ){
- // Nothing there handle T a valid IPv4 address, so...
- Return true;
- } Else {
- // Assume it's an attempt at a mixed address (IPv6 + IPv4)
- If ($ addressLiteral [$ index-1]! = ':') Return false; // Character preceding IPv4 address must be ':'
- If (substr ($ addressLiteral, 0, 5 )! = 'Ipv6: ') return false; // RFC5321 section 4.1.3
$ IPv6 = substr ($ addressLiteral, 5, ($ index = 7 )? 2: $ index-6 );
- $ GroupMax = 6;
- }
- } Else {
- // It must be an attempt at pure IPv6
- If (substr ($ addressLiteral, 0, 5 )! = 'Ipv6: ') return false; // RFC5321 section 4.1.3
- $ IPv6 = substr ($ addressLiteral, 5 );
- $ GroupMax = 8;
- }
$ GroupCount = preg_match_all ('/^ [0-9a-fA-F] {} |\\: [0-9a-fA-F] {} | (.)/', $ IPv6, $ matchesIP );
- $ Index = strpos ($ IPv6 ,'::');
If ($ index = false ){
- // We need exactly the right number of groups
- If ($ groupCount! ==$ GroupMax) return false; // RFC5321 section 4.1.3
- } Else {
- If ($ index! = Strrpos ($ IPv6, ':') return false; // More than one '::'
- $ GroupMax = ($ index = 0 | $ index = (strlen ($ IPv6)-2 ))? $ GroupMax: $ groupMax-1;
- If ($ groupCount> $ groupMax) return false; // Too quota IPv6 groups in address
- }
// Check for unmatched characters
- Array_multisort ($ matchesIP [1], SORT_DESC );
- If ($ matchesIP [1] [0]! = '') Return false; // Illegal characters in address
// It's a valid IPv6 address, so...
- Return true;
- } Else {
- // It's a domain name...
// The syntax of a legal Internet host name was specified in RFC-952
- // One aspect of host name syntax is hereby changed:
- // Restriction on the first character is relaxed to allow either
- // Letter or a digit.
- // (Http://tools.ietf.org/html/rfc1123#section-2.1)
- //
- // Nb rfc 1123 updates RFC 1035, but this is not currently apparent from reading RFC 1035.
- //
- // Most common applications, including email and the Web, will generally not permit... escaped strings
- // (Http://tools.ietf.org/html/rfc3696#section-2)
- //
- // Characters outside the set of alphabetic characters, digits, and hyphen must not appear in domain name
- // Labels for SMTP clients or servers
- // (Http://tools.ietf.org/html/rfc5321#section-4.1.2)
- //
- // RFC5321 precludes the use of a trailing dot in a domain name for SMTP purposes
- // (Http://tools.ietf.org/html/rfc5321#section-4.1.2)
- $ Matches = array ();
- $ GroupCount = preg_match_all ('/(? : [0-9a-zA-Z] [0-9a-zA-Z-] {0, 61} [0-9a-zA-Z] | [a-zA-Z]) (? : \. | $) | (.)/', $ Domain, $ matches );
- $ Level = count ($ matches [0]);
If ($ level = 1) return false; // Mail host can't be a TLD
$ TLD = $ matches [0] [$ level-1];
- If (substr ($ TLD, strlen ($ TLD)-1, 1) ==='. ') return false; // TLD can't end in a dot
- If (preg_match ('/^ [0-9] + $/', $ TLD)> 0) return false; // TLD can't be all-numeric
// Check for unmatched characters
- Array_multisort ($ matches [1], SORT_DESC );
- If ($ matches [1] [0]! = '') Return false; // Illegal characters in domain, or label longer than 63 characters
// Check DNS?
- If ($ checkDNS & function_exists ('checkdnsrr ')){
- If (! (Checkdnsrr ($ domain, 'A') | checkdnsrr ($ domain, 'MX '))){
- Return false; // Domain doesn' t actually exist
- }
- }
// Eliminate all other factors, and the one which remains must be the truth.
- // (Sherlock Holmes, The Sign of Four)
- Return true;
- }
- }
Function unitTest ($ email, $ reason = ''){
- $ Expected = ($ reason = '')? True: false;
- $ Valid = is_email ($ email );
- $ Not = ($ valid )? '': 'Not ';
- $ Unexpected = ($ valid! ==$ Expected )? 'This was unexpected!':'';
- $ Reason = ($ reason = '')? "": "Reason: $ reason ";
-
- Return "The address$ EmailIs $ not valid. $ unexpected $ reason
\ N ";
- }
// Email validator test cases (Dominic Sayers, January 2009)
- // Valid addresses
- Echo unitTest ('First. last@example.com ');
- Echo unitTest ('1970 @ example.com ');
- Echo unitTest ('"first last" @ example.com ');
- Echo unitTest ('"first \" last "@ example.com'); // Not totally sure whether this is valid or not
- Echo unitTest ('First \ @ last@example.com ');
- Echo unitTest ('"first @ last" @ example.com ');
- Echo unitTest ('First \ last@example.com '); // Note that \ is escaped even in single-quote strings, so this is testing "first \ last" @ example.com
- Echo unitTest ('first. last@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.
- Examples. x23456789.x23456789. x23456789.x23456789. x23456789.x23456789. x23456789.x23456789. x23456789.x2345 ');
- Echo unitTest ('First. last @ [12.34.56.78] ');
- Echo unitTest ('First. last @ [IPv6: 12.34.56.78] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 12.34.56.78] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666: 12.34.56.78] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666:] ');
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666: 7777: 8888] ');
- Echo unitTest ('First. last@x23456789012345678901234567890123456789012345678901234567890123.example.com ');
- Echo unitTest ('First. last@1xample.com ');
- Echo unitTest ('First. last@123.example.com ');
// Invalid addresses
- Echo unitTest ('First. la', "No @");
- Echo unitTest ('@ example.com', "No local part ");
- Echo unitTest ('1970 @ example.com ', "Local part more than 64 characters ");
- Echo unitTest ('. first.last@example.com', "Local part starts with a dot ");
- Echo unitTest ('First. last.@example.com ', "Local part ends with a dot ");
- Echo unitTest ('First .. ts ', "Local part has consecutive dots ");
- Echo unitTest ('"first" last "@ example.com'," Local part contains unescaped excluded characters ");
- Echo unitTest ('First \\\@ last@example.com ', "Local part contains unescaped excluded characters ");
- Echo unitTest ('First. last @ ', "No domain ");
- Echo unitTest ('first. last@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.
- Examples. Examples. x23456789.x23456789. x23456789.x23456789. x23456789.x23456789. x23456789.x23456', "Domain exceeds 255 chars ");
- Echo unitTest ('First. last @ [. 12.34.56.78] ', "Only char that can precede IPv4 address is ':'");
- Echo unitTest ('First. last @ [12.34.56.789] ', "Can't be interpreted as IPv4 so IPv6 tag is missing ");
- Echo unitTest ('First. last @ [: 12.34.56.78] ', "IPv6 tag is missing ");
- Echo unitTest ('First. last @ [Protocol 5: 12.34.56.78] ', "IPv6 tag is wrong ");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 12.34.56.78] ', "Too ipvipv6 groups (4 max )");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 12.34.56.78] ', "Not enough IPv6 groups ");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666: 7777: 12.34.56.78] ', "Too ipvipv6 groups (6 max )");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666] ', "Not enough IPv6 groups ");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666: 7777: 8888: 9999] ', "Too ipvipv6 groups (8 max )");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555] ', "Too comment': '(can be none or one )");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 3333: 4444: 5555: 6666] ', "Too worker IPv6 groups (6 max )");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 333x: 4444: 5555] ', "x is not valid in an IPv6 address ");
- Echo unitTest ('First. last @ [IPv6: 1111: 2222: 33333: 4444: 5555] ', "33333 is not a valid group in an IPv6 address ");
- Echo unitTest ('First. last@example.123 ', "TLD can't be all digits ");
- Echo unitTest ('First. last @ com ', "Mail host must be second-or lower level ");
- Echo unitTest ('First. last@-xample.com ', "Label can't begin with a hyphen ");
- Echo unitTest ('First. last@exampl-.com ', "Label can't end with a hyphen ");
- Echo unitTest ('First. last@x234567890123456789012345678901234567890123456789012345678901234.example.com ', "Label can't be longer than 63 ETS ");
// Test cases from rfc00006 (February 2004, http://tools.ietf.org/html/rfc3696#section-3)
- Echo unitTest ('ABC \ @ def@example.com ');
- Echo unitTest ('Fred \ Bloggs@example.com ');
- Echo unitTest ('Joe. \\\ Blow@example.com ');
- Echo unitTest ('"Abc @ def" @ example.com ');
- Echo unitTest ('"Fred Bloggs" @ example.com ');
- Echo unitTest ('User + mailbox@example.com ');
- Echo unitTest ('customer/department = shipping@example.com ');
- Echo unitTest ('$ A12345@example.com ');
- Echo unitTest ('! Def! Xyz % abc@example.com ');
- Echo unitTest ('_ somename@example.com ');
// Test cases from Doug Lovell (LinuxJournal, June 2007, http://www.linuxjournal.com/article/9585)
- Echo unitTest ("dclo@us.ibm.com ");
- Echo unitTest ("abc \ @ def@example.com ");
- Echo unitTest ("abc \\\\@ example.com ");
- Echo unitTest ("Fred \ Bloggs@example.com ");
- Echo unitTest ("Joe. \ Blow@example.com ");
- Echo unitTest ("\" Abc @ def \ "@ example.com ");
- Echo unitTest ("\" Fred Bloggs \ "@ example.com ");
- Echo unitTest ("customer/department = shipping@example.com ");
- Echo unitTest ("\ $ A12345@example.com ");
- Echo unitTest ("! Def! Xyz % abc@example.com ");
- Echo unitTest ("_ somename@example.com ");
- Echo unitTest ("user + mailbox@example.com ");
- Echo unitTest ("peter.piper@example.com ");
- Echo unitTest ("Doug \" Ace \ "\ Lovell@example.com ");
- Echo unitTest ("\" Doug \ "Ace \" L. \ "@ example.com ");
- Echo unitTest ("abc @ def@example.com", "Doug Lovell says this shocould fail ");
- Echo unitTest ("abc \\\\@ def@example.com", "Doug Lovell says this shoshould fail ");
- Echo unitTest ("abc \ @ example.com", "Doug Lovell says this shoshould fail ");
- Echo unitTest ("@ example.com", "Doug Lovell says this showould fail ");
- Echo unitTest ("doug @", "Doug Lovell says this shoshould fail ");
- Echo unitTest ("\" qu@example.com "," Doug Lovell says this shocould fail ");
- Echo unitTest ("ote \" @ example.com "," Doug Lovell says this shoshould fail ");
- Echo unitTest (". dot@example.com", "Doug Lovell says this shocould fail ");
- Echo unitTest ("dot.@example.com", "Doug Lovell says this shocould fail ");
- Echo unitTest ("two..dot@example.com", "Doug Lovell says this shocould fail ");
- Echo unitTest ("\" Doug \ "Ace \" L. \ "@ example.com", "Doug Lovell says this shoshould fail ");
- Echo unitTest ("Doug \" Ace \ "\ L \. @ example.com", "Doug Lovell says this shoshould fail ");
- Echo unitTest ("hello world@example.com", "Doug Lovell says this shocould fail ");
- Echo unitTest ("gatsby@f. SC .ot.t.f. I .tzg.era.l.d.", "Doug Lovell says this shocould fail ");
- ?>
|