|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00025 require_once 'Zend/Validate/Abstract.php'; 00026 00030 require_once 'Zend/Validate/Hostname.php'; 00031 00038 class Zend_Validate_EmailAddress extends Zend_Validate_Abstract 00039 { 00040 const INVALID = 'emailAddressInvalid'; 00041 const INVALID_FORMAT = 'emailAddressInvalidFormat'; 00042 const INVALID_HOSTNAME = 'emailAddressInvalidHostname'; 00043 const INVALID_MX_RECORD = 'emailAddressInvalidMxRecord'; 00044 const INVALID_SEGMENT = 'emailAddressInvalidSegment'; 00045 const DOT_ATOM = 'emailAddressDotAtom'; 00046 const QUOTED_STRING = 'emailAddressQuotedString'; 00047 const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart'; 00048 const LENGTH_EXCEEDED = 'emailAddressLengthExceeded'; 00049 00053 protected $_messageTemplates = array( 00054 self::INVALID => "Invalid type given, value should be a string", 00055 self::INVALID_FORMAT => "'%value%' is no valid email address in the basic format local-part@hostname", 00056 self::INVALID_HOSTNAME => "'%hostname%' is no valid hostname for email address '%value%'", 00057 self::INVALID_MX_RECORD => "'%hostname%' does not appear to have a valid MX record for the email address '%value%'", 00058 self::INVALID_SEGMENT => "'%hostname%' is not in a routable network segment. The email address '%value%' should not be resolved from public network.", 00059 self::DOT_ATOM => "'%localPart%' can not be matched against dot-atom format", 00060 self::QUOTED_STRING => "'%localPart%' can not be matched against quoted-string format", 00061 self::INVALID_LOCAL_PART => "'%localPart%' is no valid local part for email address '%value%'", 00062 self::LENGTH_EXCEEDED => "'%value%' exceeds the allowed length", 00063 ); 00064 00069 protected $_invalidIp = array( 00070 '0' => '0.0.0.0/8', 00071 '10' => '10.0.0.0/8', 00072 '127' => '127.0.0.0/8', 00073 '128' => '128.0.0.0/16', 00074 '169' => '169.254.0.0/16', 00075 '172' => '172.16.0.0/12', 00076 '191' => '191.255.0.0/16', 00077 '192' => array( 00078 '192.0.0.0/24', 00079 '192.0.2.0/24', 00080 '192.88.99.0/24', 00081 '192.168.0.0/16' 00082 ), 00083 '198' => '198.18.0.0/15', 00084 '223' => '223.255.255.0/24', 00085 '224' => '224.0.0.0/4', 00086 '240' => '240.0.0.0/4' 00087 ); 00088 00092 protected $_messageVariables = array( 00093 'hostname' => '_hostname', 00094 'localPart' => '_localPart' 00095 ); 00096 00100 protected $_hostname; 00101 00105 protected $_localPart; 00106 00110 protected $_options = array( 00111 'mx' => false, 00112 'deep' => false, 00113 'domain' => true, 00114 'allow' => Zend_Validate_Hostname::ALLOW_DNS, 00115 'hostname' => null 00116 ); 00117 00130 public function __construct($options = array()) 00131 { 00132 if ($options instanceof Zend_Config) { 00133 $options = $options->toArray(); 00134 } else if (!is_array($options)) { 00135 $options = func_get_args(); 00136 $temp['allow'] = array_shift($options); 00137 if (!empty($options)) { 00138 $temp['mx'] = array_shift($options); 00139 } 00140 00141 if (!empty($options)) { 00142 $temp['hostname'] = array_shift($options); 00143 } 00144 00145 $options = $temp; 00146 } 00147 00148 $options += $this->_options; 00149 $this->setOptions($options); 00150 } 00151 00157 public function getOptions() 00158 { 00159 return $this->_options; 00160 } 00161 00168 public function setOptions(array $options = array()) 00169 { 00170 if (array_key_exists('messages', $options)) { 00171 $this->setMessages($options['messages']); 00172 } 00173 00174 if (array_key_exists('hostname', $options)) { 00175 if (array_key_exists('allow', $options)) { 00176 $this->setHostnameValidator($options['hostname'], $options['allow']); 00177 } else { 00178 $this->setHostnameValidator($options['hostname']); 00179 } 00180 } 00181 00182 if (array_key_exists('mx', $options)) { 00183 $this->setValidateMx($options['mx']); 00184 } 00185 00186 if (array_key_exists('deep', $options)) { 00187 $this->setDeepMxCheck($options['deep']); 00188 } 00189 00190 if (array_key_exists('domain', $options)) { 00191 $this->setDomainCheck($options['domain']); 00192 } 00193 00194 return $this; 00195 } 00196 00206 public function setMessage($messageString, $messageKey = null) 00207 { 00208 $messageKeys = $messageKey; 00209 if ($messageKey === null) { 00210 $keys = array_keys($this->_messageTemplates); 00211 $messageKeys = current($keys); 00212 } 00213 00214 if (!isset($this->_messageTemplates[$messageKeys])) { 00215 $this->_options['hostname']->setMessage($messageString, $messageKey); 00216 } 00217 00218 $this->_messageTemplates[$messageKeys] = $messageString; 00219 return $this; 00220 } 00221 00227 public function getHostnameValidator() 00228 { 00229 return $this->_options['hostname']; 00230 } 00231 00237 public function setHostnameValidator(Zend_Validate_Hostname $hostnameValidator = null, $allow = Zend_Validate_Hostname::ALLOW_DNS) 00238 { 00239 if (!$hostnameValidator) { 00240 $hostnameValidator = new Zend_Validate_Hostname($allow); 00241 } 00242 00243 $this->_options['hostname'] = $hostnameValidator; 00244 $this->_options['allow'] = $allow; 00245 return $this; 00246 } 00247 00255 public function validateMxSupported() 00256 { 00257 return function_exists('getmxrr'); 00258 } 00259 00265 public function getValidateMx() 00266 { 00267 return $this->_options['mx']; 00268 } 00269 00278 public function setValidateMx($mx) 00279 { 00280 if ((bool) $mx && !$this->validateMxSupported()) { 00281 require_once 'Zend/Validate/Exception.php'; 00282 throw new Zend_Validate_Exception('MX checking not available on this system'); 00283 } 00284 00285 $this->_options['mx'] = (bool) $mx; 00286 return $this; 00287 } 00288 00294 public function getDeepMxCheck() 00295 { 00296 return $this->_options['deep']; 00297 } 00298 00305 public function setDeepMxCheck($deep) 00306 { 00307 $this->_options['deep'] = (bool) $deep; 00308 return $this; 00309 } 00310 00316 public function getDomainCheck() 00317 { 00318 return $this->_options['domain']; 00319 } 00320 00328 public function setDomainCheck($domain = true) 00329 { 00330 $this->_options['domain'] = (boolean) $domain; 00331 return $this; 00332 } 00333 00340 private function _isReserved($host){ 00341 if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) { 00342 $host = gethostbyname($host); 00343 } 00344 00345 $octet = explode('.',$host); 00346 if ((int)$octet[0] >= 224) { 00347 return true; 00348 } else if (array_key_exists($octet[0], $this->_invalidIp)) { 00349 foreach ((array)$this->_invalidIp[$octet[0]] as $subnetData) { 00350 // we skip the first loop as we already know that octet matches 00351 for ($i = 1; $i < 4; $i++) { 00352 if (strpos($subnetData, $octet[$i]) !== $i * 4) { 00353 break; 00354 } 00355 } 00356 00357 $host = explode("/", $subnetData); 00358 $binaryHost = ""; 00359 $tmp = explode(".", $host[0]); 00360 for ($i = 0; $i < 4 ; $i++) { 00361 $binaryHost .= str_pad(decbin($tmp[$i]), 8, "0", STR_PAD_LEFT); 00362 } 00363 00364 $segmentData = array( 00365 'network' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 0)), 00366 'broadcast' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 1)) 00367 ); 00368 00369 for ($j = $i; $j < 4; $j++) { 00370 if ((int)$octet[$j] < $segmentData['network'][$j] || 00371 (int)$octet[$j] > $segmentData['broadcast'][$j]) { 00372 return false; 00373 } 00374 } 00375 } 00376 00377 return true; 00378 } else { 00379 return false; 00380 } 00381 } 00382 00389 private function _toIp($binary) 00390 { 00391 $ip = array(); 00392 $tmp = explode(".", chunk_split($binary, 8, ".")); 00393 for ($i = 0; $i < 4 ; $i++) { 00394 $ip[$i] = bindec($tmp[$i]); 00395 } 00396 00397 return $ip; 00398 } 00399 00405 private function _validateLocalPart() 00406 { 00407 // First try to match the local part on the common dot-atom format 00408 $result = false; 00409 00410 // Dot-atom characters are: 1*atext *("." 1*atext) 00411 // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*", 00412 // "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~" 00413 $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; 00414 if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) { 00415 $result = true; 00416 } else { 00417 // Try quoted string format 00418 00419 // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE 00420 // qtext: Non white space controls, and the rest of the US-ASCII characters not 00421 // including "\" or the quote character 00422 $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f'; 00423 $qtext = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e'; 00424 $ws = '\x20\x09'; 00425 if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) { 00426 $result = true; 00427 } else { 00428 $this->_error(self::DOT_ATOM); 00429 $this->_error(self::QUOTED_STRING); 00430 $this->_error(self::INVALID_LOCAL_PART); 00431 } 00432 } 00433 00434 return $result; 00435 } 00436 00442 private function _validateMXRecords() 00443 { 00444 $mxHosts = array(); 00445 $result = getmxrr($this->_hostname, $mxHosts); 00446 if (!$result) { 00447 $this->_error(self::INVALID_MX_RECORD); 00448 } else if ($this->_options['deep'] && function_exists('checkdnsrr')) { 00449 $validAddress = false; 00450 $reserved = true; 00451 foreach ($mxHosts as $hostname) { 00452 $res = $this->_isReserved($hostname); 00453 if (!$res) { 00454 $reserved = false; 00455 } 00456 00457 if (!$res 00458 && (checkdnsrr($hostname, "A") 00459 || checkdnsrr($hostname, "AAAA") 00460 || checkdnsrr($hostname, "A6"))) { 00461 $validAddress = true; 00462 break; 00463 } 00464 } 00465 00466 if (!$validAddress) { 00467 $result = false; 00468 if ($reserved) { 00469 $this->_error(self::INVALID_SEGMENT); 00470 } else { 00471 $this->_error(self::INVALID_MX_RECORD); 00472 } 00473 } 00474 } 00475 00476 return $result; 00477 } 00478 00484 private function _validateHostnamePart() 00485 { 00486 $hostname = $this->_options['hostname']->setTranslator($this->getTranslator()) 00487 ->isValid($this->_hostname); 00488 if (!$hostname) { 00489 $this->_error(self::INVALID_HOSTNAME); 00490 00491 // Get messages and errors from hostnameValidator 00492 foreach ($this->_options['hostname']->getMessages() as $code => $message) { 00493 $this->_messages[$code] = $message; 00494 } 00495 00496 foreach ($this->_options['hostname']->getErrors() as $error) { 00497 $this->_errors[] = $error; 00498 } 00499 } else if ($this->_options['mx']) { 00500 // MX check on hostname 00501 $hostname = $this->_validateMXRecords(); 00502 } 00503 00504 return $hostname; 00505 } 00506 00518 public function isValid($value) 00519 { 00520 if (!is_string($value)) { 00521 $this->_error(self::INVALID); 00522 return false; 00523 } 00524 00525 $matches = array(); 00526 $length = true; 00527 $this->_setValue($value); 00528 00529 // Split email address up and disallow '..' 00530 if ((strpos($value, '..') !== false) or 00531 (!preg_match('/^(.+)@([^@]+)$/', $value, $matches))) { 00532 $this->_error(self::INVALID_FORMAT); 00533 return false; 00534 } 00535 00536 $this->_localPart = $matches[1]; 00537 $this->_hostname = $matches[2]; 00538 00539 if ((strlen($this->_localPart) > 64) || (strlen($this->_hostname) > 255)) { 00540 $length = false; 00541 $this->_error(self::LENGTH_EXCEEDED); 00542 } 00543 00544 // Match hostname part 00545 if ($this->_options['domain']) { 00546 $hostname = $this->_validateHostnamePart(); 00547 } 00548 00549 $local = $this->_validateLocalPart(); 00550 00551 // If both parts valid, return true 00552 if ($local && $length) { 00553 if (($this->_options['domain'] && $hostname) || !$this->_options['domain']) { 00554 return true; 00555 } 00556 } 00557 00558 return false; 00559 } 00560 }