diff options
Diffstat (limited to 'libs/Zend/Validate/EmailAddress.php')
-rw-r--r-- | libs/Zend/Validate/EmailAddress.php | 502 |
1 files changed, 399 insertions, 103 deletions
diff --git a/libs/Zend/Validate/EmailAddress.php b/libs/Zend/Validate/EmailAddress.php index 4948bb83f3..c008dcfe9f 100644 --- a/libs/Zend/Validate/EmailAddress.php +++ b/libs/Zend/Validate/EmailAddress.php @@ -14,25 +14,25 @@ * * @category Zend * @package Zend_Validate - * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @version $Id: EmailAddress.php 16223 2009-06-21 20:04:53Z thomas $ + * @version $Id: EmailAddress.php 21461 2010-03-10 22:34:03Z thomas $ */ /** * @see Zend_Validate_Abstract */ -require_once 'Zend/Validate/Abstract.php'; +// require_once 'Zend/Validate/Abstract.php'; /** * @see Zend_Validate_Hostname */ -require_once 'Zend/Validate/Hostname.php'; +// require_once 'Zend/Validate/Hostname.php'; /** * @category Zend * @package Zend_Validate - * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract @@ -41,6 +41,7 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract const INVALID_FORMAT = 'emailAddressInvalidFormat'; const INVALID_HOSTNAME = 'emailAddressInvalidHostname'; const INVALID_MX_RECORD = 'emailAddressInvalidMxRecord'; + const INVALID_SEGMENT = 'emailAddressInvalidSegment'; const DOT_ATOM = 'emailAddressDotAtom'; const QUOTED_STRING = 'emailAddressQuotedString'; const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart'; @@ -51,37 +52,47 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract */ protected $_messageTemplates = array( self::INVALID => "Invalid type given, value should be a string", - self::INVALID_FORMAT => "'%value%' is not a valid email address in the basic format local-part@hostname", - self::INVALID_HOSTNAME => "'%hostname%' is not a valid hostname for email address '%value%'", + self::INVALID_FORMAT => "'%value%' is no valid email address in the basic format local-part@hostname", + self::INVALID_HOSTNAME => "'%hostname%' is no valid hostname for email address '%value%'", self::INVALID_MX_RECORD => "'%hostname%' does not appear to have a valid MX record for the email address '%value%'", - self::DOT_ATOM => "'%localPart%' not matched against dot-atom format", - self::QUOTED_STRING => "'%localPart%' not matched against quoted-string format", - self::INVALID_LOCAL_PART => "'%localPart%' is not a valid local part for email address '%value%'", - self::LENGTH_EXCEEDED => "'%value%' exceeds the allowed length" + self::INVALID_SEGMENT => "'%hostname%' is not in a routable network segment. The email address '%value%' should not be resolved from public network.", + self::DOT_ATOM => "'%localPart%' can not be matched against dot-atom format", + self::QUOTED_STRING => "'%localPart%' can not be matched against quoted-string format", + self::INVALID_LOCAL_PART => "'%localPart%' is no valid local part for email address '%value%'", + self::LENGTH_EXCEEDED => "'%value%' exceeds the allowed length", ); /** + * @see http://en.wikipedia.org/wiki/IPv4 * @var array */ - protected $_messageVariables = array( - 'hostname' => '_hostname', - 'localPart' => '_localPart' + protected $_invalidIp = array( + '0' => '0.0.0.0/8', + '10' => '10.0.0.0/8', + '127' => '127.0.0.0/8', + '128' => '128.0.0.0/16', + '169' => '169.254.0.0/16', + '172' => '172.16.0.0/12', + '191' => '191.255.0.0/16', + '192' => array( + '192.0.0.0/24', + '192.0.2.0/24', + '192.88.99.0/24', + '192.168.0.0/16' + ), + '198' => '198.18.0.0/15', + '223' => '223.255.255.0/24', + '224' => '224.0.0.0/4', + '240' => '240.0.0.0/4' ); /** - * Local object for validating the hostname part of an email address - * - * @var Zend_Validate_Hostname - * @depreciated - */ - public $hostnameValidator; - - /** - * Whether we check for a valid MX record via DNS - * - * @var boolean + * @var array */ - protected $_validateMx = false; + protected $_messageVariables = array( + 'hostname' => '_hostname', + 'localPart' => '_localPart' + ); /** * @var string @@ -94,21 +105,118 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract protected $_localPart; /** + * Internal options array + */ + protected $_options = array( + 'mx' => false, + 'deep' => false, + 'domain' => true, + 'allow' => Zend_Validate_Hostname::ALLOW_DNS, + 'hostname' => null + ); + + /** * Instantiates hostname validator for local use * - * You can pass a bitfield to determine what types of hostnames are allowed. - * These bitfields are defined by the ALLOW_* constants in Zend_Validate_Hostname - * The default is to allow DNS hostnames only + * The following option keys are supported: + * 'hostname' => A hostname validator, see Zend_Validate_Hostname + * 'allow' => Options for the hostname validator, see Zend_Validate_Hostname::ALLOW_* + * 'mx' => If MX check should be enabled, boolean + * 'deep' => If a deep MX check should be done, boolean * - * @param integer $allow OPTIONAL - * @param bool $validateMx OPTIONAL - * @param Zend_Validate_Hostname $hostnameValidator OPTIONAL + * @param array|Zend_Config $options OPTIONAL * @return void */ - public function __construct($allow = Zend_Validate_Hostname::ALLOW_DNS, $validateMx = false, Zend_Validate_Hostname $hostnameValidator = null) + public function __construct($options = array()) { - $this->setValidateMx($validateMx); - $this->setHostnameValidator($hostnameValidator, $allow); + if ($options instanceof Zend_Config) { + $options = $options->toArray(); + } else if (!is_array($options)) { + $options = func_get_args(); + $temp['allow'] = array_shift($options); + if (!empty($options)) { + $temp['mx'] = array_shift($options); + } + + if (!empty($options)) { + $temp['hostname'] = array_shift($options); + } + + $options = $temp; + } + + $options += $this->_options; + $this->setOptions($options); + } + + /** + * Returns all set Options + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Set options for the email validator + * + * @param array $options + * @return Zend_Validate_EmailAddress fluid interface + */ + public function setOptions(array $options = array()) + { + if (array_key_exists('messages', $options)) { + $this->setMessages($options['messages']); + } + + if (array_key_exists('hostname', $options)) { + if (array_key_exists('allow', $options)) { + $this->setHostnameValidator($options['hostname'], $options['allow']); + } else { + $this->setHostnameValidator($options['hostname']); + } + } + + if (array_key_exists('mx', $options)) { + $this->setValidateMx($options['mx']); + } + + if (array_key_exists('deep', $options)) { + $this->setDeepMxCheck($options['deep']); + } + + if (array_key_exists('domain', $options)) { + $this->setDomainCheck($options['domain']); + } + + return $this; + } + + /** + * Sets the validation failure message template for a particular key + * Adds the ability to set messages to the attached hostname validator + * + * @param string $messageString + * @param string $messageKey OPTIONAL + * @return Zend_Validate_Abstract Provides a fluent interface + * @throws Zend_Validate_Exception + */ + public function setMessage($messageString, $messageKey = null) + { + $messageKeys = $messageKey; + if ($messageKey === null) { + $keys = array_keys($this->_messageTemplates); + $messageKeys = current($keys); + } + + if (!isset($this->_messageTemplates[$messageKeys])) { + $this->_options['hostname']->setMessage($messageString, $messageKey); + } + + $this->_messageTemplates[$messageKeys] = $messageString; + return $this; } /** @@ -118,7 +226,7 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract */ public function getHostnameValidator() { - return $this->hostnameValidator; + return $this->_options['hostname']; } /** @@ -128,14 +236,17 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract */ public function setHostnameValidator(Zend_Validate_Hostname $hostnameValidator = null, $allow = Zend_Validate_Hostname::ALLOW_DNS) { - if ($hostnameValidator === null) { + if (!$hostnameValidator) { $hostnameValidator = new Zend_Validate_Hostname($allow); } - $this->hostnameValidator = $hostnameValidator; + + $this->_options['hostname'] = $hostnameValidator; + $this->_options['allow'] = $allow; + return $this; } /** - * Whether MX checking via dns_get_mx is supported or not + * Whether MX checking via getmxrr is supported or not * * This currently only works on UNIX systems * @@ -143,7 +254,17 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract */ public function validateMxSupported() { - return function_exists('dns_get_mx'); + return function_exists('getmxrr'); + } + + /** + * Returns the set validateMx option + * + * @return boolean + */ + public function getValidateMx() + { + return $this->_options['mx']; } /** @@ -151,11 +272,236 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract * * This only applies when DNS hostnames are validated * - * @param boolean $allowed Set allowed to true to validate for MX records, and false to not validate them + * @param boolean $mx Set allowed to true to validate for MX records, and false to not validate them + * @return Zend_Validate_EmailAddress Fluid Interface + */ + public function setValidateMx($mx) + { + if ((bool) $mx && !$this->validateMxSupported()) { + // require_once 'Zend/Validate/Exception.php'; + throw new Zend_Validate_Exception('MX checking not available on this system'); + } + + $this->_options['mx'] = (bool) $mx; + return $this; + } + + /** + * Returns the set deepMxCheck option + * + * @return boolean + */ + public function getDeepMxCheck() + { + return $this->_options['deep']; + } + + /** + * Set whether we check MX record should be a deep validation + * + * @param boolean $deep Set deep to true to perform a deep validation process for MX records + * @return Zend_Validate_EmailAddress Fluid Interface */ - public function setValidateMx($allowed) + public function setDeepMxCheck($deep) { - $this->_validateMx = (bool) $allowed; + $this->_options['deep'] = (bool) $deep; + return $this; + } + + /** + * Returns the set domainCheck option + * + * @return unknown + */ + public function getDomainCheck() + { + return $this->_options['domain']; + } + + /** + * Sets if the domain should also be checked + * or only the local part of the email address + * + * @param boolean $domain + * @return Zend_Validate_EmailAddress Fluid Interface + */ + public function setDomainCheck($domain = true) + { + $this->_options['domain'] = (boolean) $domain; + return $this; + } + + /** + * Returns if the given host is reserved + * + * @param string $host + * @return boolean + */ + private function _isReserved($host){ + if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) { + $host = gethostbyname($host); + } + + $octet = explode('.',$host); + if ((int)$octet[0] >= 224) { + return true; + } else if (array_key_exists($octet[0], $this->_invalidIp)) { + foreach ((array)$this->_invalidIp[$octet[0]] as $subnetData) { + // we skip the first loop as we already know that octet matches + for ($i = 1; $i < 4; $i++) { + if (strpos($subnetData, $octet[$i]) !== $i * 4) { + break; + } + } + + $host = explode("/", $subnetData); + $binaryHost = ""; + $tmp = explode(".", $host[0]); + for ($i = 0; $i < 4 ; $i++) { + $binaryHost .= str_pad(decbin($tmp[$i]), 8, "0", STR_PAD_LEFT); + } + + $segmentData = array( + 'network' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 0)), + 'broadcast' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 1)) + ); + + for ($j = $i; $j < 4; $j++) { + if ((int)$octet[$j] < $segmentData['network'][$j] || + (int)$octet[$j] > $segmentData['broadcast'][$j]) { + return false; + } + } + } + + return true; + } else { + return false; + } + } + + /** + * Converts a binary string to an IP address + * + * @param string $binary + * @return mixed + */ + private function _toIp($binary) + { + $ip = array(); + $tmp = explode(".", chunk_split($binary, 8, ".")); + for ($i = 0; $i < 4 ; $i++) { + $ip[$i] = bindec($tmp[$i]); + } + + return $ip; + } + + /** + * Internal method to validate the local part of the email address + * + * @return boolean + */ + private function _validateLocalPart() + { + // First try to match the local part on the common dot-atom format + $result = false; + + // Dot-atom characters are: 1*atext *("." 1*atext) + // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*", + // "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~" + $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; + if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) { + $result = true; + } else { + // Try quoted string format + + // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE + // qtext: Non white space controls, and the rest of the US-ASCII characters not + // including "\" or the quote character + $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f'; + $qtext = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e'; + $ws = '\x20\x09'; + if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) { + $result = true; + } else { + $this->_error(self::DOT_ATOM); + $this->_error(self::QUOTED_STRING); + $this->_error(self::INVALID_LOCAL_PART); + } + } + + return $result; + } + + /** + * Internal method to validate the servers MX records + * + * @return boolean + */ + private function _validateMXRecords() + { + $mxHosts = array(); + $result = getmxrr($this->_hostname, $mxHosts); + if (!$result) { + $this->_error(self::INVALID_MX_RECORD); + } else if ($this->_options['deep'] && function_exists('checkdnsrr')) { + $validAddress = false; + $reserved = true; + foreach ($mxHosts as $hostname) { + $res = $this->_isReserved($hostname); + if (!$res) { + $reserved = false; + } + + if (!$res + && (checkdnsrr($hostname, "A") + || checkdnsrr($hostname, "AAAA") + || checkdnsrr($hostname, "A6"))) { + $validAddress = true; + break; + } + } + + if (!$validAddress) { + $result = false; + if ($reserved) { + $this->_error(self::INVALID_SEGMENT); + } else { + $this->_error(self::INVALID_MX_RECORD); + } + } + } + + return $result; + } + + /** + * Internal method to validate the hostname part of the email address + * + * @return boolean + */ + private function _validateHostnamePart() + { + $hostname = $this->_options['hostname']->setTranslator($this->getTranslator()) + ->isValid($this->_hostname); + if (!$hostname) { + $this->_error(self::INVALID_HOSTNAME); + + // Get messages and errors from hostnameValidator + foreach ($this->_options['hostname']->getMessages() as $code => $message) { + $this->_messages[$code] = $message; + } + + foreach ($this->_options['hostname']->getErrors() as $error) { + $this->_errors[] = $error; + } + } else if ($this->_options['mx']) { + // MX check on hostname + $hostname = $this->_validateMXRecords(); + } + + return $hostname; } /** @@ -176,9 +522,8 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract return false; } - $matches = array(); - $length = true; - + $matches = array(); + $length = true; $this->_setValue($value); // Split email address up and disallow '..' @@ -197,68 +542,19 @@ class Zend_Validate_EmailAddress extends Zend_Validate_Abstract } // Match hostname part - $hostnameResult = $this->hostnameValidator->setTranslator($this->getTranslator()) - ->isValid($this->_hostname); - if (!$hostnameResult) { - $this->_error(self::INVALID_HOSTNAME); - - // Get messages and errors from hostnameValidator - foreach ($this->hostnameValidator->getMessages() as $code => $message) { - $this->_messages[$code] = $message; - } - foreach ($this->hostnameValidator->getErrors() as $error) { - $this->_errors[] = $error; - } - } else if ($this->_validateMx) { - // MX check on hostname via dns_get_record() - if ($this->validateMxSupported()) { - $result = dns_get_mx($this->_hostname, $mxHosts); - if (count($mxHosts) < 1) { - $hostnameResult = false; - $this->_error(self::INVALID_MX_RECORD); - } - } else { - /** - * MX checks are not supported by this system - * @see Zend_Validate_Exception - */ - require_once 'Zend/Validate/Exception.php'; - throw new Zend_Validate_Exception('Internal error: MX checking not available on this system'); - } + if ($this->_options['domain']) { + $hostname = $this->_validateHostnamePart(); } - // First try to match the local part on the common dot-atom format - $localResult = false; - - // Dot-atom characters are: 1*atext *("." 1*atext) - // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*", - // "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~" - $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; - if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) { - $localResult = true; - } else { - // Try quoted string format + $local = $this->_validateLocalPart(); - // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE - // qtext: Non white space controls, and the rest of the US-ASCII characters not - // including "\" or the quote character - $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f'; - $qtext = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e'; - $ws = '\x20\x09'; - if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) { - $localResult = true; - } else { - $this->_error(self::DOT_ATOM); - $this->_error(self::QUOTED_STRING); - $this->_error(self::INVALID_LOCAL_PART); + // If both parts valid, return true + if ($local && $length) { + if (($this->_options['domain'] && $hostname) || !$this->_options['domain']) { + return true; } } - // If both parts valid, return true - if ($localResult && $hostnameResult && $length) { - return true; - } else { - return false; - } + return false; } } |