Page 1 of 1

Sending spam through my hosting

Posted: Wed Dec 07, 2011 9:20 am
by luckylouie
Hi;

I had an email from my hosting company which said that my account was hacked and one script in images folder is trying to send thousands of spams(file name : "/public_html/images/sm5vy7.php"). they blocked my account and asked me to check if there is any script or code that may cause this problem.
The only server side page I had was a contact.php file that has mail() function in it. the code is like this;

Code: Select all

if( isset($_POST['submit'])) {
$name=$_POST['name'];
$comment=$_POST['comment'];
$email=$_POST['email'];
$phone=$_POST['phone'];
$to = "sample@gmail.com";
$subject = "sample";
$message = "sample";
$from = "$email";
$headers = "Content-type:text;charset=utf-8" . "\r\n";
$headers .= "From: $from" . "\r\n";
mail($to,$subject,$message,$headers);
}
My question is "can the code I used cause any security problem that someone be able to create a php file in my images folder or someone has accessed my account?"

Thank you in advance
I'm really in a big trouble

Re: Sending spam through my hosting

Posted: Wed Dec 07, 2011 2:19 pm
by Weirdan
This particular snippet is unlikely to have allowed php file upload. However it's prone to email header injection |(because you did not validate the data in $email variable), which alone is enough to be able to send spam from your site.

What you need to be aware though is that many of such issues result from the website's owner computers being infected with a trojan/backdoor/rootkit that steals ftp/ssh passwords. So in addition to removing malicious scripts from your site you need to check your own computer (or any other computer you might have used to access webserver's filesystem).

Re: Sending spam through my hosting

Posted: Sun Dec 11, 2011 8:18 pm
by Bind
sanitize email address to prevent form hijacking injecton attacks:

Code: Select all

function SanitizeEmail($email)
   {
      return preg_replace( '((?:\n|\r|\t|%0A|%0D|%08|%09)+)i' , '', $email);
   }
validate the proper email address format:

Code: Select all

<?php
/**
 * http://isemail.info/about
 *
 * To validate an email address according to RFCs 5321, 5322 and others
 * 
 * Copyright © 2008-2011, Dominic Sayers					<br>
 * Test schema documentation Copyright © 2011, Daniel Marschall			<br>
 * 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.01.1 - Fixed examples and readme.txt
 */

// 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?
					$return_status[] = ($element_count === 0) ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS;	// Fatal error
				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;

					$end_or_die	= false;	// CFWS & quoted strings are OK again now we're at the beginning of an element (although they are obsolete forms)
					$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
					$end_or_die = true;	// We can't start FWS in the middle of an element, so this better be the end

				$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
				// 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.
				elseif	(strlen($parsedata[ISEMAIL_COMPONENT_LOCALPART]) > 64)
								$return_status[]	= ISEMAIL_RFC5322_LOCAL_TOOLONG;
				// 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.
				elseif	(($context_prior === ISEMAIL_CONTEXT_COMMENT) || ($context_prior === ISEMAIL_CONTEXT_FWS))
								$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;
					$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?
					$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;

					$end_or_die		= false;	// CFWS is OK again now we're at the beginning of an element (although it may be obsolete CFWS)
					$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;
					$end_or_die	= true;	// We can't start FWS in the middle of an element, so this better be the end
				}

				$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) $addressliteral = substr($addressliteral, 0, $index) . '0:0'; // Convert IPv4 part to IPv6 format for further testing
					}

					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);
						$matchesIP	= explode(ISEMAIL_STRING_COLON, $IPv6);	// Revision 2.7: Daniel Marschall's new IPv6 testing strategy
						$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
		// 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.
		elseif	(strlen($parsedata[ISEMAIL_COMPONENT_DOMAIN]) > 255)
										$return_status[]	= ISEMAIL_RFC5322_DOMAIN_TOOLONG;
		// 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.
		elseif	(strlen($parsedata[ISEMAIL_COMPONENT_LOCALPART] . ISEMAIL_STRING_AT . $parsedata[ISEMAIL_COMPONENT_DOMAIN]) > 254)
										$return_status[]	= ISEMAIL_RFC5322_TOOLONG;
		// http://tools.ietf.org/html/rfc1035#section-2.3.4
		// labels          63 octets or less
		elseif ($element_len > 63)					$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

		$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)

		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);
}
?>