diff --git a/EmailValidator/Parser/FoldingWhiteSpace.php b/EmailValidator/Parser/FoldingWhiteSpace.php new file mode 100644 index 0000000..a67d757 --- /dev/null +++ b/EmailValidator/Parser/FoldingWhiteSpace.php @@ -0,0 +1,92 @@ +isFWS()) { + return new ValidEmail(); + } + + $previous = $this->lexer->getPrevious(); + + $this->checkCRLFInFWS(); + + if ($this->lexer->token['type'] === EmailLexer::S_CR) { + return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']); + } + + if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { + return new InvalidEmail(new AtextAfterCFWS(), $this->lexer->token['value']); + } + + if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { + return new InvalidEmail(new ExpectingCTEXT(), $this->lexer->token['value']); + } + + if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) { + $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); + } else { + $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); + } + + return new ValidEmail(); + } + + protected function checkCRLFInFWS() + { + if ($this->lexer->token['type'] !== EmailLexer::CRLF) { + return new ValidEmail(); + } + + if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { + return new InvalidEmail(new CRLFX2(), $this->lexer->token['value']); + } + + //this has no coverage. Condition is repeated from above one + if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { + return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->token['value']); + } + } + + /** + * @return bool + */ + protected function isFWS() + { + if ($this->escaped()) { + return false; + } + + return $this->lexer->token['type'] === EmailLexer::S_SP || + $this->lexer->token['type'] === EmailLexer::S_HTAB || + $this->lexer->token['type'] === EmailLexer::S_CR || + $this->lexer->token['type'] === EmailLexer::S_LF || + $this->lexer->token['type'] === EmailLexer::CRLF; + } + + /** + * @return bool + */ + protected function escaped() + { + $previous = $this->lexer->getPrevious(); + + return $previous && $previous['type'] === EmailLexer::S_BACKSLASH + && + $this->lexer->token['type'] !== EmailLexer::GENERIC; + } + +} \ No newline at end of file diff --git a/EmailValidator/Parser/LocalPart.php b/EmailValidator/Parser/LocalPart.php index c617cde..e23eb59 100644 --- a/EmailValidator/Parser/LocalPart.php +++ b/EmailValidator/Parser/LocalPart.php @@ -26,9 +26,12 @@ class LocalPart extends Parser EmailLexer::INVALID => EmailLexer::INVALID ); + private $foldingWS; + public function parse($localPart) : Result { $totalLength = 0; + $this->foldingWS = new FoldingWhiteSpace($this->lexer); while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) { if ($this->hasDotAtStart()) { @@ -73,8 +76,9 @@ class LocalPart extends Parser return new InvalidEmail(new ReasonExpectingATEXT('Invalid token found'), $this->lexer->token['value']); } - if ($this->isFWS()) { - $this->parseFWS(); + $resultFWS = $this->parseLocalFWS(); + if($resultFWS->isInvalid()) { + return $resultFWS; } $totalLength += strlen($this->lexer->token['value']); @@ -88,6 +92,18 @@ class LocalPart extends Parser return new ValidEmail(); } + protected function parseLocalFWS() : Result + { + $resultFWS = $this->foldingWS->parse('remove'); + if ($resultFWS->isValid()) { + $warns = $this->foldingWS->getWarnings(); + foreach ($warns as $code => $dWarning) { + $this->warnings[$code] = $dWarning; + } + } + return $resultFWS; + } + protected function hasDotAtStart() : bool { return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']; diff --git a/EmailValidator/Result/Reason/AtextAfterCFWS.php b/EmailValidator/Result/Reason/AtextAfterCFWS.php new file mode 100644 index 0000000..96e2284 --- /dev/null +++ b/EmailValidator/Result/Reason/AtextAfterCFWS.php @@ -0,0 +1,16 @@ +