|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /** 00003 * RFC 822 Email address list validation Utility 00004 * 00005 * PHP versions 4 and 5 00006 * 00007 * LICENSE: 00008 * 00009 * Copyright (c) 2001-2010, Richard Heyes 00010 * All rights reserved. 00011 * 00012 * Redistribution and use in source and binary forms, with or without 00013 * modification, are permitted provided that the following conditions 00014 * are met: 00015 * 00016 * o Redistributions of source code must retain the above copyright 00017 * notice, this list of conditions and the following disclaimer. 00018 * o Redistributions in binary form must reproduce the above copyright 00019 * notice, this list of conditions and the following disclaimer in the 00020 * documentation and/or other materials provided with the distribution. 00021 * o The names of the authors may not be used to endorse or promote 00022 * products derived from this software without specific prior written 00023 * permission. 00024 * 00025 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00026 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00027 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00028 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00029 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00030 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00031 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00032 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00033 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00034 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00035 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 * 00037 * @category Mail 00038 * @package Mail 00039 * @author Richard Heyes <richard@phpguru.org> 00040 * @author Chuck Hagenbuch <chuck@horde.org 00041 * @copyright 2001-2010 Richard Heyes 00042 * @license http://opensource.org/licenses/bsd-license.php New BSD License 00043 * @version CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $ 00044 * @link http://pear.php.net/package/Mail/ 00045 * 00046 * Incorporated in TYPO3 by Ernesto Baschny <ernst@cron-it.de> 00047 */ 00048 00049 /** 00050 * RFC 822 Email address list validation Utility 00051 * 00052 * What is it? 00053 * 00054 * This class will take an address string, and parse it into it's consituent 00055 * parts, be that either addresses, groups, or combinations. Nested groups 00056 * are not supported. The structure it returns is pretty straight forward, 00057 * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use 00058 * print_r() to view the structure. 00059 * 00060 * How do I use it? 00061 * 00062 * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;'; 00063 * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true) 00064 * print_r($structure); 00065 * 00066 * @author Richard Heyes <richard@phpguru.org> 00067 * @author Chuck Hagenbuch <chuck@horde.org> 00068 * @version $Revision: 294749 $ 00069 * @license BSD 00070 * @package Mail 00071 */ 00072 class t3lib_mail_Rfc822AddressesParser { 00073 00074 /** 00075 * The address being parsed by the RFC822 object. 00076 * @var string $address 00077 */ 00078 private $address = ''; 00079 00080 /** 00081 * The default domain to use for unqualified addresses. 00082 * @var string $default_domain 00083 */ 00084 private $default_domain = 'localhost'; 00085 00086 /** 00087 /** 00088 * Whether or not to validate atoms for non-ascii characters. 00089 * @var boolean $validate 00090 */ 00091 private $validate = TRUE; 00092 00093 /** 00094 * The array of raw addresses built up as we parse. 00095 * @var array $addresses 00096 */ 00097 private $addresses = array(); 00098 00099 /** 00100 * The final array of parsed address information that we build up. 00101 * @var array $structure 00102 */ 00103 private $structure = array(); 00104 00105 /** 00106 * The current error message, if any. 00107 * @var string $error 00108 */ 00109 private $error = null; 00110 00111 /** 00112 * An internal counter/pointer. 00113 * @var integer $index 00114 */ 00115 private $index = null; 00116 00117 /** 00118 * The number of groups that have been found in the address list. 00119 * @var integer $num_groups 00120 * @access public 00121 */ 00122 private $num_groups = 0; 00123 00124 /** 00125 * A limit after which processing stops 00126 * @var int $limit 00127 */ 00128 private $limit = null; 00129 00130 /** 00131 * Sets up the object. 00132 * 00133 * @access public 00134 * @param string $address The address(es) to validate. 00135 * @param string $default_domain Default domain/host etc. If not supplied, will be set to localhost. 00136 * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance. 00137 */ 00138 public function __construct($address = null, $default_domain = null, $validate = null, $limit = null) { 00139 if (isset($address)) $this->address = $address; 00140 if (isset($default_domain)) $this->default_domain = $default_domain; 00141 if (isset($validate)) $this->validate = $validate; 00142 if (isset($limit)) $this->limit = $limit; 00143 } 00144 00145 /** 00146 * Starts the whole process. The address must either be set here 00147 * or when creating the object. One or the other. 00148 * 00149 * @access public 00150 * @param string $address The address(es) to validate. 00151 * @param string $default_domain Default domain/host etc. 00152 * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing. 00153 * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance. 00154 * 00155 * @return array A structured array of addresses. 00156 */ 00157 public function parseAddressList($address = null, $default_domain = null, $validate = null, $limit = null) { 00158 if (isset($address)) $this->address = $address; 00159 if (isset($default_domain)) $this->default_domain = $default_domain; 00160 if (isset($validate)) $this->validate = $validate; 00161 if (isset($limit)) $this->limit = $limit; 00162 00163 $this->structure = array(); 00164 $this->addresses = array(); 00165 $this->error = null; 00166 $this->index = null; 00167 00168 // Unfold any long lines in $this->address. 00169 $this->address = preg_replace('/\r?\n/', "\r\n", $this->address); 00170 $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address); 00171 00172 while ($this->address = $this->_splitAddresses($this->address)); 00173 00174 if ($this->address === false || isset($this->error)) { 00175 throw new Exception($this->error, 1294681466); 00176 } 00177 00178 // Validate each address individually. If we encounter an invalid 00179 // address, stop iterating and return an error immediately. 00180 foreach ($this->addresses as $address) { 00181 $valid = $this->_validateAddress($address); 00182 00183 if ($valid === false || isset($this->error)) { 00184 throw new Exception($this->error, 1294681467); 00185 } 00186 00187 $this->structure = array_merge($this->structure, $valid); 00188 } 00189 00190 return $this->structure; 00191 } 00192 00193 /** 00194 * Splits an address into separate addresses. 00195 * 00196 * @access private 00197 * @param string $address The addresses to split. 00198 * @return boolean Success or failure. 00199 */ 00200 protected function _splitAddresses($address) { 00201 if (!empty($this->limit) && count($this->addresses) == $this->limit) { 00202 return ''; 00203 } 00204 00205 if ($this->_isGroup($address) && !isset($this->error)) { 00206 $split_char = ';'; 00207 $is_group = true; 00208 } elseif (!isset($this->error)) { 00209 $split_char = ','; 00210 $is_group = false; 00211 } elseif (isset($this->error)) { 00212 return false; 00213 } 00214 00215 // Split the string based on the above ten or so lines. 00216 $parts = explode($split_char, $address); 00217 $string = $this->_splitCheck($parts, $split_char); 00218 00219 // If a group... 00220 if ($is_group) { 00221 // If $string does not contain a colon outside of 00222 // brackets/quotes etc then something's fubar. 00223 00224 // First check there's a colon at all: 00225 if (strpos($string, ':') === false) { 00226 $this->error = 'Invalid address: ' . $string; 00227 return false; 00228 } 00229 00230 // Now check it's outside of brackets/quotes: 00231 if (!$this->_splitCheck(explode(':', $string), ':')) { 00232 return false; 00233 } 00234 00235 // We must have a group at this point, so increase the counter: 00236 $this->num_groups++; 00237 } 00238 00239 // $string now contains the first full address/group. 00240 // Add to the addresses array. 00241 $this->addresses[] = array( 00242 'address' => trim($string), 00243 'group' => $is_group 00244 ); 00245 00246 // Remove the now stored address from the initial line, the +1 00247 // is to account for the explode character. 00248 $address = trim(substr($address, strlen($string) + 1)); 00249 00250 // If the next char is a comma and this was a group, then 00251 // there are more addresses, otherwise, if there are any more 00252 // chars, then there is another address. 00253 if ($is_group && substr($address, 0, 1) == ',') { 00254 $address = trim(substr($address, 1)); 00255 return $address; 00256 00257 } elseif (strlen($address) > 0) { 00258 return $address; 00259 00260 } else { 00261 return ''; 00262 } 00263 } 00264 00265 /** 00266 * Checks for a group at the start of the string. 00267 * 00268 * @access private 00269 * @param string $address The address to check. 00270 * @return boolean Whether or not there is a group at the start of the string. 00271 */ 00272 protected function _isGroup($address) { 00273 // First comma not in quotes, angles or escaped: 00274 $parts = explode(',', $address); 00275 $string = $this->_splitCheck($parts, ','); 00276 00277 // Now we have the first address, we can reliably check for a 00278 // group by searching for a colon that's not escaped or in 00279 // quotes or angle brackets. 00280 if (count($parts = explode(':', $string)) > 1) { 00281 $string2 = $this->_splitCheck($parts, ':'); 00282 return ($string2 !== $string); 00283 } else { 00284 return false; 00285 } 00286 } 00287 00288 /** 00289 * A common function that will check an exploded string. 00290 * 00291 * @access private 00292 * @param array $parts The exloded string. 00293 * @param string $char The char that was exploded on. 00294 * @return mixed False if the string contains unclosed quotes/brackets, or the string on success. 00295 */ 00296 protected function _splitCheck($parts, $char) { 00297 $string = $parts[0]; 00298 00299 for ($i = 0; $i < count($parts); $i++) { 00300 if ($this->_hasUnclosedQuotes($string) 00301 || $this->_hasUnclosedBrackets($string, '<>') 00302 || $this->_hasUnclosedBrackets($string, '[]') 00303 || $this->_hasUnclosedBrackets($string, '()') 00304 || substr($string, -1) == '\\') { 00305 if (isset($parts[$i + 1])) { 00306 $string = $string . $char . $parts[$i + 1]; 00307 } else { 00308 $this->error = 'Invalid address spec. Unclosed bracket or quotes'; 00309 return false; 00310 } 00311 } else { 00312 $this->index = $i; 00313 break; 00314 } 00315 } 00316 00317 return $string; 00318 } 00319 00320 /** 00321 * Checks if a string has unclosed quotes or not. 00322 * 00323 * @access private 00324 * @param string $string The string to check. 00325 * @return boolean True if there are unclosed quotes inside the string, 00326 * false otherwise. 00327 */ 00328 protected function _hasUnclosedQuotes($string) { 00329 $string = trim($string); 00330 $iMax = strlen($string); 00331 $in_quote = false; 00332 $i = $slashes = 0; 00333 00334 for (; $i < $iMax; ++$i) { 00335 switch ($string[$i]) { 00336 case '\\': 00337 ++$slashes; 00338 break; 00339 00340 case '"': 00341 if ($slashes % 2 == 0) { 00342 $in_quote = !$in_quote; 00343 } 00344 // Fall through to default action below. 00345 00346 default: 00347 $slashes = 0; 00348 break; 00349 } 00350 } 00351 00352 return $in_quote; 00353 } 00354 00355 /** 00356 * Checks if a string has an unclosed brackets or not. IMPORTANT: 00357 * This function handles both angle brackets and square brackets; 00358 * 00359 * @access private 00360 * @param string $string The string to check. 00361 * @param string $chars The characters to check for. 00362 * @return boolean True if there are unclosed brackets inside the string, false otherwise. 00363 */ 00364 protected function _hasUnclosedBrackets($string, $chars) { 00365 $num_angle_start = substr_count($string, $chars[0]); 00366 $num_angle_end = substr_count($string, $chars[1]); 00367 00368 $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]); 00369 $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]); 00370 00371 if ($num_angle_start < $num_angle_end) { 00372 $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')'; 00373 return false; 00374 } else { 00375 return ($num_angle_start > $num_angle_end); 00376 } 00377 } 00378 00379 /** 00380 * Sub function that is used only by hasUnclosedBrackets(). 00381 * 00382 * @access private 00383 * @param string $string The string to check. 00384 * @param integer &$num The number of occurences. 00385 * @param string $char The character to count. 00386 * @return integer The number of occurences of $char in $string, adjusted for backslashes. 00387 */ 00388 protected function _hasUnclosedBracketsSub($string, &$num, $char) { 00389 $parts = explode($char, $string); 00390 for ($i = 0; $i < count($parts); $i++) { 00391 if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i])) 00392 $num--; 00393 if (isset($parts[$i + 1])) 00394 $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1]; 00395 } 00396 00397 return $num; 00398 } 00399 00400 /** 00401 * Function to begin checking the address. 00402 * 00403 * @access private 00404 * @param string $address The address to validate. 00405 * @return mixed False on failure, or a structured array of address information on success. 00406 */ 00407 protected function _validateAddress($address) { 00408 $is_group = false; 00409 $addresses = array(); 00410 00411 if ($address['group']) { 00412 $is_group = true; 00413 00414 // Get the group part of the name 00415 $parts = explode(':', $address['address']); 00416 $groupname = $this->_splitCheck($parts, ':'); 00417 $structure = array(); 00418 00419 // And validate the group part of the name. 00420 if (!$this->_validatePhrase($groupname)) { 00421 $this->error = 'Group name did not validate.'; 00422 return false; 00423 } 00424 00425 $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':'))); 00426 } 00427 00428 // If a group then split on comma and put into an array. 00429 // Otherwise, Just put the whole address in an array. 00430 if ($is_group) { 00431 while (strlen($address['address']) > 0) { 00432 $parts = explode(',', $address['address']); 00433 $addresses[] = $this->_splitCheck($parts, ','); 00434 $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ','))); 00435 } 00436 } else { 00437 $addresses[] = $address['address']; 00438 } 00439 00440 // Check that $addresses is set, if address like this: 00441 // Groupname:; 00442 // Then errors were appearing. 00443 if (!count($addresses)) { 00444 $this->error = 'Empty group.'; 00445 return false; 00446 } 00447 00448 // Trim the whitespace from all of the address strings. 00449 array_map('trim', $addresses); 00450 00451 // Validate each mailbox. 00452 // Format could be one of: name <geezer@domain.com> 00453 // geezer@domain.com 00454 // geezer 00455 // ... or any other format valid by RFC 822. 00456 for ($i = 0; $i < count($addresses); $i++) { 00457 if (!$this->validateMailbox($addresses[$i])) { 00458 if (empty($this->error)) { 00459 $this->error = 'Validation failed for: ' . $addresses[$i]; 00460 } 00461 return false; 00462 } 00463 } 00464 00465 if ($is_group) { 00466 $structure = array_merge($structure, $addresses); 00467 } else { 00468 $structure = $addresses; 00469 } 00470 00471 return $structure; 00472 } 00473 00474 /** 00475 * Function to validate a phrase. 00476 * 00477 * @access private 00478 * @param string $phrase The phrase to check. 00479 * @return boolean Success or failure. 00480 */ 00481 protected function _validatePhrase($phrase) { 00482 // Splits on one or more Tab or space. 00483 $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY); 00484 00485 $phrase_parts = array(); 00486 while (count($parts) > 0) { 00487 $phrase_parts[] = $this->_splitCheck($parts, ' '); 00488 for ($i = 0; $i < $this->index + 1; $i++) 00489 array_shift($parts); 00490 } 00491 00492 foreach ($phrase_parts as $part) { 00493 // If quoted string: 00494 if (substr($part, 0, 1) == '"') { 00495 if (!$this->_validateQuotedString($part)) { 00496 return false; 00497 } 00498 continue; 00499 } 00500 00501 // Otherwise it's an atom: 00502 if (!$this->_validateAtom($part)) return false; 00503 } 00504 00505 return true; 00506 } 00507 00508 /** 00509 * Function to validate an atom which from rfc822 is: 00510 * atom = 1*<any CHAR except specials, SPACE and CTLs> 00511 * 00512 * If validation ($this->validate) has been turned off, then 00513 * validateAtom() doesn't actually check anything. This is so that you 00514 * can split a list of addresses up before encoding personal names 00515 * (umlauts, etc.), for example. 00516 * 00517 * @access private 00518 * @param string $atom The string to check. 00519 * @return boolean Success or failure. 00520 */ 00521 protected function _validateAtom($atom) { 00522 if (!$this->validate) { 00523 // Validation has been turned off; assume the atom is okay. 00524 return true; 00525 } 00526 00527 // Check for any char from ASCII 0 - ASCII 127 00528 if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) { 00529 return false; 00530 } 00531 00532 // Check for specials: 00533 if (preg_match('/[][()<>@,;\\:". ]/', $atom)) { 00534 return false; 00535 } 00536 00537 // Check for control characters (ASCII 0-31): 00538 if (preg_match('/[\\x00-\\x1F]+/', $atom)) { 00539 return false; 00540 } 00541 00542 return true; 00543 } 00544 00545 /** 00546 * Function to validate quoted string, which is: 00547 * quoted-string = <"> *(qtext/quoted-pair) <"> 00548 * 00549 * @access private 00550 * @param string $qstring The string to check 00551 * @return boolean Success or failure. 00552 */ 00553 protected function _validateQuotedString($qstring) { 00554 // Leading and trailing " 00555 $qstring = substr($qstring, 1, -1); 00556 00557 // Perform check, removing quoted characters first. 00558 return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring)); 00559 } 00560 00561 /** 00562 * Function to validate a mailbox, which is: 00563 * mailbox = addr-spec ; simple address 00564 * / phrase route-addr ; name and route-addr 00565 * 00566 * @access public 00567 * @param string &$mailbox The string to check. 00568 * @return boolean Success or failure. 00569 */ 00570 protected function validateMailbox(&$mailbox) { 00571 // A couple of defaults. 00572 $phrase = ''; 00573 $comment = ''; 00574 $comments = array(); 00575 00576 // Catch any RFC822 comments and store them separately. 00577 $_mailbox = $mailbox; 00578 while (strlen(trim($_mailbox)) > 0) { 00579 $parts = explode('(', $_mailbox); 00580 $before_comment = $this->_splitCheck($parts, '('); 00581 if ($before_comment != $_mailbox) { 00582 // First char should be a (. 00583 $comment = substr(str_replace($before_comment, '', $_mailbox), 1); 00584 $parts = explode(')', $comment); 00585 $comment = $this->_splitCheck($parts, ')'); 00586 $comments[] = $comment; 00587 00588 // +2 is for the brackets 00589 $_mailbox = substr($_mailbox, strpos($_mailbox, '(' . $comment) + strlen($comment) + 2); 00590 } else { 00591 break; 00592 } 00593 } 00594 00595 foreach ($comments as $comment) { 00596 $mailbox = str_replace("($comment)", '', $mailbox); 00597 } 00598 00599 $mailbox = trim($mailbox); 00600 00601 // Check for name + route-addr 00602 if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') { 00603 $parts = explode('<', $mailbox); 00604 $name = $this->_splitCheck($parts, '<'); 00605 00606 $phrase = trim($name); 00607 $route_addr = trim(substr($mailbox, strlen($name . '<'), -1)); 00608 00609 if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) { 00610 return false; 00611 } 00612 00613 // Only got addr-spec 00614 } else { 00615 // First snip angle brackets if present. 00616 if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') { 00617 $addr_spec = substr($mailbox, 1, -1); 00618 } else { 00619 $addr_spec = $mailbox; 00620 } 00621 00622 if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { 00623 return false; 00624 } 00625 } 00626 00627 // Construct the object that will be returned. 00628 $mbox = new stdClass(); 00629 00630 // Add the phrase (even if empty) and comments 00631 $mbox->personal = $phrase; 00632 $mbox->comment = isset($comments) ? $comments : array(); 00633 00634 if (isset($route_addr)) { 00635 $mbox->mailbox = $route_addr['local_part']; 00636 $mbox->host = $route_addr['domain']; 00637 $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : ''; 00638 } else { 00639 $mbox->mailbox = $addr_spec['local_part']; 00640 $mbox->host = $addr_spec['domain']; 00641 } 00642 00643 $mailbox = $mbox; 00644 return true; 00645 } 00646 00647 /** 00648 * This function validates a route-addr which is: 00649 * route-addr = "<" [route] addr-spec ">" 00650 * 00651 * Angle brackets have already been removed at the point of 00652 * getting to this function. 00653 * 00654 * @access private 00655 * @param string $route_addr The string to check. 00656 * @return mixed False on failure, or an array containing validated address/route information on success. 00657 */ 00658 protected function _validateRouteAddr($route_addr) { 00659 // Check for colon. 00660 if (strpos($route_addr, ':') !== false) { 00661 $parts = explode(':', $route_addr); 00662 $route = $this->_splitCheck($parts, ':'); 00663 } else { 00664 $route = $route_addr; 00665 } 00666 00667 // If $route is same as $route_addr then the colon was in 00668 // quotes or brackets or, of course, non existent. 00669 if ($route === $route_addr) { 00670 unset($route); 00671 $addr_spec = $route_addr; 00672 if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { 00673 return false; 00674 } 00675 } else { 00676 // Validate route part. 00677 if (($route = $this->_validateRoute($route)) === false) { 00678 return false; 00679 } 00680 00681 $addr_spec = substr($route_addr, strlen($route . ':')); 00682 00683 // Validate addr-spec part. 00684 if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { 00685 return false; 00686 } 00687 } 00688 00689 if (isset($route)) { 00690 $return['adl'] = $route; 00691 } else { 00692 $return['adl'] = ''; 00693 } 00694 00695 $return = array_merge($return, $addr_spec); 00696 return $return; 00697 } 00698 00699 /** 00700 * Function to validate a route, which is: 00701 * route = 1#("@" domain) ":" 00702 * 00703 * @access private 00704 * @param string $route The string to check. 00705 * @return mixed False on failure, or the validated $route on success. 00706 */ 00707 protected function _validateRoute($route) { 00708 // Split on comma. 00709 $domains = explode(',', trim($route)); 00710 00711 foreach ($domains as $domain) { 00712 $domain = str_replace('@', '', trim($domain)); 00713 if (!$this->_validateDomain($domain)) return false; 00714 } 00715 00716 return $route; 00717 } 00718 00719 /** 00720 * Function to validate a domain, though this is not quite what 00721 * you expect of a strict internet domain. 00722 * 00723 * domain = sub-domain *("." sub-domain) 00724 * 00725 * @access private 00726 * @param string $domain The string to check. 00727 * @return mixed False on failure, or the validated domain on success. 00728 */ 00729 protected function _validateDomain($domain) { 00730 // Note the different use of $subdomains and $sub_domains 00731 $subdomains = explode('.', $domain); 00732 00733 while (count($subdomains) > 0) { 00734 $sub_domains[] = $this->_splitCheck($subdomains, '.'); 00735 for ($i = 0; $i < $this->index + 1; $i++) 00736 array_shift($subdomains); 00737 } 00738 00739 foreach ($sub_domains as $sub_domain) { 00740 if (!$this->_validateSubdomain(trim($sub_domain))) 00741 return false; 00742 } 00743 00744 // Managed to get here, so return input. 00745 return $domain; 00746 } 00747 00748 /** 00749 * Function to validate a subdomain: 00750 * subdomain = domain-ref / domain-literal 00751 * 00752 * @access private 00753 * @param string $subdomain The string to check. 00754 * @return boolean Success or failure. 00755 */ 00756 protected function _validateSubdomain($subdomain) { 00757 if (preg_match('|^\[(.*)]$|', $subdomain, $arr)) { 00758 if (!$this->_validateDliteral($arr[1])) return false; 00759 } else { 00760 if (!$this->_validateAtom($subdomain)) return false; 00761 } 00762 00763 // Got here, so return successful. 00764 return true; 00765 } 00766 00767 /** 00768 * Function to validate a domain literal: 00769 * domain-literal = "[" *(dtext / quoted-pair) "]" 00770 * 00771 * @access private 00772 * @param string $dliteral The string to check. 00773 * @return boolean Success or failure. 00774 */ 00775 protected function _validateDliteral($dliteral) { 00776 return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\'; 00777 } 00778 00779 /** 00780 * Function to validate an addr-spec. 00781 * 00782 * addr-spec = local-part "@" domain 00783 * 00784 * @access private 00785 * @param string $addr_spec The string to check. 00786 * @return mixed False on failure, or the validated addr-spec on success. 00787 */ 00788 protected function _validateAddrSpec($addr_spec) { 00789 $addr_spec = trim($addr_spec); 00790 00791 // Split on @ sign if there is one. 00792 if (strpos($addr_spec, '@') !== false) { 00793 $parts = explode('@', $addr_spec); 00794 $local_part = $this->_splitCheck($parts, '@'); 00795 $domain = substr($addr_spec, strlen($local_part . '@')); 00796 00797 // No @ sign so assume the default domain. 00798 } else { 00799 $local_part = $addr_spec; 00800 $domain = $this->default_domain; 00801 } 00802 00803 if (($local_part = $this->_validateLocalPart($local_part)) === false) return false; 00804 if (($domain = $this->_validateDomain($domain)) === false) return false; 00805 00806 // Got here so return successful. 00807 return array('local_part' => $local_part, 'domain' => $domain); 00808 } 00809 00810 /** 00811 * Function to validate the local part of an address: 00812 * local-part = word *("." word) 00813 * 00814 * @access private 00815 * @param string $local_part 00816 * @return mixed False on failure, or the validated local part on success. 00817 */ 00818 protected function _validateLocalPart($local_part) { 00819 $parts = explode('.', $local_part); 00820 $words = array(); 00821 00822 // Split the local_part into words. 00823 while (count($parts) > 0) { 00824 $words[] = $this->_splitCheck($parts, '.'); 00825 for ($i = 0; $i < $this->index + 1; $i++) { 00826 array_shift($parts); 00827 } 00828 } 00829 00830 // Validate each word. 00831 foreach ($words as $word) { 00832 // If this word contains an unquoted space, it is invalid. (6.2.4) 00833 if (strpos($word, ' ') && $word[0] !== '"') { 00834 return false; 00835 } 00836 00837 if ($this->_validatePhrase(trim($word)) === false) return false; 00838 } 00839 00840 // Managed to get here, so return the input. 00841 return $local_part; 00842 } 00843 } 00844 00845 ?>
1.8.0