Source of file CheckFormData.php
Size: 37,094 Bytes - Last Modified: 2021-12-23T10:21:11+00:00
/var/www/docs.ssmods.com/process/src/code/Validation/CheckFormData.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116 | <?php namespace CustomHtmlForm\Validation; use CustomHtmlForm\Dev\Tools; use CustomHtmlForm\Forms\GoogleRecaptchaField; use ReCaptcha\ReCaptcha; /** * Offers methods for form input validation. * * @package CustomHtmlForm * @subpackage Validation * @author Sebastian Diel <sdiel@pixeltricks.de> * @since 11.10.2017 * @copyright 2017 pixeltricks GmbH * @license see license file in modules root directory */ class CheckFormData { /** * value of expression to be checked * * @var mixed */ protected $value; /** * creates a new expression * * @param mixed $value value of expression to be checked */ public function __construct($value) { $this -> value = $value; } /** * Checks if input containes special chars and if the result corresponds to * the expected result * * @param boolean $expectedResult expected result * * @return array * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function hasSpecialSigns($expectedResult) { $errorMessage = ''; $match = false; preg_match( '/^[A-Za-z0-9@\.]+$/', $this->value, $matches ); if ($matches && ($matches[0] == $this->value)) { $match = true; } if ($match == $expectedResult) { $success = true; } else { $success = false; if ($match) { $errorMessage = _t(CheckFormData::class . '.HASNOSPECIALSIGNS', 'This field must contain special signs (other signs than letters, numbers and the signs "@" and ".").'); } else { $errorMessage = _t(CheckFormData::class . '.HASSPECIALSIGNS', 'This field must not contain special signs (letters, numbers and the signs "@" and ".").'); } } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * Checks, whether the given string matches basicly an email address. * The rule is: one or more chars, then '@', then two ore more chars, then * '.', then two or more chars. This matching was simplified because the * stricter version did not match some special cases. * * @param boolean $expectedResult Das erwartete Resultat. * * @return array * * @author Sascha Koehler <skoehler@pixeltricks.de>, Sebastian Diel <sdiel@pixeltricks.de> * @since 21.06.2011 */ public function isEmailAddress($expectedResult) { $errorMessage = ''; if (empty($this->value)) { $success = true; } else { $match = $this->is_valid_email_address($this->value); if ($match == $expectedResult) { $success = true; } else { $success = false; if ($match) { $errorMessage = _t(CheckFormData::class . '.MUSTNOTBEEMAILADDRESS', 'Please don\'t enter an email address.'); } else { $errorMessage = _t(CheckFormData::class . '.MUSTBEEMAILADDRESS', 'Please enter a valid email address.'); } } } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * checks captcha field input * * @param array $parameters form's and field's name * array( * 'formName' => string, * 'fieldName' => string * ) * * @return array * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function PtCaptchaInput($parameters) { $customHtmlForm = Tools::Session()->get('CustomHtmlForm'); $spamCheck = Tools::Session()->get('CustomHtmlForm.SpamCheck'); if (is_null($customHtmlForm)) { Tools::Session()->set('CustomHtmlForm', array()); } if (is_null($spamCheck)) { Tools::Session()->set('CustomHtmlForm.SpamCheck', array()); } Tools::saveSession(); $codeToMatch = Tools::Session()->get('CustomHtmlForm.SpamCheck.' . $parameters['fieldName']); $success = false; $errorMessage = ''; $checkCode = md5(strtolower($this->getValueWithoutWhitespace($this->value))); if ($checkCode === $codeToMatch) { $success = true; } else { $success = false; $errorMessage = _t(CustomHtmlForm::class . '.Title', 'Enter this captcha code in the following field please:'); } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * checks reCAPTCHA field input * * @param array $parameters form and field name * * @return array * * @author Sebastian Diel <sdiel@pixeltricks.de> * @since 21.12.2016 */ public function GoogleRecaptchaField($parameters) { $success = false; $gRecaptchaResponse = $_REQUEST['g-recaptcha-response']; $remoteIp = $_SERVER['REMOTE_ADDR']; $recaptcha = new ReCaptcha(GoogleRecaptchaField::get_recaptcha_secret()); $resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp); if ($resp->isSuccess()) { $success = true; } else { $errorMessage = _t(CustomHtmlForm::class . '.CAPTCHAFIELDNOMATCH', 'Your entry was not correct. Please try again!'); } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * Checks if a field is empty and if this result is expected * * @param boolean $expectedResult the expected result * @param string $checkValue Optional static value to check. * * @return array * * @author Sebastian Diel <sdiel@pixeltricks.de>, * Sascha Koehler <skoehler@pixeltricks.de> * @since 14.07.2014 */ public function isFilledIn($expectedResult, $checkValue = null) { $isFilledIn = true; $success = false; $errorMessage = ''; if (is_null($checkValue)) { $checkValue = $this->getValueWithoutWhitespace($this->value); } if ($checkValue === '') { $isFilledIn = false; } if ($isFilledIn === $expectedResult) { $success = true; } if (!$success) { if ($isFilledIn) { $errorMessage = _t(CheckFormData::class . '.FIELD_MUST_BE_EMPTY', 'This field must be empty.'); } else { $errorMessage = _t(CheckFormData::class . '.FIELD_MAY_NOT_BE_EMPTY', 'This field may not be empty.'); } } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * Is the field empty? If a dependent field is not filled in an error will * be returned * * @param array $parameters fields to be checked * array( * array( * 'field' => string, * 'hasValue' => mixed * ), * array( * 'field' => mixed * ) * ) * * @throws Exception * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function isFilledInDependantOn($parameters) { $isFilledInCorrectly = true; $checkValue = $this -> getValueWithoutWhitespace($this -> value); if (is_array($parameters)) { if (array_key_exists('requirement', $parameters[0])) { return $this->isValidDependantOn($parameters, 'isFilledIn'); } if (!isset($parameters[0]['field']) || !isset($parameters[0]['hasValue'])) { throw new Exception( 'Field is misconfigured for "CheckFormData->isFilledInDependantOn".' ); } if ($parameters[1][$parameters[0]['field']] == $parameters[0]['hasValue']) { if (empty($checkValue)) { $isFilledInCorrectly = false; } } } else { throw new Exception( 'Field is misconfigured for "CheckFormData->isFilledInDependantOn".' ); } return array( 'success' => $isFilledInCorrectly, 'errorMessage' => _t(CheckFormData::class . '.FIELD_MUST_BE_FILLED_IN', 'Please fill in this field.') ); } /** * Does the input strings have the minimum length? Whitespaces do not count * * @param int $minLength the expression#s minimum length * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function hasMinLength($minLength) { $hasMinLength = true; $checkValue = trim($this -> value); if (strlen($checkValue) > 0 && strlen($checkValue) < $minLength) { $hasMinLength = false; } return array( 'success' => $hasMinLength, 'errorMessage' => _t(CheckFormData::class . '.MIN_CHARS', 'Enter at least {count} characters.', [ 'count' => $minLength, ] ) ); } /** * Does the input string match the length defined? Whitespaces do not count * * @param int $length tthe expressions exact length * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function hasLength($length) { $hasLength = true; $checkValue = trim($this -> value); $checkValueLength = (int) strLen($checkValue); if ($checkValueLength > 0 && $checkValueLength !== (int) $length) { $hasLength = false; } return array( 'success' => $hasLength, 'errorMessage' => _t(CheckFormData::class . '.FIED_REQUIRES_NR_OF_CHARS', 'This field requires exactly {count} characters.', [ 'count' => $length, ] ) ); } /** * Do the values of two fields match? * * @param array $parameters Value and field name to be compared * array ( * 'value' => string: the value the field must have * 'fieldName' => string: Name of the other field * ) * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function mustEqual($parameters) { $isEqual = true; if ($this -> value !== $parameters['value']) { $isEqual = false; } if (isset($parameters['fieldTitle'])) { $refererField = $parameters['fieldTitle']; } else { $refererField = $parameters['fieldName']; } return array( 'success' => $isEqual, 'errorMessage' => _t(CheckFormData::class . '.REQUIRES_SAME_VALUE_AS_IN_FIELD', 'Please enter the same value as in field "{field}".', [ 'field' => $refererField ] ) ); } /** * checks if two field values do NOT match (inversion of mustEqual()) * * @param array $parameters Value and field name to be compared * array ( * 'value' => string: value the field must NOT have * 'fieldName' => string: Name of the other field * ) * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function mustNotEqual($parameters) { $isNotEqual = true; if ($this -> value == $parameters['value']) { $isNotEqual = false; } if (isset($parameters['fieldTitle'])) { $refererField = $parameters['fieldTitle']; } else { $refererField = $parameters['fieldName']; } return array( 'success' => $isNotEqual, 'errorMessage' => _t(CheckFormData::class . '.REQUIRES_OTHER_VALUE_AS_IN_FIELD', 'This field may not have the same value as field "{field}".', [ 'field' => $refererField, ] ) ); } /** * Checks the equality of two fields dependant of another field. * * @param array $parameters fields to be checked * array( * array( * 'field' => string, * 'hasValue' => mixed * ), * array( * 'field' => mixed * ) * ) * * @throws Exception * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sebastian Diel <sdiel@pixeltricks.de> * @since 14.07.2014 */ public function mustEqualDependantOn($parameters) { return $this->isValidDependantOn($parameters, 'mustEqual'); } /** * Checks the equality of two fields dependant of another field. * * @param array $parameters fields to be checked * * @throws Exception * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sebastian Diel <sdiel@pixeltricks.de> * @since 14.07.2014 */ public function mustNotEqualDependantOn($parameters) { return $this->isValidDependantOn($parameters, 'mustNotEqual'); } /** * Checks the validity of the field dependant of another field and a generic * validation method. * * @param array $parameters fields to be checked * @param string $method Method to call * * @throws Exception * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sebastian Diel <sdiel@pixeltricks.de> * @since 14.07.2014 */ public function isValidDependantOn($parameters, $method) { $isValidDependantOn = array( 'success' => true, 'errorMessage' => '', ); if (is_array($parameters)) { if (!isset($parameters[0]['field']) || !isset($parameters[0]['requirement'])) { throw new Exception( 'Field is misconfigured for "CheckFormData->mustNotEqualDependantOn".' ); } $requirement = $parameters[0]['requirement']; $dependantValue = $parameters[1][$parameters[0]['field']]; switch ($requirement) { case 'isFilledIn': default: $result = $this->isFilledIn(true,$dependantValue); break; } if ($result['success']) { $isValidDependantOn = $this->$method($parameters[2]); } } else { throw new Exception( 'Field is misconfigured for "CheckFormData->mustNotEqualDependantOn".' ); } return $isValidDependantOn; } /** * Does a field contain number only * * @param boolean $expectedResult the expected result can be true or false * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function isNumbersOnly($expectedResult) { $consistsOfNumbersOnly = true; $success = false; $checkValue = preg_replace( '/[0-9]*/', '', $this -> value ); if (strlen($checkValue) > 0) { $consistsOfNumbersOnly = false; } if ($consistsOfNumbersOnly === $expectedResult) { $success = true; } return array( 'success' => $success, 'errorMessage' => _t(CheckFormData::class . '.NUMBERS_ONLY', 'This field may consist of numbers only.') ); } /** * Does a field contain only characters for quantity specification? * * @param int $numberOfDecimalPlaces The number of decimal places that are allowed * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 22.11.2012 */ public function isDecimalNumber($numberOfDecimalPlaces) { $isQuantityField = true; $success = true; $errorMessage = ''; $checkValue = preg_replace( '/[0-9,\.]*/', '', $this->value ); $cleanValue = str_replace(',', '.', $this->value); if (strlen($checkValue) > 0) { $isQuantityField = false; } if ($isQuantityField === false) { $errorMessage = _t(CheckFormData::class . '.QUANTITY_ONLY', 'This field may consist of numbers and "." or "," only.'); $success = false; } else { // Check for number of decimal places $separatorPos = strpos($cleanValue, '.'); $decimalPlacesInValue = strlen($this->value) - ($separatorPos + 1); if ($decimalPlacesInValue > $numberOfDecimalPlaces) { $errorMessage = _t(CheckFormData::class . '.MAX_DECIMAL_PLACES_ALLOWED', 'This field can not have more than {places} decimal places.', [ 'places' => $numberOfDecimalPlaces, ] ); $success = false; } } return array( 'success' => $success, 'errorMessage' => $errorMessage ); } /** * Checks if the field input is a currency * * @param mixed $expectedResult the expected result * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function isCurrency($expectedResult) { $success = $expectedResult; if (!empty($this->value)) { $nrOfMatches = preg_match('/^[\d]*[,]?[^\D]*$/', $this->value, $matches); if ($nrOfMatches === 0) { $success = false; } } return array( 'success' => $success, 'errorMessage' => _t(CheckFormData::class . '.CURRENCY_ONLY', 'Please enter a valid currency amount (e.g. 1499,00).') ); } /** * Checks if the field input is a date * * @param mixed $expectedResult programmers expectation to be met * * @return array( * 'success' => bool, * 'errorMessage' => string * ) * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ public function isDate($expectedResult) { $success = $expectedResult; if (!empty($this->value)) { $nrOfMatches = preg_match('/[\d]{2}[\.]{1}[\d]{2}[.]{1}[\d]{4}/', $this->value); if ($nrOfMatches === 0) { $success = false; } } return array( 'success' => $success, 'errorMessage' => _t(CheckFormData::class . '.DATE_ONLY', 'Please enter a valid german date (e.g. "dd.mm.yyyy").') ); } /** * removes a values whitespaces and returns the value cleaned * * @param string $value value to be cleaned of whitespaces * * @return string the cheaned value * * @author Sascha Koehler <skoehler@pixeltricks.de> * @since 25.10.2010 */ private function getValueWithoutWhitespace($value) { return preg_replace('/[\s]*/', '', $value); } /** * Taken from "https://github.com/iamcal/rfc822". * * Checks if an email conforms to the rfc822 standard. * * @param string $email The email address to check * @param array $options Additional options: * - 'allow_comments' * - 'public_internet' * * @return boolean * * @author Cal Henderson <cal@iamcal.com> * @since 19.11.2012 */ private function is_valid_email_address($email, $options=array()) { # # you can pass a few different named options as a second argument, # but the defaults are usually a good choice. # $defaults = array( 'allow_comments' => true, 'public_internet' => true, # turn this off for 'strict' mode ); $opts = array(); foreach ($defaults as $k => $v) { $opts[$k] = isset($options[$k]) ? $options[$k] : $v; } $options = $opts; #################################################################################### # # NO-WS-CTL = %d1-8 / ; US-ASCII control characters # %d11 / ; that do not include the # %d12 / ; carriage return, line feed, # %d14-31 / ; and white space characters # %d127 # ALPHA = %x41-5A / %x61-7A ; A-Z / a-z # DIGIT = %x30-39 $no_ws_ctl = "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]"; $alpha = "[\\x41-\\x5a\\x61-\\x7a]"; $digit = "[\\x30-\\x39]"; $cr = "\\x0d"; $lf = "\\x0a"; $crlf = "(?:$cr$lf)"; #################################################################################### # # obs-char = %d0-9 / %d11 / ; %d0-127 except CR and # %d12 / %d14-127 ; LF # obs-text = *LF *CR *(obs-char *LF *CR) # text = %d1-9 / ; Characters excluding CR and LF # %d11 / # %d12 / # %d14-127 / # obs-text # obs-qp = "\" (%d0-127) # quoted-pair = ("\" text) / obs-qp $obs_char = "[\\x00-\\x09\\x0b\\x0c\\x0e-\\x7f]"; $obs_text = "(?:$lf*$cr*(?:$obs_char$lf*$cr*)*)"; $text = "(?:[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f]|$obs_text)"; # # there's an issue with the definition of 'text', since 'obs_text' can # be blank and that allows qp's with no character after the slash. we're # treating that as bad, so this just checks we have at least one # (non-CRLF) character # $text = "(?:$lf*$cr*$obs_char$lf*$cr*)"; $obs_qp = "(?:\\x5c[\\x00-\\x7f])"; $quoted_pair = "(?:\\x5c$text|$obs_qp)"; #################################################################################### # # obs-FWS = 1*WSP *(CRLF 1*WSP) # FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space # obs-FWS # ctext = NO-WS-CTL / ; Non white space controls # %d33-39 / ; The rest of the US-ASCII # %d42-91 / ; characters not including "(", # %d93-126 ; ")", or "\" # ccontent = ctext / quoted-pair / comment # comment = "(" *([FWS] ccontent) [FWS] ")" # CFWS = *([FWS] comment) (([FWS] comment) / FWS) # # note: we translate ccontent only partially to avoid an infinite loop # instead, we'll recursively strip *nested* comments before processing # the input. that will leave 'plain old comments' to be matched during # the main parse. # $wsp = "[\\x20\\x09]"; $obs_fws = "(?:$wsp+(?:$crlf$wsp+)*)"; $fws = "(?:(?:(?:$wsp*$crlf)?$wsp+)|$obs_fws)"; $ctext = "(?:$no_ws_ctl|[\\x21-\\x27\\x2A-\\x5b\\x5d-\\x7e])"; $ccontent = "(?:$ctext|$quoted_pair)"; $comment = "(?:\\x28(?:$fws?$ccontent)*$fws?\\x29)"; $cfws = "(?:(?:$fws?$comment)*(?:$fws?$comment|$fws))"; # # these are the rules for removing *nested* comments. we'll just detect # outer comment and replace it with an empty comment, and recurse until # we stop. # $outer_ccontent_dull = "(?:$fws?$ctext|$quoted_pair)"; $outer_ccontent_nest = "(?:$fws?$comment)"; $outer_comment = "(?:\\x28$outer_ccontent_dull*(?:$outer_ccontent_nest$outer_ccontent_dull*)+$fws?\\x29)"; #################################################################################### # # atext = ALPHA / DIGIT / ; Any character except controls, # "!" / "#" / ; SP, and specials. # "$" / "%" / ; Used for atoms # "&" / "'" / # "*" / "+" / # "-" / "/" / # "=" / "?" / # "^" / "_" / # "`" / "{" / # "|" / "}" / # "~" # atom = [CFWS] 1*atext [CFWS] $atext = "(?:$alpha|$digit|[\\x21\\x23-\\x27\\x2a\\x2b\\x2d\\x2f\\x3d\\x3f\\x5e\\x5f\\x60\\x7b-\\x7e])"; $atom = "(?:$cfws?(?:$atext)+$cfws?)"; #################################################################################### # # qtext = NO-WS-CTL / ; Non white space controls # %d33 / ; The rest of the US-ASCII # %d35-91 / ; characters not including "\" # %d93-126 ; or the quote character # qcontent = qtext / quoted-pair # quoted-string = [CFWS] # DQUOTE *([FWS] qcontent) [FWS] DQUOTE # [CFWS] # word = atom / quoted-string $qtext = "(?:$no_ws_ctl|[\\x21\\x23-\\x5b\\x5d-\\x7e])"; $qcontent = "(?:$qtext|$quoted_pair)"; $quoted_string = "(?:$cfws?\\x22(?:$fws?$qcontent)*$fws?\\x22$cfws?)"; # # changed the '*' to a '+' to require that quoted strings are not empty # $quoted_string = "(?:$cfws?\\x22(?:$fws?$qcontent)+$fws?\\x22$cfws?)"; $word = "(?:$atom|$quoted_string)"; #################################################################################### # # obs-local-part = word *("." word) # obs-domain = atom *("." atom) $obs_local_part = "(?:$word(?:\\x2e$word)*)"; $obs_domain = "(?:$atom(?:\\x2e$atom)*)"; #################################################################################### # # dot-atom-text = 1*atext *("." 1*atext) # dot-atom = [CFWS] dot-atom-text [CFWS] $dot_atom_text = "(?:$atext+(?:\\x2e$atext+)*)"; $dot_atom = "(?:$cfws?$dot_atom_text$cfws?)"; #################################################################################### # # domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS] # dcontent = dtext / quoted-pair # dtext = NO-WS-CTL / ; Non white space controls # # %d33-90 / ; The rest of the US-ASCII # %d94-126 ; characters not including "[", # ; "]", or "\" $dtext = "(?:$no_ws_ctl|[\\x21-\\x5a\\x5e-\\x7e])"; $dcontent = "(?:$dtext|$quoted_pair)"; $domain_literal = "(?:$cfws?\\x5b(?:$fws?$dcontent)*$fws?\\x5d$cfws?)"; #################################################################################### # # local-part = dot-atom / quoted-string / obs-local-part # domain = dot-atom / domain-literal / obs-domain # addr-spec = local-part "@" domain $local_part = "(($dot_atom)|($quoted_string)|($obs_local_part))"; $domain = "(($dot_atom)|($domain_literal)|($obs_domain))"; $addr_spec = "$local_part\\x40$domain"; # # this was previously 256 based on RFC3696, but dominic's errata was accepted. # if (strlen($email) > 254) { return false; } # # we need to strip nested comments first - we replace them with a simple comment # if ($options['allow_comments']) { $email = $this->email_strip_comments($outer_comment, $email, "(x)"); } # # now match what's left # if (!preg_match("!^$addr_spec$!", $email, $m)) { return false; } $bits = array( 'local' => isset($m[1]) ? $m[1] : '', 'local-atom' => isset($m[2]) ? $m[2] : '', 'local-quoted' => isset($m[3]) ? $m[3] : '', 'local-obs' => isset($m[4]) ? $m[4] : '', 'domain' => isset($m[5]) ? $m[5] : '', 'domain-atom' => isset($m[6]) ? $m[6] : '', 'domain-literal' => isset($m[7]) ? $m[7] : '', 'domain-obs' => isset($m[8]) ? $m[8] : '', ); # # we need to now strip comments from $bits[local] and $bits[domain], # since we know they're in the right place and we want them out of the # way for checking IPs, label sizes, etc # if ($options['allow_comments']) { $bits['local'] = $this->email_strip_comments($comment, $bits['local']); $bits['domain'] = $this->email_strip_comments($comment, $bits['domain']); } # # length limits on segments # if (strlen($bits['local']) > 64) { return false; } if (strlen($bits['domain']) > 255) { return false; } # # restrictions on domain-literals from RFC2821 section 4.1.3 # # RFC4291 changed the meaning of :: in IPv6 addresses - i can mean one or # more zero groups (updated from 2 or more). # if (strlen($bits['domain-literal'])) { $Snum = "(\d{1,3})"; $IPv4_address_literal = "$Snum\.$Snum\.$Snum\.$Snum"; $IPv6_hex = "(?:[0-9a-fA-F]{1,4})"; $IPv6_full = "IPv6\:$IPv6_hex(?:\:$IPv6_hex){7}"; $IPv6_comp_part = "(?:$IPv6_hex(?:\:$IPv6_hex){0,7})?"; $IPv6_comp = "IPv6\:($IPv6_comp_part\:\:$IPv6_comp_part)"; $IPv6v4_full = "IPv6\:$IPv6_hex(?:\:$IPv6_hex){5}\:$IPv4_address_literal"; $IPv6v4_comp_part = "$IPv6_hex(?:\:$IPv6_hex){0,5}"; $IPv6v4_comp = "IPv6\:((?:$IPv6v4_comp_part)?\:\:(?:$IPv6v4_comp_part\:)?)$IPv4_address_literal"; # # IPv4 is simple # if (preg_match("!^\[$IPv4_address_literal\]$!", $bits['domain'], $m)) { if (intval($m[1]) > 255) { return false; } if (intval($m[2]) > 255) { return false; } if (intval($m[3]) > 255) { return false; } if (intval($m[4]) > 255) { return false; } } else { # # this should be IPv6 - a bunch of tests are needed here :) # while (1) { if (preg_match("!^\[$IPv6_full\]$!", $bits['domain'])) { break; } if (preg_match("!^\[$IPv6_comp\]$!", $bits['domain'], $m)) { list($a, $b) = explode('::', $m[1]); $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b"; $groups = explode(':', $folded); if (count($groups) > 7) { return false; } break; } if (preg_match("!^\[$IPv6v4_full\]$!", $bits['domain'], $m)) { if (intval($m[1]) > 255) { return false; } if (intval($m[2]) > 255) { return false; } if (intval($m[3]) > 255) { return false; } if (intval($m[4]) > 255) { return false; } break; } if (preg_match("!^\[$IPv6v4_comp\]$!", $bits['domain'], $m)) { list($a, $b) = explode('::', $m[1]); $b = substr($b, 0, -1); # remove the trailing colon before the IPv4 address $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b"; $groups = explode(':', $folded); if (count($groups) > 5) { return false; } break; } return false; } } } else { # # the domain is either dot-atom or obs-domain - either way, it's # made up of simple labels and we split on dots # $labels = explode('.', $bits['domain']); # # this is allowed by both dot-atom and obs-domain, but is un-routeable on the # public internet, so we'll fail it (e.g. user@localhost) # if ($options['public_internet']) { if (count($labels) == 1) { return false; } } # # checks on each label # foreach ($labels as $label) { if (strlen($label) > 63) { return false; } if (substr($label, 0, 1) == '-') { return false; } if (substr($label, -1) == '-') { return false; } } # # last label can't be all numeric # if ($options['public_internet']) { if (preg_match('!^[0-9]+$!', array_pop($labels))) { return false; } } } return true; } /** * Taken from "https://github.com/iamcal/rfc822". * * Removes comments from an email. * * @param string $comment The comment * @param string $email The email * @param string $replace The replace string * * @return string * * @author Cal Henderson <cal@iamcal.com> * @since 19.11.2012 */ private function email_strip_comments($comment, $email, $replace='') { while (1) { $new = preg_replace("!$comment!", $replace, $email); if (strlen($new) == strlen($email)) { return $email; } $email = $new; } } } |