[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/zend/Zend/Validate/ -> EmailAddress.php (source)

   1  <?php
   2  /**
   3   * Zend Framework
   4   *
   5   * LICENSE
   6   *
   7   * This source file is subject to the new BSD license that is bundled
   8   * with this package in the file LICENSE.txt.
   9   * It is also available through the world-wide-web at this URL:
  10   * http://framework.zend.com/license/new-bsd
  11   * If you did not receive a copy of the license and are unable to
  12   * obtain it through the world-wide-web, please send an email
  13   * to [email protected] so we can send you a copy immediately.
  14   *
  15   * @category   Zend
  16   * @package    Zend_Validate
  17   * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18   * @license    http://framework.zend.com/license/new-bsd     New BSD License
  19   * @version    $Id$
  20   */
  21  
  22  /**
  23   * @see Zend_Validate_Abstract
  24   */
  25  require_once 'Zend/Validate/Abstract.php';
  26  
  27  /**
  28   * @see Zend_Validate_Hostname
  29   */
  30  require_once 'Zend/Validate/Hostname.php';
  31  
  32  /**
  33   * @category   Zend
  34   * @package    Zend_Validate
  35   * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  36   * @license    http://framework.zend.com/license/new-bsd     New BSD License
  37   */
  38  class Zend_Validate_EmailAddress extends Zend_Validate_Abstract
  39  {
  40      const INVALID            = 'emailAddressInvalid';
  41      const INVALID_FORMAT     = 'emailAddressInvalidFormat';
  42      const INVALID_HOSTNAME   = 'emailAddressInvalidHostname';
  43      const INVALID_MX_RECORD  = 'emailAddressInvalidMxRecord';
  44      const INVALID_SEGMENT    = 'emailAddressInvalidSegment';
  45      const DOT_ATOM           = 'emailAddressDotAtom';
  46      const QUOTED_STRING      = 'emailAddressQuotedString';
  47      const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart';
  48      const LENGTH_EXCEEDED    = 'emailAddressLengthExceeded';
  49  
  50      /**
  51       * @var array
  52       */
  53      protected $_messageTemplates = array(
  54          self::INVALID            => "Invalid type given, value should be a string",
  55          self::INVALID_FORMAT     => "'%value%' is no valid email address in the basic format local-part@hostname",
  56          self::INVALID_HOSTNAME   => "'%hostname%' is no valid hostname for email address '%value%'",
  57          self::INVALID_MX_RECORD  => "'%hostname%' does not appear to have a valid MX record for the email address '%value%'",
  58          self::INVALID_SEGMENT    => "'%hostname%' is not in a routable network segment. The email address '%value%' should not be resolved from public network.",
  59          self::DOT_ATOM           => "'%localPart%' can not be matched against dot-atom format",
  60          self::QUOTED_STRING      => "'%localPart%' can not be matched against quoted-string format",
  61          self::INVALID_LOCAL_PART => "'%localPart%' is no valid local part for email address '%value%'",
  62          self::LENGTH_EXCEEDED    => "'%value%' exceeds the allowed length",
  63      );
  64  
  65      /**
  66       * @see http://en.wikipedia.org/wiki/IPv4
  67       * @var array
  68       */
  69      protected $_invalidIp = array(
  70          '0'   => '0.0.0.0/8',
  71          '10'  => '10.0.0.0/8',
  72          '127' => '127.0.0.0/8',
  73          '128' => '128.0.0.0/16',
  74          '169' => '169.254.0.0/16',
  75          '172' => '172.16.0.0/12',
  76          '191' => '191.255.0.0/16',
  77          '192' => array(
  78              '192.0.0.0/24',
  79              '192.0.2.0/24',
  80              '192.88.99.0/24',
  81              '192.168.0.0/16'
  82          ),
  83          '198' => '198.18.0.0/15',
  84          '223' => '223.255.255.0/24',
  85          '224' => '224.0.0.0/4',
  86          '240' => '240.0.0.0/4'
  87      );
  88  
  89      /**
  90       * @var array
  91       */
  92      protected $_messageVariables = array(
  93          'hostname'  => '_hostname',
  94          'localPart' => '_localPart'
  95      );
  96  
  97      /**
  98       * @var string
  99       */
 100      protected $_hostname;
 101  
 102      /**
 103       * @var string
 104       */
 105      protected $_localPart;
 106  
 107      /**
 108       * Internal options array
 109       */
 110      protected $_options = array(
 111          'mx'       => false,
 112          'deep'     => false,
 113          'domain'   => true,
 114          'allow'    => Zend_Validate_Hostname::ALLOW_DNS,
 115          'hostname' => null
 116      );
 117  
 118      /**
 119       * Instantiates hostname validator for local use
 120       *
 121       * The following option keys are supported:
 122       * 'hostname' => A hostname validator, see Zend_Validate_Hostname
 123       * 'allow'    => Options for the hostname validator, see Zend_Validate_Hostname::ALLOW_*
 124       * 'mx'       => If MX check should be enabled, boolean
 125       * 'deep'     => If a deep MX check should be done, boolean
 126       *
 127       * @param array|Zend_Config $options OPTIONAL
 128       * @return void
 129       */
 130      public function __construct($options = array())
 131      {
 132          if ($options instanceof Zend_Config) {
 133              $options = $options->toArray();
 134          } else if (!is_array($options)) {
 135              $options = func_get_args();
 136              $temp['allow'] = array_shift($options);
 137              if (!empty($options)) {
 138                  $temp['mx'] = array_shift($options);
 139              }
 140  
 141              if (!empty($options)) {
 142                  $temp['hostname'] = array_shift($options);
 143              }
 144  
 145              $options = $temp;
 146          }
 147  
 148          $options += $this->_options;
 149          $this->setOptions($options);
 150      }
 151  
 152      /**
 153       * Returns all set Options
 154       *
 155       * @return array
 156       */
 157      public function getOptions()
 158      {
 159          return $this->_options;
 160      }
 161  
 162      /**
 163       * Set options for the email validator
 164       *
 165       * @param array $options
 166       * @return Zend_Validate_EmailAddress fluid interface
 167       */
 168      public function setOptions(array $options = array())
 169      {
 170          if (array_key_exists('messages', $options)) {
 171              $this->setMessages($options['messages']);
 172          }
 173  
 174          if (array_key_exists('hostname', $options)) {
 175              if (array_key_exists('allow', $options)) {
 176                  $this->setHostnameValidator($options['hostname'], $options['allow']);
 177              } else {
 178                  $this->setHostnameValidator($options['hostname']);
 179              }
 180          }
 181  
 182          if (array_key_exists('mx', $options)) {
 183              $this->setValidateMx($options['mx']);
 184          }
 185  
 186          if (array_key_exists('deep', $options)) {
 187              $this->setDeepMxCheck($options['deep']);
 188          }
 189  
 190          if (array_key_exists('domain', $options)) {
 191              $this->setDomainCheck($options['domain']);
 192          }
 193  
 194          return $this;
 195      }
 196  
 197      /**
 198       * Sets the validation failure message template for a particular key
 199       * Adds the ability to set messages to the attached hostname validator
 200       *
 201       * @param  string $messageString
 202       * @param  string $messageKey     OPTIONAL
 203       * @return Zend_Validate_Abstract Provides a fluent interface
 204       * @throws Zend_Validate_Exception
 205       */
 206      public function setMessage($messageString, $messageKey = null)
 207      {
 208          $messageKeys = $messageKey;
 209          if ($messageKey === null) {
 210              $keys = array_keys($this->_messageTemplates);
 211              $messageKeys = current($keys);
 212          }
 213  
 214          if (!isset($this->_messageTemplates[$messageKeys])) {
 215              $this->_options['hostname']->setMessage($messageString, $messageKey);
 216          }
 217  
 218          $this->_messageTemplates[$messageKeys] = $messageString;
 219          return $this;
 220      }
 221  
 222      /**
 223       * Returns the set hostname validator
 224       *
 225       * @return Zend_Validate_Hostname
 226       */
 227      public function getHostnameValidator()
 228      {
 229          return $this->_options['hostname'];
 230      }
 231  
 232      /**
 233       * @param Zend_Validate_Hostname $hostnameValidator OPTIONAL
 234       * @param int                    $allow             OPTIONAL
 235       * @return void
 236       */
 237      public function setHostnameValidator(Zend_Validate_Hostname $hostnameValidator = null, $allow = Zend_Validate_Hostname::ALLOW_DNS)
 238      {
 239          if (!$hostnameValidator) {
 240              $hostnameValidator = new Zend_Validate_Hostname($allow);
 241          }
 242  
 243          $this->_options['hostname'] = $hostnameValidator;
 244          $this->_options['allow']    = $allow;
 245          return $this;
 246      }
 247  
 248      /**
 249       * Whether MX checking via getmxrr is supported or not
 250       *
 251       * This currently only works on UNIX systems
 252       *
 253       * @return boolean
 254       */
 255      public function validateMxSupported()
 256      {
 257          return function_exists('getmxrr');
 258      }
 259  
 260      /**
 261       * Returns the set validateMx option
 262       *
 263       * @return boolean
 264       */
 265      public function getValidateMx()
 266      {
 267          return $this->_options['mx'];
 268      }
 269  
 270      /**
 271       * Set whether we check for a valid MX record via DNS
 272       *
 273       * This only applies when DNS hostnames are validated
 274       *
 275       * @param boolean $mx Set allowed to true to validate for MX records, and false to not validate them
 276       * @return Zend_Validate_EmailAddress Fluid Interface
 277       */
 278      public function setValidateMx($mx)
 279      {
 280          if ((bool) $mx && !$this->validateMxSupported()) {
 281              require_once 'Zend/Validate/Exception.php';
 282              throw new Zend_Validate_Exception('MX checking not available on this system');
 283          }
 284  
 285          $this->_options['mx'] = (bool) $mx;
 286          return $this;
 287      }
 288  
 289      /**
 290       * Returns the set deepMxCheck option
 291       *
 292       * @return boolean
 293       */
 294      public function getDeepMxCheck()
 295      {
 296          return $this->_options['deep'];
 297      }
 298  
 299      /**
 300       * Set whether we check MX record should be a deep validation
 301       *
 302       * @param boolean $deep Set deep to true to perform a deep validation process for MX records
 303       * @return Zend_Validate_EmailAddress Fluid Interface
 304       */
 305      public function setDeepMxCheck($deep)
 306      {
 307          $this->_options['deep'] = (bool) $deep;
 308          return $this;
 309      }
 310  
 311      /**
 312       * Returns the set domainCheck option
 313       *
 314       * @return unknown
 315       */
 316      public function getDomainCheck()
 317      {
 318          return $this->_options['domain'];
 319      }
 320  
 321      /**
 322       * Sets if the domain should also be checked
 323       * or only the local part of the email address
 324       *
 325       * @param boolean $domain
 326       * @return Zend_Validate_EmailAddress Fluid Interface
 327       */
 328      public function setDomainCheck($domain = true)
 329      {
 330          $this->_options['domain'] = (boolean) $domain;
 331          return $this;
 332      }
 333  
 334      /**
 335       * Returns if the given host is reserved
 336       *
 337       * @param string $host
 338       * @return boolean
 339       */
 340      private function _isReserved($host){
 341          if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) {
 342              $host = gethostbyname($host);
 343          }
 344  
 345          $octet = explode('.',$host);
 346          if ((int)$octet[0] >= 224) {
 347              return true;
 348          } else if (array_key_exists($octet[0], $this->_invalidIp)) {
 349              foreach ((array)$this->_invalidIp[$octet[0]] as $subnetData) {
 350                  // we skip the first loop as we already know that octet matches
 351                  for ($i = 1; $i < 4; $i++) {
 352                      if (strpos($subnetData, $octet[$i]) !== $i * 4) {
 353                          break;
 354                      }
 355                  }
 356  
 357                  $host       = explode("/", $subnetData);
 358                  $binaryHost = "";
 359                  $tmp        = explode(".", $host[0]);
 360                  for ($i = 0; $i < 4 ; $i++) {
 361                      $binaryHost .= str_pad(decbin($tmp[$i]), 8, "0", STR_PAD_LEFT);
 362                  }
 363  
 364                  $segmentData = array(
 365                      'network'   => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 0)),
 366                      'broadcast' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 1))
 367                  );
 368  
 369                  for ($j = $i; $j < 4; $j++) {
 370                      if ((int)$octet[$j] < $segmentData['network'][$j] ||
 371                          (int)$octet[$j] > $segmentData['broadcast'][$j]) {
 372                          return false;
 373                      }
 374                  }
 375              }
 376  
 377              return true;
 378          } else {
 379              return false;
 380          }
 381      }
 382  
 383      /**
 384       * Converts a binary string to an IP address
 385       *
 386       * @param string $binary
 387       * @return mixed
 388       */
 389      private function _toIp($binary)
 390      {
 391          $ip  = array();
 392          $tmp = explode(".", chunk_split($binary, 8, "."));
 393          for ($i = 0; $i < 4 ; $i++) {
 394              $ip[$i] = bindec($tmp[$i]);
 395          }
 396  
 397          return $ip;
 398      }
 399  
 400      /**
 401       * Internal method to validate the local part of the email address
 402       *
 403       * @return boolean
 404       */
 405      private function _validateLocalPart()
 406      {
 407          // First try to match the local part on the common dot-atom format
 408          $result = false;
 409  
 410          // Dot-atom characters are: 1*atext *("." 1*atext)
 411          // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
 412          //        "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
 413          $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e';
 414          if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) {
 415              $result = true;
 416          } else {
 417              // Try quoted string format
 418  
 419              // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
 420              // qtext: Non white space controls, and the rest of the US-ASCII characters not
 421              //   including "\" or the quote character
 422              $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f';
 423              $qtext   = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e';
 424              $ws      = '\x20\x09';
 425              if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) {
 426                  $result = true;
 427              } else {
 428                  $this->_error(self::DOT_ATOM);
 429                  $this->_error(self::QUOTED_STRING);
 430                  $this->_error(self::INVALID_LOCAL_PART);
 431              }
 432          }
 433  
 434          return $result;
 435      }
 436  
 437      /**
 438       * Internal method to validate the servers MX records
 439       *
 440       * @return boolean
 441       */
 442      private function _validateMXRecords()
 443      {
 444          $mxHosts = array();
 445          $result = getmxrr($this->_hostname, $mxHosts);
 446          if (!$result) {
 447              $this->_error(self::INVALID_MX_RECORD);
 448          } else if ($this->_options['deep'] && function_exists('checkdnsrr')) {
 449              $validAddress = false;
 450              $reserved     = true;
 451              foreach ($mxHosts as $hostname) {
 452                  $res = $this->_isReserved($hostname);
 453                  if (!$res) {
 454                      $reserved = false;
 455                  }
 456  
 457                  if (!$res
 458                      && (checkdnsrr($hostname, "A")
 459                      || checkdnsrr($hostname, "AAAA")
 460                      || checkdnsrr($hostname, "A6"))) {
 461                      $validAddress = true;
 462                      break;
 463                  }
 464              }
 465  
 466              if (!$validAddress) {
 467                  $result = false;
 468                  if ($reserved) {
 469                      $this->_error(self::INVALID_SEGMENT);
 470                  } else {
 471                      $this->_error(self::INVALID_MX_RECORD);
 472                  }
 473              }
 474          }
 475  
 476          return $result;
 477      }
 478  
 479      /**
 480       * Internal method to validate the hostname part of the email address
 481       *
 482       * @return boolean
 483       */
 484      private function _validateHostnamePart()
 485      {
 486          $hostname = $this->_options['hostname']->setTranslator($this->getTranslator())
 487                           ->isValid($this->_hostname);
 488          if (!$hostname) {
 489              $this->_error(self::INVALID_HOSTNAME);
 490  
 491              // Get messages and errors from hostnameValidator
 492              foreach ($this->_options['hostname']->getMessages() as $code => $message) {
 493                  $this->_messages[$code] = $message;
 494              }
 495  
 496              foreach ($this->_options['hostname']->getErrors() as $error) {
 497                  $this->_errors[] = $error;
 498              }
 499          } else if ($this->_options['mx']) {
 500              // MX check on hostname
 501              $hostname = $this->_validateMXRecords();
 502          }
 503  
 504          return $hostname;
 505      }
 506  
 507      /**
 508       * Defined by Zend_Validate_Interface
 509       *
 510       * Returns true if and only if $value is a valid email address
 511       * according to RFC2822
 512       *
 513       * @link   http://www.ietf.org/rfc/rfc2822.txt RFC2822
 514       * @link   http://www.columbia.edu/kermit/ascii.html US-ASCII characters
 515       * @param  string $value
 516       * @return boolean
 517       */
 518      public function isValid($value)
 519      {
 520          if (!is_string($value)) {
 521              $this->_error(self::INVALID);
 522              return false;
 523          }
 524  
 525          $matches = array();
 526          $length  = true;
 527          $this->_setValue($value);
 528  
 529          // Split email address up and disallow '..'
 530          if ((strpos($value, '..') !== false) or
 531              (!preg_match('/^(.+)@([^@]+)$/', $value, $matches))) {
 532              $this->_error(self::INVALID_FORMAT);
 533              return false;
 534          }
 535  
 536          $this->_localPart = $matches[1];
 537          $this->_hostname  = $matches[2];
 538  
 539          if ((strlen($this->_localPart) > 64) || (strlen($this->_hostname) > 255)) {
 540              $length = false;
 541              $this->_error(self::LENGTH_EXCEEDED);
 542          }
 543  
 544          // Match hostname part
 545          if ($this->_options['domain']) {
 546              $hostname = $this->_validateHostnamePart();
 547          }
 548  
 549          $local = $this->_validateLocalPart();
 550  
 551          // If both parts valid, return true
 552          if ($local && $length) {
 553              if (($this->_options['domain'] && $hostname) || !$this->_options['domain']) {
 554                  return true;
 555              }
 556          }
 557  
 558          return false;
 559      }
 560  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1