-<?php\r
-/**\r
- * To validate an email address according to RFCs 5321, 5322 and others\r
- * \r
- * Copyright © 2008-2011, Dominic Sayers <br>\r
- * Test schema documentation Copyright © 2011, Daniel Marschall <br>\r
- * All rights reserved.\r
- * \r
- * Redistribution and use in source and binary forms, with or without modification,\r
- * are permitted provided that the following conditions are met:\r
- * \r
- * - Redistributions of source code must retain the above copyright notice,\r
- * this list of conditions and the following disclaimer.\r
- * - Redistributions in binary form must reproduce the above copyright notice,\r
- * this list of conditions and the following disclaimer in the documentation\r
- * and/or other materials provided with the distribution.\r
- * - Neither the name of Dominic Sayers nor the names of its contributors may be\r
- * used to endorse or promote products derived from this software without\r
- * specific prior written permission.\r
- * \r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\r
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- * \r
- * @package is_email\r
- * @author Dominic Sayers <dominic@sayers.cc>\r
- * @copyright 2008-2011 Dominic Sayers\r
- * @license http://www.opensource.org/licenses/bsd-license.php BSD License\r
- * @link http://www.dominicsayers.com/isemail\r
- * @version 3.01.1 - Fixed examples and readme.txt\r
- */\r
-\r
-// The quality of this code has been improved greatly by using PHPLint\r
-// Copyright (c) 2010 Umberto Salsi\r
-// This is free software; see the license for copying conditions.\r
-// More info: http://www.icosaedro.it/phplint/\r
-/*.\r
- require_module 'standard';\r
- require_module 'pcre';\r
-.*/\r
-\r
-if (!defined('ISEMAIL_VALID')) {\r
-/*:diagnostic constants start:*/\r
-// This part of the code is generated using data from test/meta.xml. Beware of making manual alterations\r
- // Categories\r
- define('ISEMAIL_VALID_CATEGORY', 1);\r
- define('ISEMAIL_DNSWARN', 7);\r
- define('ISEMAIL_RFC5321', 15);\r
- define('ISEMAIL_CFWS', 31);\r
- define('ISEMAIL_DEPREC', 63);\r
- define('ISEMAIL_RFC5322', 127);\r
- define('ISEMAIL_ERR', 255);\r
-\r
- // Diagnoses\r
- // Address is valid\r
- define('ISEMAIL_VALID', 0);\r
- // Address is valid but a DNS check was not successful\r
- define('ISEMAIL_DNSWARN_NO_MX_RECORD', 5);\r
- define('ISEMAIL_DNSWARN_NO_RECORD', 6);\r
- // Address is valid for SMTP but has unusual elements\r
- define('ISEMAIL_RFC5321_TLD', 9);\r
- define('ISEMAIL_RFC5321_TLDNUMERIC', 10);\r
- define('ISEMAIL_RFC5321_QUOTEDSTRING', 11);\r
- define('ISEMAIL_RFC5321_ADDRESSLITERAL', 12);\r
- define('ISEMAIL_RFC5321_IPV6DEPRECATED', 13);\r
- // Address is valid within the message but cannot be used unmodified for the envelope\r
- define('ISEMAIL_CFWS_COMMENT', 17);\r
- define('ISEMAIL_CFWS_FWS', 18);\r
- // Address contains deprecated elements but may still be valid in restricted contexts\r
- define('ISEMAIL_DEPREC_LOCALPART', 33);\r
- define('ISEMAIL_DEPREC_FWS', 34);\r
- define('ISEMAIL_DEPREC_QTEXT', 35);\r
- define('ISEMAIL_DEPREC_QP', 36);\r
- define('ISEMAIL_DEPREC_COMMENT', 37);\r
- define('ISEMAIL_DEPREC_CTEXT', 38);\r
- define('ISEMAIL_DEPREC_CFWS_NEAR_AT', 49);\r
- // The address is only valid according to the broad definition of RFC 5322. It is otherwise invalid.\r
- define('ISEMAIL_RFC5322_DOMAIN', 65);\r
- define('ISEMAIL_RFC5322_TOOLONG', 66);\r
- define('ISEMAIL_RFC5322_LOCAL_TOOLONG', 67);\r
- define('ISEMAIL_RFC5322_DOMAIN_TOOLONG', 68);\r
- define('ISEMAIL_RFC5322_LABEL_TOOLONG', 69);\r
- define('ISEMAIL_RFC5322_DOMAINLITERAL', 70);\r
- define('ISEMAIL_RFC5322_DOMLIT_OBSDTEXT', 71);\r
- define('ISEMAIL_RFC5322_IPV6_GRPCOUNT', 72);\r
- define('ISEMAIL_RFC5322_IPV6_2X2XCOLON', 73);\r
- define('ISEMAIL_RFC5322_IPV6_BADCHAR', 74);\r
- define('ISEMAIL_RFC5322_IPV6_MAXGRPS', 75);\r
- define('ISEMAIL_RFC5322_IPV6_COLONSTRT', 76);\r
- define('ISEMAIL_RFC5322_IPV6_COLONEND', 77);\r
- // Address is invalid for any purpose\r
- define('ISEMAIL_ERR_EXPECTING_DTEXT', 129);\r
- define('ISEMAIL_ERR_NOLOCALPART', 130);\r
- define('ISEMAIL_ERR_NODOMAIN', 131);\r
- define('ISEMAIL_ERR_CONSECUTIVEDOTS', 132);\r
- define('ISEMAIL_ERR_ATEXT_AFTER_CFWS', 133);\r
- define('ISEMAIL_ERR_ATEXT_AFTER_QS', 134);\r
- define('ISEMAIL_ERR_ATEXT_AFTER_DOMLIT', 135);\r
- define('ISEMAIL_ERR_EXPECTING_QPAIR', 136);\r
- define('ISEMAIL_ERR_EXPECTING_ATEXT', 137);\r
- define('ISEMAIL_ERR_EXPECTING_QTEXT', 138);\r
- define('ISEMAIL_ERR_EXPECTING_CTEXT', 139);\r
- define('ISEMAIL_ERR_BACKSLASHEND', 140);\r
- define('ISEMAIL_ERR_DOT_START', 141);\r
- define('ISEMAIL_ERR_DOT_END', 142);\r
- define('ISEMAIL_ERR_DOMAINHYPHENSTART', 143);\r
- define('ISEMAIL_ERR_DOMAINHYPHENEND', 144);\r
- define('ISEMAIL_ERR_UNCLOSEDQUOTEDSTR', 145);\r
- define('ISEMAIL_ERR_UNCLOSEDCOMMENT', 146);\r
- define('ISEMAIL_ERR_UNCLOSEDDOMLIT', 147);\r
- define('ISEMAIL_ERR_FWS_CRLF_X2', 148);\r
- define('ISEMAIL_ERR_FWS_CRLF_END', 149);\r
- define('ISEMAIL_ERR_CR_NO_LF', 150);\r
-// End of generated code\r
-/*:diagnostic constants end:*/\r
-\r
- // function control\r
- define('ISEMAIL_THRESHOLD' , 16);\r
-\r
- // Email parts\r
- define('ISEMAIL_COMPONENT_LOCALPART' , 0);\r
- define('ISEMAIL_COMPONENT_DOMAIN' , 1);\r
- define('ISEMAIL_COMPONENT_LITERAL' , 2);\r
- define('ISEMAIL_CONTEXT_COMMENT' , 3);\r
- define('ISEMAIL_CONTEXT_FWS' , 4);\r
- define('ISEMAIL_CONTEXT_QUOTEDSTRING' , 5);\r
- define('ISEMAIL_CONTEXT_QUOTEDPAIR' , 6);\r
-\r
- // Miscellaneous string constants\r
- define('ISEMAIL_STRING_AT' , '@');\r
- define('ISEMAIL_STRING_BACKSLASH' , '\\');\r
- define('ISEMAIL_STRING_DOT' , '.');\r
- define('ISEMAIL_STRING_DQUOTE' , '"');\r
- define('ISEMAIL_STRING_OPENPARENTHESIS' , '(');\r
- define('ISEMAIL_STRING_CLOSEPARENTHESIS', ')');\r
- define('ISEMAIL_STRING_OPENSQBRACKET' , '[');\r
- define('ISEMAIL_STRING_CLOSESQBRACKET' , ']');\r
- define('ISEMAIL_STRING_HYPHEN' , '-');\r
- define('ISEMAIL_STRING_COLON' , ':');\r
- define('ISEMAIL_STRING_DOUBLECOLON' , '::');\r
- define('ISEMAIL_STRING_SP' , ' ');\r
- define('ISEMAIL_STRING_HTAB' , "\t");\r
- define('ISEMAIL_STRING_CR' , "\r");\r
- define('ISEMAIL_STRING_LF' , "\n");\r
- define('ISEMAIL_STRING_IPV6TAG' , 'IPv6:');\r
- // US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3)\r
- define('ISEMAIL_STRING_SPECIALS' , '()<>[]:;@\\,."');\r
-}\r
-\r
-/**\r
- * Check that an email address conforms to RFCs 5321, 5322 and others\r
- *\r
- * As of Version 3.0, we are now distinguishing clearly between a Mailbox\r
- * as defined by RFC 5321 and an addr-spec as defined by RFC 5322. Depending\r
- * on the context, either can be regarded as a valid email address. The\r
- * RFC 5321 Mailbox specification is more restrictive (comments, white space\r
- * and obsolete forms are not allowed)\r
- *\r
- * @param string $email The email address to check\r
- * @param boolean $checkDNS If true then a DNS check for MX records will be made\r
- * @param mixed $errorlevel Determines the boundary between valid and invalid addresses.\r
- * Status codes above this number will be returned as-is,\r
- * status codes below will be returned as ISEMAIL_VALID. Thus the\r
- * calling program can simply look for ISEMAIL_VALID if it is\r
- * only interested in whether an address is valid or not. The\r
- * errorlevel will determine how "picky" is_email() is about\r
- * the address.\r
- *\r
- * If omitted or passed as false then is_email() will return\r
- * true or false rather than an integer error or warning.\r
- *\r
- * NB Note the difference between $errorlevel = false and\r
- * $errorlevel = 0\r
- * @param array $parsedata If passed, returns the parsed address components\r
- */\r
-/*.mixed.*/ function is_email($email, $checkDNS = false, $errorlevel = false, &$parsedata = array()) {\r
- // Check that $email is a valid address. Read the following RFCs to understand the constraints:\r
- // (http://tools.ietf.org/html/rfc5321)\r
- // (http://tools.ietf.org/html/rfc5322)\r
- // (http://tools.ietf.org/html/rfc4291#section-2.2)\r
- // (http://tools.ietf.org/html/rfc1123#section-2.1)\r
- // (http://tools.ietf.org/html/rfc3696) (guidance only)\r
-// version 2.0: Enhance $diagnose parameter to $errorlevel\r
-// version 3.0: Introduced status categories\r
-// revision 3.1: BUG: $parsedata was passed by value instead of by reference\r
-\r
- if (is_bool($errorlevel)) {\r
- $threshold = ISEMAIL_VALID;\r
- $diagnose = (bool) $errorlevel;\r
- } else {\r
- $diagnose = true;\r
-\r
- switch ((int) $errorlevel) {\r
- case E_WARNING: $threshold = ISEMAIL_THRESHOLD; break; // For backward compatibility\r
- case E_ERROR: $threshold = ISEMAIL_VALID; break; // For backward compatibility\r
- default: $threshold = (int) $errorlevel;\r
- }\r
- }\r
-\r
- $return_status = array(ISEMAIL_VALID);\r
-\r
- // Parse the address into components, character by character\r
- $raw_length = strlen($email);\r
- $context = ISEMAIL_COMPONENT_LOCALPART; // Where we are\r
- $context_stack = array($context); // Where we have been\r
- $context_prior = ISEMAIL_COMPONENT_LOCALPART; // Where we just came from\r
- $token = ''; // The current character\r
- $token_prior = ''; // The previous character\r
- $parsedata = array(\r
- ISEMAIL_COMPONENT_LOCALPART => '',\r
- ISEMAIL_COMPONENT_DOMAIN => ''\r
- ); // For the components of the address\r
-\r
- $atomlist = array(\r
- ISEMAIL_COMPONENT_LOCALPART => array(''),\r
- ISEMAIL_COMPONENT_DOMAIN => array('')\r
- ); // For the dot-atom elements of the address\r
- $element_count = 0;\r
- $element_len = 0;\r
- $hyphen_flag = false; // Hyphen cannot occur at the end of a subdomain\r
- $end_or_die = false; // CFWS can only appear at the end of the element\r
-\r
-//-echo "<table style=\"clear:left;\">"; // debug\r
- for ($i = 0; $i < $raw_length; $i++) {\r
- $token = $email[$i];\r
-//-echo "<tr><td><strong>$context|",(($end_or_die) ? 'true' : 'false'),"|$token|" . max($return_status) . "</strong></td>"; // debug\r
-\r
- switch ($context) {\r
- //-------------------------------------------------------------\r
- // local-part\r
- //-------------------------------------------------------------\r
- case ISEMAIL_COMPONENT_LOCALPART:\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // local-part = dot-atom / quoted-string / obs-local-part\r
- //\r
- // dot-atom = [CFWS] dot-atom-text [CFWS]\r
- //\r
- // dot-atom-text = 1*atext *("." 1*atext)\r
- //\r
- // quoted-string = [CFWS]\r
- // DQUOTE *([FWS] qcontent) [FWS] DQUOTE\r
- // [CFWS]\r
- //\r
- // obs-local-part = word *("." word)\r
- //\r
- // word = atom / quoted-string\r
- //\r
- // atom = [CFWS] 1*atext [CFWS]\r
- switch ($token) {\r
- // Comment\r
- case ISEMAIL_STRING_OPENPARENTHESIS:\r
- if ($element_len === 0)\r
- // Comments are OK at the beginning of an element\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_CFWS_COMMENT : ISEMAIL_DEPREC_COMMENT;\r
- else {\r
- $return_status[] = ISEMAIL_CFWS_COMMENT;\r
- $end_or_die = true; // We can't start a comment in the middle of an element, so this better be the end\r
- }\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_COMMENT;\r
- break;\r
- // Next dot-atom element\r
- case ISEMAIL_STRING_DOT:\r
- if ($element_len === 0)\r
- // Another dot, already?\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS; // Fatal error\r
- else\r
- // The entire local-part can be a quoted string for RFC 5321\r
- // If it's just one atom that is quoted then it's an RFC 5322 obsolete form\r
- if ($end_or_die) $return_status[] = ISEMAIL_DEPREC_LOCALPART;\r
-\r
- $end_or_die = false; // CFWS & quoted strings are OK again now we're at the beginning of an element (although they are obsolete forms)\r
- $element_len = 0;\r
- $element_count++;\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] = '';\r
-\r
- break;\r
- // Quoted string\r
- case ISEMAIL_STRING_DQUOTE:\r
- if ($element_len === 0) {\r
- // The entire local-part can be a quoted string for RFC 5321\r
- // If it's just one atom that is quoted then it's an RFC 5322 obsolete form\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_RFC5321_QUOTEDSTRING : ISEMAIL_DEPREC_LOCALPART;\r
-\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;\r
- $element_len++;\r
- $end_or_die = true; // Quoted string must be the entire element\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_QUOTEDSTRING;\r
- } else {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error\r
- }\r
-\r
- break;\r
- // Folding White Space\r
- case ISEMAIL_STRING_CR:\r
- case ISEMAIL_STRING_SP:\r
- case ISEMAIL_STRING_HTAB:\r
- if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {$return_status[] = ISEMAIL_ERR_CR_NO_LF; break;} // Fatal error\r
-\r
- if ($element_len === 0)\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_CFWS_FWS : ISEMAIL_DEPREC_FWS;\r
- else\r
- $end_or_die = true; // We can't start FWS in the middle of an element, so this better be the end\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_FWS;\r
- $token_prior = $token;\r
-\r
- break;\r
- // @\r
- case ISEMAIL_STRING_AT:\r
- // At this point we should have a valid local-part\r
- if (count($context_stack) !== 1) die('Unexpected item on context stack');\r
-\r
- if ($parsedata[ISEMAIL_COMPONENT_LOCALPART] === '')\r
- $return_status[] = ISEMAIL_ERR_NOLOCALPART; // Fatal error\r
- elseif ($element_len === 0) $return_status[] = ISEMAIL_ERR_DOT_END; // Fatal error\r
- // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1\r
- // The maximum total length of a user name or other local-part is 64\r
- // octets.\r
- elseif (strlen($parsedata[ISEMAIL_COMPONENT_LOCALPART]) > 64)\r
- $return_status[] = ISEMAIL_RFC5322_LOCAL_TOOLONG;\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // Comments and folding white space\r
- // SHOULD NOT be used around the "@" in the addr-spec.\r
- //\r
- // http://tools.ietf.org/html/rfc2119\r
- // 4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that\r
- // there may exist valid reasons in particular circumstances when the\r
- // particular behavior is acceptable or even useful, but the full\r
- // implications should be understood and the case carefully weighed\r
- // before implementing any behavior described with this label.\r
- elseif (($context_prior === ISEMAIL_CONTEXT_COMMENT) || ($context_prior === ISEMAIL_CONTEXT_FWS))\r
- $return_status[] = ISEMAIL_DEPREC_CFWS_NEAR_AT;\r
-\r
- // Clear everything down for the domain parsing\r
- $context = ISEMAIL_COMPONENT_DOMAIN; // Where we are\r
- $context_stack = array($context); // Where we have been\r
- $element_count = 0;\r
- $element_len = 0;\r
- $end_or_die = false; // CFWS can only appear at the end of the element\r
-\r
- break;\r
- // atext\r
- default:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.3\r
- // atext = ALPHA / DIGIT / ; Printable US-ASCII\r
- // "!" / "#" / ; characters not including\r
- // "$" / "%" / ; specials. Used for atoms.\r
- // "&" / "'" /\r
- // "*" / "+" /\r
- // "-" / "/" /\r
- // "=" / "?" /\r
- // "^" / "_" /\r
- // "`" / "{" /\r
- // "|" / "}" /\r
- // "~"\r
- if ($end_or_die) {\r
- // We have encountered atext where it is no longer valid\r
- switch ($context_prior) {\r
- case ISEMAIL_CONTEXT_COMMENT:\r
- case ISEMAIL_CONTEXT_FWS:\r
- $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_CFWS;\r
- break;\r
- case ISEMAIL_CONTEXT_QUOTEDSTRING:\r
- $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_QS;\r
- break;\r
- default:\r
- die ("More atext found where none is allowed, but unrecognised prior context: $context_prior");\r
- }\r
- } else {\r
- $context_prior = $context;\r
- $ord = ord($token);\r
-\r
- if (($ord < 33) || ($ord > 126) || ($ord === 10) || (!is_bool(strpos(ISEMAIL_STRING_SPECIALS, $token))))\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error\r
-\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;\r
- $element_len++;\r
- }\r
- }\r
-\r
- break;\r
- //-------------------------------------------------------------\r
- // Domain\r
- //-------------------------------------------------------------\r
- case ISEMAIL_COMPONENT_DOMAIN:\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // domain = dot-atom / domain-literal / obs-domain\r
- //\r
- // dot-atom = [CFWS] dot-atom-text [CFWS]\r
- //\r
- // dot-atom-text = 1*atext *("." 1*atext)\r
- //\r
- // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]\r
- //\r
- // dtext = %d33-90 / ; Printable US-ASCII\r
- // %d94-126 / ; characters not including\r
- // obs-dtext ; "[", "]", or "\"\r
- //\r
- // obs-domain = atom *("." atom)\r
- //\r
- // atom = [CFWS] 1*atext [CFWS]\r
-\r
-\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.2\r
- // Mailbox = Local-part "@" ( Domain / address-literal )\r
- //\r
- // Domain = sub-domain *("." sub-domain)\r
- //\r
- // address-literal = "[" ( IPv4-address-literal /\r
- // IPv6-address-literal /\r
- // General-address-literal ) "]"\r
- // ; See Section 4.1.3\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // Note: A liberal syntax for the domain portion of addr-spec is\r
- // given here. However, the domain portion contains addressing\r
- // information specified by and used in other protocols (e.g.,\r
- // [RFC1034], [RFC1035], [RFC1123], [RFC5321]). It is therefore\r
- // incumbent upon implementations to conform to the syntax of\r
- // addresses for the context in which they are used.\r
- // is_email() author's note: it's not clear how to interpret this in\r
- // the context of a general email address validator. The conclusion I\r
- // have reached is this: "addressing information" must comply with\r
- // RFC 5321 (and in turn RFC 1035), anything that is "semantically\r
- // invisible" must comply only with RFC 5322.\r
- switch ($token) {\r
- // Comment\r
- case ISEMAIL_STRING_OPENPARENTHESIS:\r
- if ($element_len === 0)\r
- // Comments at the start of the domain are deprecated in the text\r
- // Comments at the start of a subdomain are obs-domain\r
- // (http://tools.ietf.org/html/rfc5322#section-3.4.1)\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_COMMENT;\r
- else {\r
- $return_status[] = ISEMAIL_CFWS_COMMENT;\r
- $end_or_die = true; // We can't start a comment in the middle of an element, so this better be the end\r
- }\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_COMMENT;\r
- break;\r
- // Next dot-atom element\r
- case ISEMAIL_STRING_DOT:\r
- if ($element_len === 0)\r
- // Another dot, already?\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS; // Fatal error\r
- elseif ($hyphen_flag)\r
- // Previous subdomain ended in a hyphen\r
- $return_status[] = ISEMAIL_ERR_DOMAINHYPHENEND; // Fatal error\r
- else\r
- // Nowhere in RFC 5321 does it say explicitly that the\r
- // domain part of a Mailbox must be a valid domain according\r
- // to the DNS standards set out in RFC 1035, but this *is*\r
- // implied in several places. For instance, wherever the idea\r
- // of host routing is discussed the RFC says that the domain\r
- // must be looked up in the DNS. This would be nonsense unless\r
- // the domain was designed to be a valid DNS domain. Hence we\r
- // must conclude that the RFC 1035 restriction on label length\r
- // also applies to RFC 5321 domains.\r
- //\r
- // http://tools.ietf.org/html/rfc1035#section-2.3.4\r
- // labels 63 octets or less\r
- if ($element_len > 63) $return_status[] = ISEMAIL_RFC5322_LABEL_TOOLONG;\r
-\r
- $end_or_die = false; // CFWS is OK again now we're at the beginning of an element (although it may be obsolete CFWS)\r
- $element_len = 0;\r
- $element_count++;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] = '';\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
-\r
- break;\r
- // Domain literal\r
- case ISEMAIL_STRING_OPENSQBRACKET:\r
- if ($parsedata[ISEMAIL_COMPONENT_DOMAIN] === '') {\r
- $end_or_die = true; // Domain literal must be the only component\r
- $element_len++;\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_COMPONENT_LITERAL;\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;\r
- $parsedata[ISEMAIL_COMPONENT_LITERAL] = '';\r
- } else {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error\r
- }\r
-\r
- break;\r
- // Folding White Space\r
- case ISEMAIL_STRING_CR:\r
- case ISEMAIL_STRING_SP:\r
- case ISEMAIL_STRING_HTAB:\r
- if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {$return_status[] = ISEMAIL_ERR_CR_NO_LF; break;} // Fatal error\r
-\r
- if ($element_len === 0)\r
- $return_status[] = ($element_count === 0) ? ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_FWS;\r
- else {\r
- $return_status[] = ISEMAIL_CFWS_FWS;\r
- $end_or_die = true; // We can't start FWS in the middle of an element, so this better be the end\r
- }\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_FWS;\r
- $token_prior = $token;\r
- break;\r
- // atext\r
- default:\r
- // RFC 5322 allows any atext...\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.3\r
- // atext = ALPHA / DIGIT / ; Printable US-ASCII\r
- // "!" / "#" / ; characters not including\r
- // "$" / "%" / ; specials. Used for atoms.\r
- // "&" / "'" /\r
- // "*" / "+" /\r
- // "-" / "/" /\r
- // "=" / "?" /\r
- // "^" / "_" /\r
- // "`" / "{" /\r
- // "|" / "}" /\r
- // "~"\r
-\r
- // But RFC 5321 only allows letter-digit-hyphen to comply with DNS rules (RFCs 1034 & 1123)\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.2\r
- // sub-domain = Let-dig [Ldh-str]\r
- //\r
- // Let-dig = ALPHA / DIGIT\r
- //\r
- // Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig\r
- //\r
- if ($end_or_die) {\r
- // We have encountered atext where it is no longer valid\r
- switch ($context_prior) {\r
- case ISEMAIL_CONTEXT_COMMENT:\r
- case ISEMAIL_CONTEXT_FWS:\r
- $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_CFWS;\r
- break;\r
- case ISEMAIL_COMPONENT_LITERAL:\r
- $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_DOMLIT;\r
- break;\r
- default:\r
- die ("More atext found where none is allowed, but unrecognised prior context: $context_prior");\r
- }\r
- }\r
-\r
- $ord = ord($token);\r
- $hyphen_flag = false; // Assume this token isn't a hyphen unless we discover it is\r
-\r
- if (($ord < 33) || ($ord > 126) || (!is_bool(strpos(ISEMAIL_STRING_SPECIALS, $token)))) {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error\r
- } elseif ($token === ISEMAIL_STRING_HYPHEN) {\r
- if ($element_len === 0) {\r
- // Hyphens can't be at the beginning of a subdomain\r
- $return_status[] = ISEMAIL_ERR_DOMAINHYPHENSTART; // Fatal error\r
- }\r
-\r
- $hyphen_flag = true;\r
- } elseif (!(($ord > 47 && $ord < 58) || ($ord > 64 && $ord < 91) || ($ord > 96 && $ord < 123))) {\r
- // Not an RFC 5321 subdomain, but still OK by RFC 5322\r
- $return_status[] = ISEMAIL_RFC5322_DOMAIN;\r
- }\r
-\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;\r
- $element_len++;\r
- }\r
-\r
- break;\r
- //-------------------------------------------------------------\r
- // Domain literal\r
- //-------------------------------------------------------------\r
- case ISEMAIL_COMPONENT_LITERAL:\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]\r
- //\r
- // dtext = %d33-90 / ; Printable US-ASCII\r
- // %d94-126 / ; characters not including\r
- // obs-dtext ; "[", "]", or "\"\r
- //\r
- // obs-dtext = obs-NO-WS-CTL / quoted-pair\r
- switch ($token) {\r
- // End of domain literal\r
- case ISEMAIL_STRING_CLOSESQBRACKET:\r
- if ((int) max($return_status) < ISEMAIL_DEPREC) {\r
- // Could be a valid RFC 5321 address literal, so let's check\r
-\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.2\r
- // address-literal = "[" ( IPv4-address-literal /\r
- // IPv6-address-literal /\r
- // General-address-literal ) "]"\r
- // ; See Section 4.1.3\r
- //\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.3\r
- // IPv4-address-literal = Snum 3("." Snum)\r
- //\r
- // IPv6-address-literal = "IPv6:" IPv6-addr\r
- //\r
- // General-address-literal = Standardized-tag ":" 1*dcontent\r
- //\r
- // Standardized-tag = Ldh-str\r
- // ; Standardized-tag MUST be specified in a\r
- // ; Standards-Track RFC and registered with IANA\r
- //\r
- // dcontent = %d33-90 / ; Printable US-ASCII\r
- // %d94-126 ; excl. "[", "\", "]"\r
- //\r
- // Snum = 1*3DIGIT\r
- // ; representing a decimal integer\r
- // ; value in the range 0 through 255\r
- //\r
- // IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp\r
- //\r
- // IPv6-hex = 1*4HEXDIG\r
- //\r
- // IPv6-full = IPv6-hex 7(":" IPv6-hex)\r
- //\r
- // IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::"\r
- // [IPv6-hex *5(":" IPv6-hex)]\r
- // ; The "::" represents at least 2 16-bit groups of\r
- // ; zeros. No more than 6 groups in addition to the\r
- // ; "::" may be present.\r
- //\r
- // IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal\r
- //\r
- // IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"\r
- // [IPv6-hex *3(":" IPv6-hex) ":"]\r
- // IPv4-address-literal\r
- // ; The "::" represents at least 2 16-bit groups of\r
- // ; zeros. No more than 4 groups in addition to the\r
- // ; "::" and IPv4-address-literal may be present.\r
- //\r
- // is_email() author's note: We can't use ip2long() to validate\r
- // IPv4 addresses because it accepts abbreviated addresses\r
- // (xxx.xxx.xxx), expanding the last group to complete the address.\r
- // filter_var() validates IPv6 address inconsistently (up to PHP 5.3.3\r
- // at least) -- see http://bugs.php.net/bug.php?id=53236 for example\r
- $max_groups = 8;\r
- $matchesIP = array();\r
- /*.mixed.*/ $index = false;\r
- $addressliteral = $parsedata[ISEMAIL_COMPONENT_LITERAL];\r
-\r
- // Extract IPv4 part from the end of the address-literal (if there is one)\r
- 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) {\r
- $index = strrpos($addressliteral, $matchesIP[0]);\r
- if ($index !== 0) $addressliteral = substr($addressliteral, 0, $index) . '0:0'; // Convert IPv4 part to IPv6 format for further testing\r
- }\r
-\r
- if ($index === 0) {\r
- // Nothing there except a valid IPv4 address, so...\r
- $return_status[] = ISEMAIL_RFC5321_ADDRESSLITERAL;\r
- } elseif (strncasecmp($addressliteral, ISEMAIL_STRING_IPV6TAG, 5) !== 0) {\r
- $return_status[] = ISEMAIL_RFC5322_DOMAINLITERAL;\r
- } else {\r
- $IPv6 = substr($addressliteral, 5);\r
- $matchesIP = explode(ISEMAIL_STRING_COLON, $IPv6); // Revision 2.7: Daniel Marschall's new IPv6 testing strategy\r
- $groupCount = count($matchesIP);\r
- $index = strpos($IPv6,ISEMAIL_STRING_DOUBLECOLON);\r
-\r
- if ($index === false) {\r
- // We need exactly the right number of groups\r
- if ($groupCount !== $max_groups)\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_GRPCOUNT;\r
- } else {\r
- if ($index !== strrpos($IPv6,ISEMAIL_STRING_DOUBLECOLON))\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_2X2XCOLON;\r
- else {\r
- if ($index === 0 || $index === (strlen($IPv6) - 2)) $max_groups++; // RFC 4291 allows :: at the start or end of an address with 7 other groups in addition\r
-\r
- if ($groupCount > $max_groups)\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_MAXGRPS;\r
- elseif ($groupCount === $max_groups)\r
- $return_status[] = ISEMAIL_RFC5321_IPV6DEPRECATED; // Eliding a single "::"\r
- }\r
- }\r
-\r
- // Revision 2.7: Daniel Marschall's new IPv6 testing strategy\r
- if ((substr($IPv6, 0, 1) === ISEMAIL_STRING_COLON) && (substr($IPv6, 1, 1) !== ISEMAIL_STRING_COLON))\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_COLONSTRT; // Address starts with a single colon\r
- elseif ((substr($IPv6, -1) === ISEMAIL_STRING_COLON) && (substr($IPv6, -2, 1) !== ISEMAIL_STRING_COLON))\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_COLONEND; // Address ends with a single colon\r
- elseif (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0)\r
- $return_status[] = ISEMAIL_RFC5322_IPV6_BADCHAR; // Check for unmatched characters\r
- else\r
- $return_status[] = ISEMAIL_RFC5321_ADDRESSLITERAL;\r
- }\r
- } else\r
- $return_status[] = ISEMAIL_RFC5322_DOMAINLITERAL;\r
-\r
-\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;\r
- $element_len++;\r
- $context_prior = $context;\r
- $context = (int) array_pop($context_stack);\r
- break;\r
- case ISEMAIL_STRING_BACKSLASH:\r
- $return_status[] = ISEMAIL_RFC5322_DOMLIT_OBSDTEXT;\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_QUOTEDPAIR;\r
- break;\r
- // Folding White Space\r
- case ISEMAIL_STRING_CR:\r
- case ISEMAIL_STRING_SP:\r
- case ISEMAIL_STRING_HTAB:\r
- if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {$return_status[] = ISEMAIL_ERR_CR_NO_LF; break;} // Fatal error\r
-\r
- $return_status[] = ISEMAIL_CFWS_FWS;\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_FWS;\r
- $token_prior = $token;\r
- break;\r
- // dtext\r
- default:\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // dtext = %d33-90 / ; Printable US-ASCII\r
- // %d94-126 / ; characters not including\r
- // obs-dtext ; "[", "]", or "\"\r
- //\r
- // obs-dtext = obs-NO-WS-CTL / quoted-pair\r
- //\r
- // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control\r
- // %d11 / ; characters that do not\r
- // %d12 / ; include the carriage\r
- // %d14-31 / ; return, line feed, and\r
- // %d127 ; white space characters\r
- $ord = ord($token);\r
-\r
- // CR, LF, SP & HTAB have already been parsed above\r
- if (($ord > 127) || ($ord === 0) || ($token === ISEMAIL_STRING_OPENSQBRACKET)) {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_DTEXT; // Fatal error\r
- break;\r
- } elseif (($ord < 33) || ($ord === 127)) {\r
- $return_status[] = ISEMAIL_RFC5322_DOMLIT_OBSDTEXT;\r
- }\r
-\r
- $parsedata[ISEMAIL_COMPONENT_LITERAL] .= $token;\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;\r
- $element_len++;\r
- }\r
-\r
- break;\r
- //-------------------------------------------------------------\r
- // Quoted string\r
- //-------------------------------------------------------------\r
- case ISEMAIL_CONTEXT_QUOTEDSTRING:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.4\r
- // quoted-string = [CFWS]\r
- // DQUOTE *([FWS] qcontent) [FWS] DQUOTE\r
- // [CFWS]\r
- //\r
- // qcontent = qtext / quoted-pair\r
- switch ($token) {\r
- // Quoted pair\r
- case ISEMAIL_STRING_BACKSLASH:\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_QUOTEDPAIR;\r
- break;\r
- // Folding White Space\r
- // Inside a quoted string, spaces are allowed as regular characters.\r
- // It's only FWS if we include HTAB or CRLF\r
- case ISEMAIL_STRING_CR:\r
- case ISEMAIL_STRING_HTAB:\r
- if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {$return_status[] = ISEMAIL_ERR_CR_NO_LF; break;} // Fatal error\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.2\r
- // Runs of FWS, comment, or CFWS that occur between lexical tokens in a\r
- // structured header field are semantically interpreted as a single\r
- // space character.\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.4\r
- // the CRLF in any FWS/CFWS that appears within the quoted-string [is]\r
- // semantically "invisible" and therefore not part of the quoted-string\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= ISEMAIL_STRING_SP;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= ISEMAIL_STRING_SP;\r
- $element_len++;\r
-\r
- $return_status[] = ISEMAIL_CFWS_FWS;\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_FWS;\r
- $token_prior = $token;\r
- break;\r
- // End of quoted string\r
- case ISEMAIL_STRING_DQUOTE:\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;\r
- $element_len++;\r
- $context_prior = $context;\r
- $context = (int) array_pop($context_stack);\r
- break;\r
- // qtext\r
- default:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.4\r
- // qtext = %d33 / ; Printable US-ASCII\r
- // %d35-91 / ; characters not including\r
- // %d93-126 / ; "\" or the quote character\r
- // obs-qtext\r
- //\r
- // obs-qtext = obs-NO-WS-CTL\r
- //\r
- // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control\r
- // %d11 / ; characters that do not\r
- // %d12 / ; include the carriage\r
- // %d14-31 / ; return, line feed, and\r
- // %d127 ; white space characters\r
- $ord = ord($token);\r
-\r
- if (($ord > 127) || ($ord === 0) || ($ord === 10)) {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_QTEXT; // Fatal error\r
- } elseif (($ord < 32) || ($ord === 127))\r
- $return_status[] = ISEMAIL_DEPREC_QTEXT;\r
-\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;\r
- $element_len++;\r
- }\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.4.1\r
- // If the\r
- // string can be represented as a dot-atom (that is, it contains no\r
- // characters other than atext characters or "." surrounded by atext\r
- // characters), then the dot-atom form SHOULD be used and the quoted-\r
- // string form SHOULD NOT be used.\r
-// To do\r
- break;\r
- //-------------------------------------------------------------\r
- // Quoted pair\r
- //-------------------------------------------------------------\r
- case ISEMAIL_CONTEXT_QUOTEDPAIR:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.1\r
- // quoted-pair = ("\" (VCHAR / WSP)) / obs-qp\r
- //\r
- // VCHAR = %d33-126 ; visible (printing) characters\r
- // WSP = SP / HTAB ; white space\r
- //\r
- // obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)\r
- //\r
- // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control\r
- // %d11 / ; characters that do not\r
- // %d12 / ; include the carriage\r
- // %d14-31 / ; return, line feed, and\r
- // %d127 ; white space characters\r
- //\r
- // i.e. obs-qp = "\" (%d0-8, %d10-31 / %d127)\r
- $ord = ord($token);\r
-\r
- if ($ord > 127)\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_QPAIR; // Fatal error\r
- elseif ((($ord < 31) && ($ord !== 9)) || ($ord === 127)) // SP & HTAB are allowed\r
- $return_status[] = ISEMAIL_DEPREC_QP;\r
-\r
- // At this point we know where this qpair occurred so\r
- // we could check to see if the character actually\r
- // needed to be quoted at all.\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.2\r
- // the sending system SHOULD transmit the\r
- // form that uses the minimum quoting possible.\r
-// To do: check whether the character needs to be quoted (escaped) in this context\r
- $context_prior = $context;\r
- $context = (int) array_pop($context_stack); // End of qpair\r
- $token = ISEMAIL_STRING_BACKSLASH . $token;\r
-\r
- switch ($context) {\r
- case ISEMAIL_CONTEXT_COMMENT:\r
- break;\r
- case ISEMAIL_CONTEXT_QUOTEDSTRING:\r
- $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;\r
- $element_len += 2; // The maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash\r
- break;\r
- case ISEMAIL_COMPONENT_LITERAL:\r
- $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;\r
- $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;\r
- $element_len += 2; // The maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash\r
- break;\r
- default:\r
- die("Quoted pair logic invoked in an invalid context: $context");\r
- }\r
-\r
- break;\r
- //-------------------------------------------------------------\r
- // Comment\r
- //-------------------------------------------------------------\r
- case ISEMAIL_CONTEXT_COMMENT:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.2\r
- // comment = "(" *([FWS] ccontent) [FWS] ")"\r
- //\r
- // ccontent = ctext / quoted-pair / comment\r
- switch ($token) {\r
- // Nested comment\r
- case ISEMAIL_STRING_OPENPARENTHESIS:\r
- // Nested comments are OK\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_COMMENT;\r
- break;\r
- // End of comment\r
- case ISEMAIL_STRING_CLOSEPARENTHESIS:\r
- $context_prior = $context;\r
- $context = (int) array_pop($context_stack);\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.2\r
- // Runs of FWS, comment, or CFWS that occur between lexical tokens in a\r
- // structured header field are semantically interpreted as a single\r
- // space character.\r
- //\r
- // is_email() author's note: This *cannot* mean that we must add a\r
- // space to the address wherever CFWS appears. This would result in\r
- // any addr-spec that had CFWS outside a quoted string being invalid\r
- // for RFC 5321.\r
-// if (($context === ISEMAIL_COMPONENT_LOCALPART) || ($context === ISEMAIL_COMPONENT_DOMAIN)) {\r
-// $parsedata[$context] .= ISEMAIL_STRING_SP;\r
-// $atomlist[$context][$element_count] .= ISEMAIL_STRING_SP;\r
-// $element_len++;\r
-// }\r
-\r
- break;\r
- // Quoted pair\r
- case ISEMAIL_STRING_BACKSLASH:\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_QUOTEDPAIR;\r
- break;\r
- // Folding White Space\r
- case ISEMAIL_STRING_CR:\r
- case ISEMAIL_STRING_SP:\r
- case ISEMAIL_STRING_HTAB:\r
- if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {$return_status[] = ISEMAIL_ERR_CR_NO_LF; break;} // Fatal error\r
-\r
- $return_status[] = ISEMAIL_CFWS_FWS;\r
-\r
- $context_stack[] = $context;\r
- $context = ISEMAIL_CONTEXT_FWS;\r
- $token_prior = $token;\r
- break;\r
- // ctext\r
- default:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.3\r
- // ctext = %d33-39 / ; Printable US-ASCII\r
- // %d42-91 / ; characters not including\r
- // %d93-126 / ; "(", ")", or "\"\r
- // obs-ctext\r
- //\r
- // obs-ctext = obs-NO-WS-CTL\r
- //\r
- // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control\r
- // %d11 / ; characters that do not\r
- // %d12 / ; include the carriage\r
- // %d14-31 / ; return, line feed, and\r
- // %d127 ; white space characters\r
- $ord = ord($token);\r
-\r
- if (($ord > 127) || ($ord === 0) || ($ord === 10)) {\r
- $return_status[] = ISEMAIL_ERR_EXPECTING_CTEXT; // Fatal error\r
- break;\r
- } elseif (($ord < 32) || ($ord === 127)) {\r
- $return_status[] = ISEMAIL_DEPREC_CTEXT;\r
- }\r
- }\r
-\r
- break;\r
- //-------------------------------------------------------------\r
- // Folding White Space\r
- //-------------------------------------------------------------\r
- case ISEMAIL_CONTEXT_FWS:\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.2\r
- // FWS = ([*WSP CRLF] 1*WSP) / obs-FWS\r
- // ; Folding white space\r
-\r
- // But note the erratum:\r
- // http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908:\r
- // In the obsolete syntax, any amount of folding white space MAY be\r
- // inserted where the obs-FWS rule is allowed. This creates the\r
- // possibility of having two consecutive "folds" in a line, and\r
- // therefore the possibility that a line which makes up a folded header\r
- // field could be composed entirely of white space.\r
- //\r
- // obs-FWS = 1*([CRLF] WSP)\r
- if ($token_prior === ISEMAIL_STRING_CR) {\r
- if ($token === ISEMAIL_STRING_CR) {\r
- $return_status[] = ISEMAIL_ERR_FWS_CRLF_X2; // Fatal error\r
- break;\r
- }\r
-\r
- if (isset($crlf_count)) {\r
- if (++$crlf_count > 1)\r
- $return_status[] = ISEMAIL_DEPREC_FWS; // Multiple folds = obsolete FWS\r
- } else $crlf_count = 1;\r
- }\r
-\r
- switch ($token) {\r
- case ISEMAIL_STRING_CR:\r
- if ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))\r
- $return_status[] = ISEMAIL_ERR_CR_NO_LF; // Fatal error\r
-\r
- break;\r
- case ISEMAIL_STRING_SP:\r
- case ISEMAIL_STRING_HTAB:\r
- break;\r
- default:\r
- if ($token_prior === ISEMAIL_STRING_CR) {\r
- $return_status[] = ISEMAIL_ERR_FWS_CRLF_END; // Fatal error\r
- break;\r
- }\r
-\r
- if (isset($crlf_count)) unset($crlf_count);\r
-\r
- $context_prior = $context;\r
- $context = (int) array_pop($context_stack); // End of FWS\r
-\r
- // http://tools.ietf.org/html/rfc5322#section-3.2.2\r
- // Runs of FWS, comment, or CFWS that occur between lexical tokens in a\r
- // structured header field are semantically interpreted as a single\r
- // space character.\r
- //\r
- // is_email() author's note: This *cannot* mean that we must add a\r
- // space to the address wherever CFWS appears. This would result in\r
- // any addr-spec that had CFWS outside a quoted string being invalid\r
- // for RFC 5321.\r
-// if (($context === ISEMAIL_COMPONENT_LOCALPART) || ($context === ISEMAIL_COMPONENT_DOMAIN)) {\r
-// $parsedata[$context] .= ISEMAIL_STRING_SP;\r
-// $atomlist[$context][$element_count] .= ISEMAIL_STRING_SP;\r
-// $element_len++;\r
-// }\r
-\r
- $i--; // Look at this token again in the parent context\r
- }\r
-\r
- $token_prior = $token;\r
- break;\r
- //-------------------------------------------------------------\r
- // A context we aren't expecting\r
- //-------------------------------------------------------------\r
- default:\r
- die("Unknown context: $context");\r
- }\r
-\r
-//-echo "<td>$context|",(($end_or_die) ? 'true' : 'false'),"|$token|" . max($return_status) . "</td></tr>"; // debug\r
- if ((int) max($return_status) > ISEMAIL_RFC5322) break; // No point going on if we've got a fatal error\r
- }\r
-\r
- // Some simple final tests\r
- if ((int) max($return_status) < ISEMAIL_RFC5322) {\r
- if ($context === ISEMAIL_CONTEXT_QUOTEDSTRING) $return_status[] = ISEMAIL_ERR_UNCLOSEDQUOTEDSTR; // Fatal error\r
- elseif ($context === ISEMAIL_CONTEXT_QUOTEDPAIR) $return_status[] = ISEMAIL_ERR_BACKSLASHEND; // Fatal error\r
- elseif ($context === ISEMAIL_CONTEXT_COMMENT) $return_status[] = ISEMAIL_ERR_UNCLOSEDCOMMENT; // Fatal error\r
- elseif ($context === ISEMAIL_COMPONENT_LITERAL) $return_status[] = ISEMAIL_ERR_UNCLOSEDDOMLIT; // Fatal error\r
- elseif ($token === ISEMAIL_STRING_CR) $return_status[] = ISEMAIL_ERR_FWS_CRLF_END; // Fatal error\r
- elseif ($parsedata[ISEMAIL_COMPONENT_DOMAIN] === '') $return_status[] = ISEMAIL_ERR_NODOMAIN; // Fatal error\r
- elseif ($element_len === 0) $return_status[] = ISEMAIL_ERR_DOT_END; // Fatal error\r
- elseif ($hyphen_flag) $return_status[] = ISEMAIL_ERR_DOMAINHYPHENEND; // Fatal error\r
- // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.2\r
- // The maximum total length of a domain name or number is 255 octets.\r
- elseif (strlen($parsedata[ISEMAIL_COMPONENT_DOMAIN]) > 255)\r
- $return_status[] = ISEMAIL_RFC5322_DOMAIN_TOOLONG;\r
- // http://tools.ietf.org/html/rfc5321#section-4.1.2\r
- // Forward-path = Path\r
- //\r
- // Path = "<" [ A-d-l ":" ] Mailbox ">"\r
- //\r
- // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3\r
- // The maximum total length of a reverse-path or forward-path is 256\r
- // octets (including the punctuation and element separators).\r
- //\r
- // Thus, even without (obsolete) routing information, the Mailbox can\r
- // only be 254 characters long. This is confirmed by this verified\r
- // erratum to RFC 3696:\r
- //\r
- // http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690\r
- // However, there is a restriction in RFC 2821 on the length of an\r
- // address in MAIL and RCPT commands of 254 characters. Since addresses\r
- // that do not fit in those fields are not normally useful, the upper\r
- // limit on address lengths should normally be considered to be 254.\r
- elseif (strlen($parsedata[ISEMAIL_COMPONENT_LOCALPART] . ISEMAIL_STRING_AT . $parsedata[ISEMAIL_COMPONENT_DOMAIN]) > 254)\r
- $return_status[] = ISEMAIL_RFC5322_TOOLONG;\r
- // http://tools.ietf.org/html/rfc1035#section-2.3.4\r
- // labels 63 octets or less\r
- elseif ($element_len > 63) $return_status[] = ISEMAIL_RFC5322_LABEL_TOOLONG;\r
- }\r
-\r
- // Check DNS?\r
- $dns_checked = false;\r
-\r
- if ($checkDNS && ((int) max($return_status) < ISEMAIL_DNSWARN) && function_exists('dns_get_record')) {\r
- // http://tools.ietf.org/html/rfc5321#section-2.3.5\r
- // Names that can\r
- // be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed\r
- // in Section 5) are permitted, as are CNAME RRs whose targets can be\r
- // resolved, in turn, to MX or address RRs.\r
- //\r
- // http://tools.ietf.org/html/rfc5321#section-5.1\r
- // The lookup first attempts to locate an MX record associated with the\r
- // name. If a CNAME record is found, the resulting name is processed as\r
- // if it were the initial name. ... If an empty list of MXs is returned,\r
- // the address is treated as if it was associated with an implicit MX\r
- // RR, with a preference of 0, pointing to that host.\r
- //\r
- // is_email() author's note: We will regard the existence of a CNAME to be\r
- // sufficient evidence of the domain's existence. For performance reasons\r
- // we will not repeat the DNS lookup for the CNAME's target, but we will\r
- // raise a warning because we didn't immediately find an MX record.\r
- if ($element_count === 0) $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= '.'; // Checking TLD DNS seems to work only if you explicitly check from the root\r
-\r
- $result = @dns_get_record($parsedata[ISEMAIL_COMPONENT_DOMAIN], DNS_MX); // Not using checkdnsrr because of a suspected bug in PHP 5.3 (http://bugs.php.net/bug.php?id=51844)\r
-\r
- if ((is_bool($result) && !(bool) $result))\r
- $return_status[] = ISEMAIL_DNSWARN_NO_RECORD; // Domain can't be found in DNS\r
- else {\r
- if (count($result) === 0) {\r
- $return_status[] = ISEMAIL_DNSWARN_NO_MX_RECORD; // MX-record for domain can't be found\r
- $result = @dns_get_record($parsedata[ISEMAIL_COMPONENT_DOMAIN], DNS_A + DNS_CNAME);\r
-\r
- if (count($result) === 0)\r
- $return_status[] = ISEMAIL_DNSWARN_NO_RECORD; // No usable records for the domain can be found\r
- } else $dns_checked = true;\r
- }\r
- }\r
-\r
- // Check for TLD addresses\r
- // -----------------------\r
- // TLD addresses are specifically allowed in RFC 5321 but they are\r
- // unusual to say the least. We will allocate a separate\r
- // status to these addresses on the basis that they are more likely\r
- // to be typos than genuine addresses (unless we've already\r
- // established that the domain does have an MX record)\r
- //\r
- // http://tools.ietf.org/html/rfc5321#section-2.3.5\r
- // In the case\r
- // of a top-level domain used by itself in an email address, a single\r
- // string is used without any dots. This makes the requirement,\r
- // described in more detail below, that only fully-qualified domain\r
- // names appear in SMTP transactions on the public Internet,\r
- // particularly important where top-level domains are involved.\r
- //\r
- // TLD format\r
- // ----------\r
- // The format of TLDs has changed a number of times. The standards\r
- // used by IANA have been largely ignored by ICANN, leading to\r
- // confusion over the standards being followed. These are not defined\r
- // anywhere, except as a general component of a DNS host name (a label).\r
- // However, this could potentially lead to 123.123.123.123 being a\r
- // valid DNS name (rather than an IP address) and thereby creating\r
- // an ambiguity. The most authoritative statement on TLD formats that\r
- // the author can find is in a (rejected!) erratum to RFC 1123\r
- // submitted by John Klensin, the author of RFC 5321:\r
- //\r
- // http://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353\r
- // However, a valid host name can never have the dotted-decimal\r
- // form #.#.#.#, since this change does not permit the highest-level\r
- // component label to start with a digit even if it is not all-numeric.\r
- if (!$dns_checked && ((int) max($return_status) < ISEMAIL_DNSWARN)) {\r
- if ($element_count === 0) $return_status[] = ISEMAIL_RFC5321_TLD;\r
-\r
- if (is_numeric($atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count][0]))\r
- $return_status[] = ISEMAIL_RFC5321_TLDNUMERIC;\r
- }\r
-\r
- $return_status = array_unique($return_status);\r
- $final_status = (int) max($return_status);\r
-\r
- if (count($return_status) !== 1) array_shift($return_status); // remove redundant ISEMAIL_VALID\r
-\r
- $parsedata['status'] = $return_status;\r
-\r
- if ($final_status < $threshold) $final_status = ISEMAIL_VALID;\r
-\r
- return ($diagnose) ? $final_status : ($final_status < ISEMAIL_THRESHOLD);\r
-}\r
-?>\r
+<?php
+/**
+ * To validate an email address according to RFCs 5321, 5322 and others
+ *
+ * Copyright © 2008-2011, Dominic Sayers
+ * Test schema documentation Copyright © 2011, Daniel Marschall
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Dominic Sayers nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @package is_email
+ * @author Dominic Sayers <dominic@sayers.cc>
+ * @copyright 2008-2011 Dominic Sayers
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link http://www.dominicsayers.com/isemail
+ * @version 3.04.1 - Changed my link to http://isemail.info throughout
+ */
+
+// The quality of this code has been improved greatly by using PHPLint
+// Copyright (c) 2010 Umberto Salsi
+// This is free software; see the license for copying conditions.
+// More info: http://www.icosaedro.it/phplint/
+/*.
+ require_module 'standard';
+ require_module 'pcre';
+.*/
+
+if (!defined('ISEMAIL_VALID')) {
+/*:diagnostic constants start:*/
+// This part of the code is generated using data from test/meta.xml. Beware of making manual alterations
+ // Categories
+ define('ISEMAIL_VALID_CATEGORY', 1);
+ define('ISEMAIL_DNSWARN', 7);
+ define('ISEMAIL_RFC5321', 15);
+ define('ISEMAIL_CFWS', 31);
+ define('ISEMAIL_DEPREC', 63);
+ define('ISEMAIL_RFC5322', 127);
+ define('ISEMAIL_ERR', 255);
+
+ // Diagnoses
+ // Address is valid
+ define('ISEMAIL_VALID', 0);
+ // Address is valid but a DNS check was not successful
+ define('ISEMAIL_DNSWARN_NO_MX_RECORD', 5);
+ define('ISEMAIL_DNSWARN_NO_RECORD', 6);
+ // Address is valid for SMTP but has unusual elements
+ define('ISEMAIL_RFC5321_TLD', 9);
+ define('ISEMAIL_RFC5321_TLDNUMERIC', 10);
+ define('ISEMAIL_RFC5321_QUOTEDSTRING', 11);
+ define('ISEMAIL_RFC5321_ADDRESSLITERAL', 12);
+ define('ISEMAIL_RFC5321_IPV6DEPRECATED', 13);
+ // Address is valid within the message but cannot be used unmodified for the envelope
+ define('ISEMAIL_CFWS_COMMENT', 17);
+ define('ISEMAIL_CFWS_FWS', 18);
+ // Address contains deprecated elements but may still be valid in restricted contexts
+ define('ISEMAIL_DEPREC_LOCALPART', 33);
+ define('ISEMAIL_DEPREC_FWS', 34);
+ define('ISEMAIL_DEPREC_QTEXT', 35);
+ define('ISEMAIL_DEPREC_QP', 36);
+ define('ISEMAIL_DEPREC_COMMENT', 37);
+ define('ISEMAIL_DEPREC_CTEXT', 38);
+ define('ISEMAIL_DEPREC_CFWS_NEAR_AT', 49);
+ // The address is only valid according to the broad definition of RFC 5322. It is otherwise invalid.
+ define('ISEMAIL_RFC5322_DOMAIN', 65);
+ define('ISEMAIL_RFC5322_TOOLONG', 66);
+ define('ISEMAIL_RFC5322_LOCAL_TOOLONG', 67);
+ define('ISEMAIL_RFC5322_DOMAIN_TOOLONG', 68);
+ define('ISEMAIL_RFC5322_LABEL_TOOLONG', 69);
+ define('ISEMAIL_RFC5322_DOMAINLITERAL', 70);
+ define('ISEMAIL_RFC5322_DOMLIT_OBSDTEXT', 71);
+ define('ISEMAIL_RFC5322_IPV6_GRPCOUNT', 72);
+ define('ISEMAIL_RFC5322_IPV6_2X2XCOLON', 73);
+ define('ISEMAIL_RFC5322_IPV6_BADCHAR', 74);
+ define('ISEMAIL_RFC5322_IPV6_MAXGRPS', 75);
+ define('ISEMAIL_RFC5322_IPV6_COLONSTRT', 76);
+ define('ISEMAIL_RFC5322_IPV6_COLONEND', 77);
+ // Address is invalid for any purpose
+ define('ISEMAIL_ERR_EXPECTING_DTEXT', 129);
+ define('ISEMAIL_ERR_NOLOCALPART', 130);
+ define('ISEMAIL_ERR_NODOMAIN', 131);
+ define('ISEMAIL_ERR_CONSECUTIVEDOTS', 132);
+ define('ISEMAIL_ERR_ATEXT_AFTER_CFWS', 133);
+ define('ISEMAIL_ERR_ATEXT_AFTER_QS', 134);
+ define('ISEMAIL_ERR_ATEXT_AFTER_DOMLIT', 135);
+ define('ISEMAIL_ERR_EXPECTING_QPAIR', 136);
+ define('ISEMAIL_ERR_EXPECTING_ATEXT', 137);
+ define('ISEMAIL_ERR_EXPECTING_QTEXT', 138);
+ define('ISEMAIL_ERR_EXPECTING_CTEXT', 139);
+ define('ISEMAIL_ERR_BACKSLASHEND', 140);
+ define('ISEMAIL_ERR_DOT_START', 141);
+ define('ISEMAIL_ERR_DOT_END', 142);
+ define('ISEMAIL_ERR_DOMAINHYPHENSTART', 143);
+ define('ISEMAIL_ERR_DOMAINHYPHENEND', 144);
+ define('ISEMAIL_ERR_UNCLOSEDQUOTEDSTR', 145);
+ define('ISEMAIL_ERR_UNCLOSEDCOMMENT', 146);
+ define('ISEMAIL_ERR_UNCLOSEDDOMLIT', 147);
+ define('ISEMAIL_ERR_FWS_CRLF_X2', 148);
+ define('ISEMAIL_ERR_FWS_CRLF_END', 149);
+ define('ISEMAIL_ERR_CR_NO_LF', 150);
+// End of generated code
+/*:diagnostic constants end:*/
+
+ // function control
+ define('ISEMAIL_THRESHOLD', 16);
+
+ // Email parts
+ define('ISEMAIL_COMPONENT_LOCALPART', 0);
+ define('ISEMAIL_COMPONENT_DOMAIN', 1);
+ define('ISEMAIL_COMPONENT_LITERAL', 2);
+ define('ISEMAIL_CONTEXT_COMMENT', 3);
+ define('ISEMAIL_CONTEXT_FWS', 4);
+ define('ISEMAIL_CONTEXT_QUOTEDSTRING', 5);
+ define('ISEMAIL_CONTEXT_QUOTEDPAIR', 6);
+
+ // Miscellaneous string constants
+ define('ISEMAIL_STRING_AT', '@');
+ define('ISEMAIL_STRING_BACKSLASH', '\\');
+ define('ISEMAIL_STRING_DOT', '.');
+ define('ISEMAIL_STRING_DQUOTE', '"');
+ define('ISEMAIL_STRING_OPENPARENTHESIS', '(');
+ define('ISEMAIL_STRING_CLOSEPARENTHESIS', ')');
+ define('ISEMAIL_STRING_OPENSQBRACKET', '[');
+ define('ISEMAIL_STRING_CLOSESQBRACKET', ']');
+ define('ISEMAIL_STRING_HYPHEN', '-');
+ define('ISEMAIL_STRING_COLON', ':');
+ define('ISEMAIL_STRING_DOUBLECOLON', '::');
+ define('ISEMAIL_STRING_SP', ' ');
+ define('ISEMAIL_STRING_HTAB', "\t");
+ define('ISEMAIL_STRING_CR', "\r");
+ define('ISEMAIL_STRING_LF', "\n");
+ define('ISEMAIL_STRING_IPV6TAG', 'IPv6:');
+ // US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3)
+ define('ISEMAIL_STRING_SPECIALS', '()<>[]:;@\\,."');
+}
+
+/**
+ * Check that an email address conforms to RFCs 5321, 5322 and others
+ *
+ * As of Version 3.0, we are now distinguishing clearly between a Mailbox
+ * as defined by RFC 5321 and an addr-spec as defined by RFC 5322. Depending
+ * on the context, either can be regarded as a valid email address. The
+ * RFC 5321 Mailbox specification is more restrictive (comments, white space
+ * and obsolete forms are not allowed)
+ *
+ * @param string $email The email address to check
+ * @param boolean $checkDNS If true then a DNS check for MX records will be made
+ * @param mixed $errorlevel Determines the boundary between valid and invalid addresses.
+ * Status codes above this number will be returned as-is,
+ * status codes below will be returned as ISEMAIL_VALID. Thus the
+ * calling program can simply look for ISEMAIL_VALID if it is
+ * only interested in whether an address is valid or not. The
+ * errorlevel will determine how "picky" is_email() is about
+ * the address.
+ *
+ * If omitted or passed as false then is_email() will return
+ * true or false rather than an integer error or warning.
+ *
+ * NB Note the difference between $errorlevel = false and
+ * $errorlevel = 0
+ * @param array $parsedata If passed, returns the parsed address components
+ */
+/*.mixed.*/ function is_email($email, $checkDNS = false, $errorlevel = false, &$parsedata = array()) {
+ // Check that $email is a valid address. Read the following RFCs to understand the constraints:
+ // (http://tools.ietf.org/html/rfc5321)
+ // (http://tools.ietf.org/html/rfc5322)
+ // (http://tools.ietf.org/html/rfc4291#section-2.2)
+ // (http://tools.ietf.org/html/rfc1123#section-2.1)
+ // (http://tools.ietf.org/html/rfc3696) (guidance only)
+ // version 2.0: Enhance $diagnose parameter to $errorlevel
+ // version 3.0: Introduced status categories
+ // revision 3.1: BUG: $parsedata was passed by value instead of by reference
+
+ if (is_bool($errorlevel)) {
+ $threshold = ISEMAIL_VALID;
+ $diagnose = (bool) $errorlevel;
+ } else {
+ $diagnose = true;
+
+ switch ((int) $errorlevel) {
+ case E_WARNING:
+ $threshold = ISEMAIL_THRESHOLD;
+ break; // For backward compatibility
+ case E_ERROR:
+ $threshold = ISEMAIL_VALID;
+ break; // For backward compatibility
+ default:
+ $threshold = (int) $errorlevel;
+ }
+ }
+
+ $return_status = array(ISEMAIL_VALID);
+
+ // Parse the address into components, character by character
+ $raw_length = strlen($email);
+ $context = ISEMAIL_COMPONENT_LOCALPART; // Where we are
+ $context_stack = array($context); // Where we have been
+ $context_prior = ISEMAIL_COMPONENT_LOCALPART; // Where we just came from
+ $token = ''; // The current character
+ $token_prior = ''; // The previous character
+ $parsedata = array(
+ ISEMAIL_COMPONENT_LOCALPART => '',
+ ISEMAIL_COMPONENT_DOMAIN => ''
+ ); // For the components of the address
+
+ $atomlist = array(
+ ISEMAIL_COMPONENT_LOCALPART => array(''),
+ ISEMAIL_COMPONENT_DOMAIN => array('')
+ ); // For the dot-atom elements of the address
+ $element_count = 0;
+ $element_len = 0;
+ $hyphen_flag = false; // Hyphen cannot occur at the end of a subdomain
+ $end_or_die = false; // CFWS can only appear at the end of the element
+
+//-echo "<table style=\"clear:left;\">"; // debug
+ for ($i = 0; $i < $raw_length; $i++) {
+ $token = $email[$i];
+//-echo "<tr><td><strong>$context|",(($end_or_die) ? 'true' : 'false'),
+//"|$token|" . max($return_status) . "</strong></td>"; // debug
+
+ switch ($context) {
+ //-------------------------------------------------------------
+ // local-part
+ //-------------------------------------------------------------
+ case ISEMAIL_COMPONENT_LOCALPART:
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // local-part = dot-atom / quoted-string / obs-local-part
+ //
+ // dot-atom = [CFWS] dot-atom-text [CFWS]
+ //
+ // dot-atom-text = 1*atext *("." 1*atext)
+ //
+ // quoted-string = [CFWS]
+ // DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+ // [CFWS]
+ //
+ // obs-local-part = word *("." word)
+ //
+ // word = atom / quoted-string
+ //
+ // atom = [CFWS] 1*atext [CFWS]
+ switch ($token) {
+ // Comment
+ case ISEMAIL_STRING_OPENPARENTHESIS:
+ if ($element_len === 0) {
+ // Comments are OK at the beginning of an element
+ $return_status[] = ($element_count === 0) ?
+ ISEMAIL_CFWS_COMMENT : ISEMAIL_DEPREC_COMMENT;
+ } else {
+ $return_status[] = ISEMAIL_CFWS_COMMENT;
+ $end_or_die = true;
+ // We can't start a comment in the middle of an element, so this better be the end
+ }
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_COMMENT;
+ break;
+ // Next dot-atom element
+ case ISEMAIL_STRING_DOT:
+ if ($element_len === 0) {
+ // Another dot, already?
+ // Fatal error
+ $return_status[] = ($element_count === 0)
+ ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS;
+ } else {
+ // The entire local-part can be a quoted string for RFC 5321
+ // If it's just one atom that is quoted then it's an RFC 5322 obsolete form
+ if ($end_or_die) {
+ $return_status[] = ISEMAIL_DEPREC_LOCALPART;
+ }
+ }
+ // CFWS & quoted strings are OK again now we're at the beginning of an element
+ // (although they are obsolete forms)
+ $end_or_die = false;
+ $element_len = 0;
+ $element_count++;
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] = '';
+ break;
+ // Quoted string
+ case ISEMAIL_STRING_DQUOTE:
+ if ($element_len === 0) {
+ // The entire local-part can be a quoted string for RFC 5321
+ // If it's just one atom that is quoted then it's an RFC 5322 obsolete form
+ $return_status[] = ($element_count === 0)
+ ? ISEMAIL_RFC5321_QUOTEDSTRING : ISEMAIL_DEPREC_LOCALPART;
+
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;
+ $element_len++;
+ $end_or_die = true; // Quoted string must be the entire element
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_QUOTEDSTRING;
+ } else {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error
+ }
+
+ break;
+ // Folding White Space
+ case ISEMAIL_STRING_CR:
+ case ISEMAIL_STRING_SP:
+ case ISEMAIL_STRING_HTAB:
+ if (($token === ISEMAIL_STRING_CR)
+ && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF;
+ break;
+ } // Fatal error
+
+ if ($element_len === 0) {
+ $return_status[] = ($element_count === 0) ? ISEMAIL_CFWS_FWS : ISEMAIL_DEPREC_FWS;
+ } else {
+ // We can't start FWS in the middle of an element, so this better be the end
+ $end_or_die = true;
+ }
+
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_FWS;
+ $token_prior = $token;
+
+ break;
+ // @
+ case ISEMAIL_STRING_AT:
+ // At this point we should have a valid local-part
+ if (count($context_stack) !== 1) {
+ die('Unexpected item on context stack');
+ }
+
+ if ($parsedata[ISEMAIL_COMPONENT_LOCALPART] === '') {
+ $return_status[] = ISEMAIL_ERR_NOLOCALPART; // Fatal error
+ } elseif ($element_len === 0) {
+ $return_status[] = ISEMAIL_ERR_DOT_END; // Fatal error
+ } elseif (strlen($parsedata[ISEMAIL_COMPONENT_LOCALPART]) > 64) {
+ // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
+ // The maximum total length of a user name or other local-part is 64
+ // octets.
+ $return_status[] = ISEMAIL_RFC5322_LOCAL_TOOLONG;
+ } elseif (($context_prior === ISEMAIL_CONTEXT_COMMENT) || ($context_prior === ISEMAIL_CONTEXT_FWS)) {
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // Comments and folding white space
+ // SHOULD NOT be used around the "@" in the addr-spec.
+ //
+ // http://tools.ietf.org/html/rfc2119
+ // 4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
+ // there may exist valid reasons in particular circumstances when the
+ // particular behavior is acceptable or even useful, but the full
+ // implications should be understood and the case carefully weighed
+ // before implementing any behavior described with this label.
+ $return_status[] = ISEMAIL_DEPREC_CFWS_NEAR_AT;
+ }
+ // Clear everything down for the domain parsing
+ $context = ISEMAIL_COMPONENT_DOMAIN; // Where we are
+ $context_stack = array($context); // Where we have been
+ $element_count = 0;
+ $element_len = 0;
+ $end_or_die = false; // CFWS can only appear at the end of the element
+
+ break;
+ // atext
+ default:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.3
+ // atext = ALPHA / DIGIT / ; Printable US-ASCII
+ // "!" / "#" / ; characters not including
+ // "$" / "%" / ; specials. Used for atoms.
+ // "&" / "'" /
+ // "*" / "+" /
+ // "-" / "/" /
+ // "=" / "?" /
+ // "^" / "_" /
+ // "`" / "{" /
+ // "|" / "}" /
+ // "~"
+ if ($end_or_die) {
+ // We have encountered atext where it is no longer valid
+ switch ($context_prior) {
+ case ISEMAIL_CONTEXT_COMMENT:
+ case ISEMAIL_CONTEXT_FWS:
+ $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_CFWS;
+ break;
+ case ISEMAIL_CONTEXT_QUOTEDSTRING:
+ $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_QS;
+ break;
+ default:
+ die("More atext found where none is allowed, but unrecognised prior context: $context_prior");
+ }
+ } else {
+ $context_prior = $context;
+ $ord = ord($token);
+ if (($ord < 33) || ($ord > 126) || ($ord === 10)
+ || (!is_bool(strpos(ISEMAIL_STRING_SPECIALS, $token)))) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error
+ }
+
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;
+ $element_len++;
+ }
+ }
+ break;
+ //-------------------------------------------------------------
+ // Domain
+ //-------------------------------------------------------------
+ case ISEMAIL_COMPONENT_DOMAIN:
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // domain = dot-atom / domain-literal / obs-domain
+ //
+ // dot-atom = [CFWS] dot-atom-text [CFWS]
+ //
+ // dot-atom-text = 1*atext *("." 1*atext)
+ //
+ // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
+ //
+ // dtext = %d33-90 / ; Printable US-ASCII
+ // %d94-126 / ; characters not including
+ // obs-dtext ; "[", "]", or "\"
+ //
+ // obs-domain = atom *("." atom)
+ //
+ // atom = [CFWS] 1*atext [CFWS]
+
+
+ // http://tools.ietf.org/html/rfc5321#section-4.1.2
+ // Mailbox = Local-part "@" ( Domain / address-literal )
+ //
+ // Domain = sub-domain *("." sub-domain)
+ //
+ // address-literal = "[" ( IPv4-address-literal /
+ // IPv6-address-literal /
+ // General-address-literal ) "]"
+ // ; See Section 4.1.3
+
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // Note: A liberal syntax for the domain portion of addr-spec is
+ // given here. However, the domain portion contains addressing
+ // information specified by and used in other protocols (e.g.,
+ // [RFC1034], [RFC1035], [RFC1123], [RFC5321]). It is therefore
+ // incumbent upon implementations to conform to the syntax of
+ // addresses for the context in which they are used.
+ // is_email() author's note: it's not clear how to interpret this in
+ // the context of a general email address validator. The conclusion I
+ // have reached is this: "addressing information" must comply with
+ // RFC 5321 (and in turn RFC 1035), anything that is "semantically
+ // invisible" must comply only with RFC 5322.
+ switch ($token) {
+ // Comment
+ case ISEMAIL_STRING_OPENPARENTHESIS:
+ if ($element_len === 0) {
+ // Comments at the start of the domain are deprecated in the text
+ // Comments at the start of a subdomain are obs-domain
+ // (http://tools.ietf.org/html/rfc5322#section-3.4.1)
+ $return_status[] = ($element_count === 0) ?
+ ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_COMMENT;
+ } else {
+ $return_status[] = ISEMAIL_CFWS_COMMENT;
+ // We can't start a comment in the middle of an element, so this better be the end
+ $end_or_die = true;
+ }
+
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_COMMENT;
+ break;
+ // Next dot-atom element
+ case ISEMAIL_STRING_DOT:
+ if ($element_len === 0) {
+ // Another dot, already?
+ $return_status[] = ($element_count === 0) ?
+ ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS; // Fatal error
+ } elseif ($hyphen_flag) {
+ // Previous subdomain ended in a hyphen
+ $return_status[] = ISEMAIL_ERR_DOMAINHYPHENEND; // Fatal error
+ } else {
+ // Nowhere in RFC 5321 does it say explicitly that the
+ // domain part of a Mailbox must be a valid domain according
+ // to the DNS standards set out in RFC 1035, but this *is*
+ // implied in several places. For instance, wherever the idea
+ // of host routing is discussed the RFC says that the domain
+ // must be looked up in the DNS. This would be nonsense unless
+ // the domain was designed to be a valid DNS domain. Hence we
+ // must conclude that the RFC 1035 restriction on label length
+ // also applies to RFC 5321 domains.
+ //
+ // http://tools.ietf.org/html/rfc1035#section-2.3.4
+ // labels 63 octets or less
+ if ($element_len > 63) {
+ $return_status[] = ISEMAIL_RFC5322_LABEL_TOOLONG;
+ }
+ }
+ // CFWS is OK again now we're at the beginning of an element
+ // (although it may be obsolete CFWS)
+ $end_or_die = false;
+ $element_len = 0;
+ $element_count++;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] = '';
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+
+ break;
+ // Domain literal
+ case ISEMAIL_STRING_OPENSQBRACKET:
+ if ($parsedata[ISEMAIL_COMPONENT_DOMAIN] === '') {
+ $end_or_die = true; // Domain literal must be the only component
+ $element_len++;
+ $context_stack[] = $context;
+ $context = ISEMAIL_COMPONENT_LITERAL;
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;
+ $parsedata[ISEMAIL_COMPONENT_LITERAL] = '';
+ } else {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error
+ }
+
+ break;
+ // Folding White Space
+ case ISEMAIL_STRING_CR:
+ case ISEMAIL_STRING_SP:
+ case ISEMAIL_STRING_HTAB:
+ if (($token === ISEMAIL_STRING_CR) && ((++$i === $raw_length)
+ || ($email[$i] !== ISEMAIL_STRING_LF))) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF;
+ break;
+ } // Fatal error
+
+ if ($element_len === 0) {
+ $return_status[] = ($element_count === 0) ?
+ ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_FWS;
+ } else {
+ $return_status[] = ISEMAIL_CFWS_FWS;
+ // We can't start FWS in the middle of an element, so this better be the end
+ $end_or_die = true;
+ }
+
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_FWS;
+ $token_prior = $token;
+ break;
+ // atext
+ default:
+ // RFC 5322 allows any atext...
+ // http://tools.ietf.org/html/rfc5322#section-3.2.3
+ // atext = ALPHA / DIGIT / ; Printable US-ASCII
+ // "!" / "#" / ; characters not including
+ // "$" / "%" / ; specials. Used for atoms.
+ // "&" / "'" /
+ // "*" / "+" /
+ // "-" / "/" /
+ // "=" / "?" /
+ // "^" / "_" /
+ // "`" / "{" /
+ // "|" / "}" /
+ // "~"
+
+ // But RFC 5321 only allows letter-digit-hyphen to comply with DNS rules (RFCs 1034 & 1123)
+ // http://tools.ietf.org/html/rfc5321#section-4.1.2
+ // sub-domain = Let-dig [Ldh-str]
+ //
+ // Let-dig = ALPHA / DIGIT
+ //
+ // Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
+ //
+ if ($end_or_die) {
+ // We have encountered atext where it is no longer valid
+ switch ($context_prior) {
+ case ISEMAIL_CONTEXT_COMMENT:
+ case ISEMAIL_CONTEXT_FWS:
+ $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_CFWS;
+ break;
+ case ISEMAIL_COMPONENT_LITERAL:
+ $return_status[] = ISEMAIL_ERR_ATEXT_AFTER_DOMLIT;
+ break;
+ default:
+ die("More atext found where none is allowed, but unrecognised prior context: $context_prior");
+ }
+ }
+
+ $ord = ord($token);
+ $hyphen_flag = false; // Assume this token isn't a hyphen unless we discover it is
+
+ if (($ord < 33) || ($ord > 126) || (!is_bool(strpos(ISEMAIL_STRING_SPECIALS, $token)))) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_ATEXT; // Fatal error
+ } elseif ($token === ISEMAIL_STRING_HYPHEN) {
+ if ($element_len === 0) {
+ // Hyphens can't be at the beginning of a subdomain
+ $return_status[] = ISEMAIL_ERR_DOMAINHYPHENSTART; // Fatal error
+ }
+
+ $hyphen_flag = true;
+ } elseif (!(($ord > 47 && $ord < 58)
+ || ($ord > 64 && $ord < 91)
+ || ($ord > 96 && $ord < 123))) {
+ // Not an RFC 5321 subdomain, but still OK by RFC 5322
+ $return_status[] = ISEMAIL_RFC5322_DOMAIN;
+ }
+
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;
+ $element_len++;
+ }
+ break;
+ //-------------------------------------------------------------
+ // Domain literal
+ //-------------------------------------------------------------
+ case ISEMAIL_COMPONENT_LITERAL:
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
+ //
+ // dtext = %d33-90 / ; Printable US-ASCII
+ // %d94-126 / ; characters not including
+ // obs-dtext ; "[", "]", or "\"
+ //
+ // obs-dtext = obs-NO-WS-CTL / quoted-pair
+ switch ($token) {
+ // End of domain literal
+ case ISEMAIL_STRING_CLOSESQBRACKET:
+ if ((int) max($return_status) < ISEMAIL_DEPREC) {
+ // Could be a valid RFC 5321 address literal, so let's check
+
+ // http://tools.ietf.org/html/rfc5321#section-4.1.2
+ // address-literal = "[" ( IPv4-address-literal /
+ // IPv6-address-literal /
+ // General-address-literal ) "]"
+ // ; See Section 4.1.3
+ //
+ // http://tools.ietf.org/html/rfc5321#section-4.1.3
+ // IPv4-address-literal = Snum 3("." Snum)
+ //
+ // IPv6-address-literal = "IPv6:" IPv6-addr
+ //
+ // General-address-literal = Standardized-tag ":" 1*dcontent
+ //
+ // Standardized-tag = Ldh-str
+ // ; Standardized-tag MUST be specified in a
+ // ; Standards-Track RFC and registered with IANA
+ //
+ // dcontent = %d33-90 / ; Printable US-ASCII
+ // %d94-126 ; excl. "[", "\", "]"
+ //
+ // Snum = 1*3DIGIT
+ // ; representing a decimal integer
+ // ; value in the range 0 through 255
+ //
+ // IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
+ //
+ // IPv6-hex = 1*4HEXDIG
+ //
+ // IPv6-full = IPv6-hex 7(":" IPv6-hex)
+ //
+ // IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::"
+ // [IPv6-hex *5(":" IPv6-hex)]
+ // ; The "::" represents at least 2 16-bit groups of
+ // ; zeros. No more than 6 groups in addition to the
+ // ; "::" may be present.
+ //
+ // IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
+ //
+ // IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
+ // [IPv6-hex *3(":" IPv6-hex) ":"]
+ // IPv4-address-literal
+ // ; The "::" represents at least 2 16-bit groups of
+ // ; zeros. No more than 4 groups in addition to the
+ // ; "::" and IPv4-address-literal may be present.
+ //
+ // is_email() author's note: We can't use ip2long() to validate
+ // IPv4 addresses because it accepts abbreviated addresses
+ // (xxx.xxx.xxx), expanding the last group to complete the address.
+ // filter_var() validates IPv6 address inconsistently (up to PHP 5.3.3
+ // at least) -- see http://bugs.php.net/bug.php?id=53236 for example
+ $max_groups = 8;
+ $matchesIP = array();
+ /*.mixed.*/ $index = false;
+ $addressliteral = $parsedata[ISEMAIL_COMPONENT_LITERAL];
+
+ // 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) {
+ // Convert IPv4 part to IPv6 format for further testing
+ $addressliteral = substr($addressliteral, 0, $index) . '0:0';
+ }
+ }
+
+ if ($index === 0) {
+ // Nothing there except a valid IPv4 address, so...
+ $return_status[] = ISEMAIL_RFC5321_ADDRESSLITERAL;
+ } elseif (strncasecmp($addressliteral, ISEMAIL_STRING_IPV6TAG, 5) !== 0) {
+ $return_status[] = ISEMAIL_RFC5322_DOMAINLITERAL;
+ } else {
+ $IPv6 = substr($addressliteral, 5);
+ // Revision 2.7: Daniel Marschall's new IPv6 testing strategy
+ $matchesIP = explode(ISEMAIL_STRING_COLON, $IPv6);
+ $groupCount = count($matchesIP);
+ $index = strpos($IPv6, ISEMAIL_STRING_DOUBLECOLON);
+
+ if ($index === false) {
+ // We need exactly the right number of groups
+ if ($groupCount !== $max_groups) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_GRPCOUNT;
+ }
+ } else {
+ if ($index !== strrpos($IPv6, ISEMAIL_STRING_DOUBLECOLON)) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_2X2XCOLON;
+ } else {
+ if ($index === 0 || $index === (strlen($IPv6) - 2)) {
+ $max_groups++;
+ // RFC 4291 allows :: at the start or end of an address with 7 other groups in addition
+ }
+
+ if ($groupCount > $max_groups) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_MAXGRPS;
+ } elseif ($groupCount === $max_groups) {
+ $return_status[] = ISEMAIL_RFC5321_IPV6DEPRECATED; // Eliding a single "::"
+ }
+ }
+ }
+
+ // Revision 2.7: Daniel Marschall's new IPv6 testing strategy
+ if ((substr($IPv6, 0, 1) === ISEMAIL_STRING_COLON) && (substr($IPv6, 1, 1) !== ISEMAIL_STRING_COLON)) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_COLONSTRT; // Address starts with a single colon
+ } elseif ((substr($IPv6, -1) === ISEMAIL_STRING_COLON) && (substr($IPv6, -2, 1) !== ISEMAIL_STRING_COLON)) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_COLONEND; // Address ends with a single colon
+ } elseif (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
+ $return_status[] = ISEMAIL_RFC5322_IPV6_BADCHAR; // Check for unmatched characters
+ } else {
+ $return_status[] = ISEMAIL_RFC5321_ADDRESSLITERAL;
+ }
+ }
+ } else {
+ $return_status[] = ISEMAIL_RFC5322_DOMAINLITERAL;
+ }
+
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;
+ $element_len++;
+ $context_prior = $context;
+ $context = (int) array_pop($context_stack);
+ break;
+ case ISEMAIL_STRING_BACKSLASH:
+ $return_status[] = ISEMAIL_RFC5322_DOMLIT_OBSDTEXT;
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_QUOTEDPAIR;
+ break;
+ // Folding White Space
+ case ISEMAIL_STRING_CR:
+ case ISEMAIL_STRING_SP:
+ case ISEMAIL_STRING_HTAB:
+ if (($token === ISEMAIL_STRING_CR)
+ && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF;
+ break;
+ } // Fatal error
+
+ $return_status[] = ISEMAIL_CFWS_FWS;
+
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_FWS;
+ $token_prior = $token;
+ break;
+ // dtext
+ default:
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // dtext = %d33-90 / ; Printable US-ASCII
+ // %d94-126 / ; characters not including
+ // obs-dtext ; "[", "]", or "\"
+ //
+ // obs-dtext = obs-NO-WS-CTL / quoted-pair
+ //
+ // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
+ // %d11 / ; characters that do not
+ // %d12 / ; include the carriage
+ // %d14-31 / ; return, line feed, and
+ // %d127 ; white space characters
+ $ord = ord($token);
+
+ // CR, LF, SP & HTAB have already been parsed above
+ if (($ord > 127) || ($ord === 0) || ($token === ISEMAIL_STRING_OPENSQBRACKET)) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_DTEXT; // Fatal error
+ break;
+ } elseif (($ord < 33) || ($ord === 127)) {
+ $return_status[] = ISEMAIL_RFC5322_DOMLIT_OBSDTEXT;
+ }
+
+ $parsedata[ISEMAIL_COMPONENT_LITERAL] .= $token;
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;
+ $element_len++;
+ }
+ break;
+ //-------------------------------------------------------------
+ // Quoted string
+ //-------------------------------------------------------------
+ case ISEMAIL_CONTEXT_QUOTEDSTRING:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.4
+ // quoted-string = [CFWS]
+ // DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+ // [CFWS]
+ //
+ // qcontent = qtext / quoted-pair
+ switch ($token) {
+ // Quoted pair
+ case ISEMAIL_STRING_BACKSLASH:
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_QUOTEDPAIR;
+ break;
+ // Folding White Space
+ // Inside a quoted string, spaces are allowed as regular characters.
+ // It's only FWS if we include HTAB or CRLF
+ case ISEMAIL_STRING_CR:
+ case ISEMAIL_STRING_HTAB:
+ if (($token === ISEMAIL_STRING_CR)
+ && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF;
+ break;
+ }// Fatal error
+
+ // http://tools.ietf.org/html/rfc5322#section-3.2.2
+ // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
+ // structured header field are semantically interpreted as a single
+ // space character.
+
+ // http://tools.ietf.org/html/rfc5322#section-3.2.4
+ // the CRLF in any FWS/CFWS that appears within the quoted-string [is]
+ // semantically "invisible" and therefore not part of the quoted-string
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= ISEMAIL_STRING_SP;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= ISEMAIL_STRING_SP;
+ $element_len++;
+
+ $return_status[] = ISEMAIL_CFWS_FWS;
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_FWS;
+ $token_prior = $token;
+ break;
+ // End of quoted string
+ case ISEMAIL_STRING_DQUOTE:
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;
+ $element_len++;
+ $context_prior = $context;
+ $context = (int) array_pop($context_stack);
+ break;
+ // qtext
+ default:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.4
+ // qtext = %d33 / ; Printable US-ASCII
+ // %d35-91 / ; characters not including
+ // %d93-126 / ; "\" or the quote character
+ // obs-qtext
+ //
+ // obs-qtext = obs-NO-WS-CTL
+ //
+ // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
+ // %d11 / ; characters that do not
+ // %d12 / ; include the carriage
+ // %d14-31 / ; return, line feed, and
+ // %d127 ; white space characters
+ $ord = ord($token);
+
+ if (($ord > 127) || ($ord === 0) || ($ord === 10)) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_QTEXT; // Fatal error
+ } elseif (($ord < 32) || ($ord === 127)) {
+ $return_status[] = ISEMAIL_DEPREC_QTEXT;
+ }
+
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;
+ $element_len++;
+ }
+
+ // http://tools.ietf.org/html/rfc5322#section-3.4.1
+ // If the
+ // string can be represented as a dot-atom (that is, it contains no
+ // characters other than atext characters or "." surrounded by atext
+ // characters), then the dot-atom form SHOULD be used and the quoted-
+ // string form SHOULD NOT be used.
+ // To do
+ break;
+ //-------------------------------------------------------------
+ // Quoted pair
+ //-------------------------------------------------------------
+ case ISEMAIL_CONTEXT_QUOTEDPAIR:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.1
+ // quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
+ //
+ // VCHAR = %d33-126 ; visible (printing) characters
+ // WSP = SP / HTAB ; white space
+ //
+ // obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
+ //
+ // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
+ // %d11 / ; characters that do not
+ // %d12 / ; include the carriage
+ // %d14-31 / ; return, line feed, and
+ // %d127 ; white space characters
+ //
+ // i.e. obs-qp = "\" (%d0-8, %d10-31 / %d127)
+ $ord = ord($token);
+
+ if ($ord > 127) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_QPAIR; // Fatal error
+ } elseif ((($ord < 31) && ($ord !== 9)) || ($ord === 127)) {
+ // SP & HTAB are allowed
+ $return_status[] = ISEMAIL_DEPREC_QP;
+ }
+
+ // At this point we know where this qpair occurred so
+ // we could check to see if the character actually
+ // needed to be quoted at all.
+ // http://tools.ietf.org/html/rfc5321#section-4.1.2
+ // the sending system SHOULD transmit the
+ // form that uses the minimum quoting possible.
+ // To do: check whether the character needs to be quoted (escaped) in this context
+ $context_prior = $context;
+ $context = (int) array_pop($context_stack); // End of qpair
+ $token = ISEMAIL_STRING_BACKSLASH . $token;
+
+ switch ($context) {
+ case ISEMAIL_CONTEXT_COMMENT:
+ break;
+ case ISEMAIL_CONTEXT_QUOTEDSTRING:
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_LOCALPART][$element_count] .= $token;
+ $element_len += 2;
+ // The maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash
+ break;
+ case ISEMAIL_COMPONENT_LITERAL:
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= $token;
+ $atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count] .= $token;
+ $element_len += 2;
+ // The maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash
+ break;
+ default:
+ die("Quoted pair logic invoked in an invalid context: $context");
+ }
+
+ break;
+ //-------------------------------------------------------------
+ // Comment
+ //-------------------------------------------------------------
+ case ISEMAIL_CONTEXT_COMMENT:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.2
+ // comment = "(" *([FWS] ccontent) [FWS] ")"
+ //
+ // ccontent = ctext / quoted-pair / comment
+ switch ($token) {
+ // Nested comment
+ case ISEMAIL_STRING_OPENPARENTHESIS:
+ // Nested comments are OK
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_COMMENT;
+ break;
+ // End of comment
+ case ISEMAIL_STRING_CLOSEPARENTHESIS:
+ $context_prior = $context;
+ $context = (int) array_pop($context_stack);
+
+ // http://tools.ietf.org/html/rfc5322#section-3.2.2
+ // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
+ // structured header field are semantically interpreted as a single
+ // space character.
+ //
+ // is_email() author's note: This *cannot* mean that we must add a
+ // space to the address wherever CFWS appears. This would result in
+ // any addr-spec that had CFWS outside a quoted string being invalid
+ // for RFC 5321.
+ // if (($context === ISEMAIL_COMPONENT_LOCALPART) || ($context === ISEMAIL_COMPONENT_DOMAIN)) {
+ // $parsedata[$context] .= ISEMAIL_STRING_SP;
+ // $atomlist[$context][$element_count] .= ISEMAIL_STRING_SP;
+ // $element_len++;
+ // }
+
+ break;
+ // Quoted pair
+ case ISEMAIL_STRING_BACKSLASH:
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_QUOTEDPAIR;
+ break;
+ // Folding White Space
+ case ISEMAIL_STRING_CR:
+ case ISEMAIL_STRING_SP:
+ case ISEMAIL_STRING_HTAB:
+ if (($token === ISEMAIL_STRING_CR)
+ && ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF))) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF;
+ break;
+ } // Fatal error
+
+ $return_status[] = ISEMAIL_CFWS_FWS;
+
+ $context_stack[] = $context;
+ $context = ISEMAIL_CONTEXT_FWS;
+ $token_prior = $token;
+ break;
+ // ctext
+ default:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.3
+ // ctext = %d33-39 / ; Printable US-ASCII
+ // %d42-91 / ; characters not including
+ // %d93-126 / ; "(", ")", or "\"
+ // obs-ctext
+ //
+ // obs-ctext = obs-NO-WS-CTL
+ //
+ // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
+ // %d11 / ; characters that do not
+ // %d12 / ; include the carriage
+ // %d14-31 / ; return, line feed, and
+ // %d127 ; white space characters
+ $ord = ord($token);
+
+ if (($ord > 127) || ($ord === 0) || ($ord === 10)) {
+ $return_status[] = ISEMAIL_ERR_EXPECTING_CTEXT; // Fatal error
+ break;
+ } elseif (($ord < 32) || ($ord === 127)) {
+ $return_status[] = ISEMAIL_DEPREC_CTEXT;
+ }
+ }
+ break;
+ //-------------------------------------------------------------
+ // Folding White Space
+ //-------------------------------------------------------------
+ case ISEMAIL_CONTEXT_FWS:
+ // http://tools.ietf.org/html/rfc5322#section-3.2.2
+ // FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
+ // ; Folding white space
+
+ // But note the erratum:
+ // http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908:
+ // In the obsolete syntax, any amount of folding white space MAY be
+ // inserted where the obs-FWS rule is allowed. This creates the
+ // possibility of having two consecutive "folds" in a line, and
+ // therefore the possibility that a line which makes up a folded header
+ // field could be composed entirely of white space.
+ //
+ // obs-FWS = 1*([CRLF] WSP)
+ if ($token_prior === ISEMAIL_STRING_CR) {
+ if ($token === ISEMAIL_STRING_CR) {
+ $return_status[] = ISEMAIL_ERR_FWS_CRLF_X2; // Fatal error
+ break;
+ }
+ if (isset($crlf_count)) {
+ if (++$crlf_count > 1) {
+ $return_status[] = ISEMAIL_DEPREC_FWS; // Multiple folds = obsolete FWS
+ }
+ } else {
+ $crlf_count = 1;
+ }
+ }
+
+ switch ($token) {
+ case ISEMAIL_STRING_CR:
+ if ((++$i === $raw_length) || ($email[$i] !== ISEMAIL_STRING_LF)) {
+ $return_status[] = ISEMAIL_ERR_CR_NO_LF; // Fatal error
+ }
+ break;
+ case ISEMAIL_STRING_SP:
+ case ISEMAIL_STRING_HTAB:
+ break;
+ default:
+ if ($token_prior === ISEMAIL_STRING_CR) {
+ $return_status[] = ISEMAIL_ERR_FWS_CRLF_END; // Fatal error
+ break;
+ }
+
+ if (isset($crlf_count)) {
+ unset($crlf_count);
+ }
+
+ $context_prior = $context;
+ $context = (int) array_pop($context_stack); // End of FWS
+
+ // http://tools.ietf.org/html/rfc5322#section-3.2.2
+ // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
+ // structured header field are semantically interpreted as a single
+ // space character.
+ //
+ // is_email() author's note: This *cannot* mean that we must add a
+ // space to the address wherever CFWS appears. This would result in
+ // any addr-spec that had CFWS outside a quoted string being invalid
+ // for RFC 5321.
+ // if (($context === ISEMAIL_COMPONENT_LOCALPART) || ($context === ISEMAIL_COMPONENT_DOMAIN)) {
+ // $parsedata[$context] .= ISEMAIL_STRING_SP;
+ // $atomlist[$context][$element_count] .= ISEMAIL_STRING_SP;
+ // $element_len++;
+ // }
+
+ $i--; // Look at this token again in the parent context
+ }
+
+ $token_prior = $token;
+ break;
+ //-------------------------------------------------------------
+ // A context we aren't expecting
+ //-------------------------------------------------------------
+ default:
+ die("Unknown context: $context");
+ }
+
+//-echo "<td>$context|",(($end_or_die) ? 'true' : 'false'),"|$token|" . max($return_status) . "</td></tr>"; // debug
+ if ((int) max($return_status) > ISEMAIL_RFC5322) {
+ break; // No point going on if we've got a fatal error
+ }
+ }
+
+ // Some simple final tests
+ if ((int) max($return_status) < ISEMAIL_RFC5322) {
+ if ($context === ISEMAIL_CONTEXT_QUOTEDSTRING) {
+ $return_status[] = ISEMAIL_ERR_UNCLOSEDQUOTEDSTR; // Fatal error
+ } elseif ($context === ISEMAIL_CONTEXT_QUOTEDPAIR) {
+ $return_status[] = ISEMAIL_ERR_BACKSLASHEND; // Fatal error
+ } elseif ($context === ISEMAIL_CONTEXT_COMMENT) {
+ $return_status[] = ISEMAIL_ERR_UNCLOSEDCOMMENT; // Fatal error
+ } elseif ($context === ISEMAIL_COMPONENT_LITERAL) {
+ $return_status[] = ISEMAIL_ERR_UNCLOSEDDOMLIT; // Fatal error
+ } elseif ($token === ISEMAIL_STRING_CR) {
+ $return_status[] = ISEMAIL_ERR_FWS_CRLF_END; // Fatal error
+ } elseif ($parsedata[ISEMAIL_COMPONENT_DOMAIN] === '') {
+ $return_status[] = ISEMAIL_ERR_NODOMAIN; // Fatal error
+ } elseif ($element_len === 0) {
+ $return_status[] = ISEMAIL_ERR_DOT_END; // Fatal error
+ } elseif ($hyphen_flag) {
+ $return_status[] = ISEMAIL_ERR_DOMAINHYPHENEND; // Fatal error
+ } elseif (strlen($parsedata[ISEMAIL_COMPONENT_DOMAIN]) > 255) {
+ // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.2
+ // The maximum total length of a domain name or number is 255 octets.
+ $return_status[] = ISEMAIL_RFC5322_DOMAIN_TOOLONG;
+ } elseif (strlen(
+ $parsedata[ISEMAIL_COMPONENT_LOCALPART] . ISEMAIL_STRING_AT . $parsedata[ISEMAIL_COMPONENT_DOMAIN]
+ ) > 254) {
+ // http://tools.ietf.org/html/rfc5321#section-4.1.2
+ // Forward-path = Path
+ //
+ // Path = "<" [ A-d-l ":" ] Mailbox ">"
+ //
+ // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
+ // The maximum total length of a reverse-path or forward-path is 256
+ // octets (including the punctuation and element separators).
+ //
+ // Thus, even without (obsolete) routing information, the Mailbox can
+ // only be 254 characters long. This is confirmed by this verified
+ // erratum to RFC 3696:
+ //
+ // http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690
+ // However, there is a restriction in RFC 2821 on the length of an
+ // address in MAIL and RCPT commands of 254 characters. Since addresses
+ // that do not fit in those fields are not normally useful, the upper
+ // limit on address lengths should normally be considered to be 254.
+ $return_status[] = ISEMAIL_RFC5322_TOOLONG;
+ } elseif ($element_len > 63) {
+ // http://tools.ietf.org/html/rfc1035#section-2.3.4
+ // labels 63 octets or less
+ $return_status[] = ISEMAIL_RFC5322_LABEL_TOOLONG;
+ }
+ }
+
+ // Check DNS?
+ $dns_checked = false;
+
+ if ($checkDNS && ((int) max($return_status) < ISEMAIL_DNSWARN) && function_exists('dns_get_record')) {
+ // http://tools.ietf.org/html/rfc5321#section-2.3.5
+ // Names that can
+ // be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
+ // in Section 5) are permitted, as are CNAME RRs whose targets can be
+ // resolved, in turn, to MX or address RRs.
+ //
+ // http://tools.ietf.org/html/rfc5321#section-5.1
+ // The lookup first attempts to locate an MX record associated with the
+ // name. If a CNAME record is found, the resulting name is processed as
+ // if it were the initial name. ... If an empty list of MXs is returned,
+ // the address is treated as if it was associated with an implicit MX
+ // RR, with a preference of 0, pointing to that host.
+ //
+ // is_email() author's note: We will regard the existence of a CNAME to be
+ // sufficient evidence of the domain's existence. For performance reasons
+ // we will not repeat the DNS lookup for the CNAME's target, but we will
+ // raise a warning because we didn't immediately find an MX record.
+ if ($element_count === 0) {
+ $parsedata[ISEMAIL_COMPONENT_DOMAIN] .= '.';
+ // Checking TLD DNS seems to work only if you explicitly check from the root
+ }
+ // Not using checkdnsrr because of a suspected bug in PHP 5.3 (http://bugs.php.net/bug.php?id=51844)
+ $result = @dns_get_record($parsedata[ISEMAIL_COMPONENT_DOMAIN], DNS_MX);
+
+ if ((is_bool($result) && !(bool) $result)) {
+ $return_status[] = ISEMAIL_DNSWARN_NO_RECORD;
+ // Domain can't be found in DNS
+ } else {
+ if (count($result) === 0) {
+ $return_status[] = ISEMAIL_DNSWARN_NO_MX_RECORD; // MX-record for domain can't be found
+ $result = @dns_get_record($parsedata[ISEMAIL_COMPONENT_DOMAIN], DNS_A + DNS_CNAME);
+
+ if (count($result) === 0) {
+ $return_status[] = ISEMAIL_DNSWARN_NO_RECORD;
+ // No usable records for the domain can be found
+ }
+ } else {
+ $dns_checked = true;
+ }
+ }
+ }
+
+ // Check for TLD addresses
+ // -----------------------
+ // TLD addresses are specifically allowed in RFC 5321 but they are
+ // unusual to say the least. We will allocate a separate
+ // status to these addresses on the basis that they are more likely
+ // to be typos than genuine addresses (unless we've already
+ // established that the domain does have an MX record)
+ //
+ // http://tools.ietf.org/html/rfc5321#section-2.3.5
+ // In the case
+ // of a top-level domain used by itself in an email address, a single
+ // string is used without any dots. This makes the requirement,
+ // described in more detail below, that only fully-qualified domain
+ // names appear in SMTP transactions on the public Internet,
+ // particularly important where top-level domains are involved.
+ //
+ // TLD format
+ // ----------
+ // The format of TLDs has changed a number of times. The standards
+ // used by IANA have been largely ignored by ICANN, leading to
+ // confusion over the standards being followed. These are not defined
+ // anywhere, except as a general component of a DNS host name (a label).
+ // However, this could potentially lead to 123.123.123.123 being a
+ // valid DNS name (rather than an IP address) and thereby creating
+ // an ambiguity. The most authoritative statement on TLD formats that
+ // the author can find is in a (rejected!) erratum to RFC 1123
+ // submitted by John Klensin, the author of RFC 5321:
+ //
+ // http://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
+ // However, a valid host name can never have the dotted-decimal
+ // form #.#.#.#, since this change does not permit the highest-level
+ // component label to start with a digit even if it is not all-numeric.
+ if (!$dns_checked && ((int) max($return_status) < ISEMAIL_DNSWARN)) {
+ if ($element_count === 0) {
+ $return_status[] = ISEMAIL_RFC5321_TLD;
+ }
+
+ if (is_numeric($atomlist[ISEMAIL_COMPONENT_DOMAIN][$element_count][0])) {
+ $return_status[] = ISEMAIL_RFC5321_TLDNUMERIC;
+ }
+ }
+
+ $return_status = array_unique($return_status);
+ $final_status = (int) max($return_status);
+
+ if (count($return_status) !== 1) {
+ array_shift($return_status); // remove redundant ISEMAIL_VALID
+ }
+
+ $parsedata['status'] = $return_status;
+
+ if ($final_status < $threshold) {
+ $final_status = ISEMAIL_VALID;
+ }
+
+ return ($diagnose) ? $final_status : ($final_status < ISEMAIL_THRESHOLD);
+}