![](https://seccdn.libravatar.org/avatar/e2145bc5cf53dda95c308a3c75e8fef3.jpg?s=120&d=mm&r=g)
Hello community, here is the log from the commit of package ghc-email-validate for openSUSE:Factory checked in at 2017-07-11 08:26:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-email-validate (Old) and /work/SRC/openSUSE:Factory/.ghc-email-validate.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "ghc-email-validate" Tue Jul 11 08:26:10 2017 rev:3 rq:509053 version:2.2.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-email-validate/ghc-email-validate.changes 2016-07-20 09:27:06.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.ghc-email-validate.new/ghc-email-validate.changes 2017-07-11 08:26:12.156424440 +0200 @@ -1,0 +2,5 @@ +Fri Jun 30 03:01:55 UTC 2017 - psimons@suse.com + +- Update to version 2.2.1.1. + +------------------------------------------------------------------- Old: ---- email-validate-2.2.0.tar.gz New: ---- email-validate-2.2.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-email-validate.spec ++++++ --- /var/tmp/diff_new_pack.odc4it/_old 2017-07-11 08:26:13.556227001 +0200 +++ /var/tmp/diff_new_pack.odc4it/_new 2017-07-11 08:26:13.560226437 +0200 @@ -1,7 +1,7 @@ # # spec file for package ghc-email-validate # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,15 +19,14 @@ %global pkg_name email-validate %bcond_with tests Name: ghc-%{pkg_name} -Version: 2.2.0 +Version: 2.2.1.1 Release: 0 -Summary: Validating an email address string against RFC 5322 +Summary: Email address validation License: BSD-3-Clause -Group: System/Libraries +Group: Development/Languages/Other Url: https://hackage.haskell.org/package/%{pkg_name} Source0: https://hackage.haskell.org/package/%{pkg_name}-%{version}/%{pkg_name}-%{version}.tar.gz BuildRequires: ghc-Cabal-devel -# Begin cabal-rpm deps: BuildRequires: ghc-attoparsec-devel BuildRequires: ghc-bytestring-devel BuildRequires: ghc-rpm-macros @@ -39,7 +38,6 @@ BuildRequires: ghc-test-framework-hunit-devel BuildRequires: ghc-test-framework-quickcheck2-devel %endif -# End cabal-rpm deps %description Validating an email address string against RFC 5322. @@ -62,16 +60,11 @@ %build %ghc_lib_build - %install %ghc_lib_install - %check -%if %{with tests} -%{cabal} test -%endif - +%cabal_test %post devel %ghc_pkg_recache ++++++ email-validate-2.2.0.tar.gz -> email-validate-2.2.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/email-validate-2.2.0/email-validate.cabal new/email-validate-2.2.1.1/email-validate.cabal --- old/email-validate-2.2.0/email-validate.cabal 2016-01-14 21:58:59.000000000 +0100 +++ new/email-validate-2.2.1.1/email-validate.cabal 2017-06-26 01:32:54.000000000 +0200 @@ -1,51 +1,51 @@ -name: email-validate -version: 2.2.0 -license: BSD3 -license-file: LICENSE -author: George Pollard <porges@porg.es> -maintainer: George Pollard <porges@porg.es> -homepage: http://porg.es/blog/email-address-validation-simpler-faster-more-correct -category: Text -synopsis: Validating an email address string against RFC 5322 -description: Validating an email address string against RFC 5322 -build-type: Simple -stability: experimental -cabal-version: >= 1.10 - -source-repository head - type: git - location: git://github.com/Porges/email-validate-hs.git - -source-repository this - type: git - location: git://github.com/Porges/email-validate-hs.git - tag: v2.2.0 - -library - build-depends: - base >= 4.4 && < 5, - attoparsec >= 0.10.0, - bytestring >= 0.9, - ghc-prim - default-language: Haskell2010 - default-extensions: DeriveGeneric, DeriveDataTypeable - hs-source-dirs: src - exposed-modules: - Text.Email.Validate, - Text.Email.Parser - -Test-Suite Main - type: exitcode-stdio-1.0 - main-is: Main.hs - hs-source-dirs: tests - x-uses-tf: true - default-language: Haskell2010 - build-depends: - base >= 4 && < 5, - HUnit >= 1.2 && < 2, - email-validate, - QuickCheck >= 2.4, - test-framework >= 0.4.1, - test-framework-quickcheck2, - test-framework-hunit, - bytestring >= 0.9 +name: email-validate +version: 2.2.1.1 +cabal-version: >=1.10 +build-type: Simple +license: BSD3 +license-file: LICENSE +maintainer: George Pollard <porges@porg.es> +stability: experimental +homepage: https://github.com/Porges/email-validate-hs +synopsis: Email address validation +description: + Validating an email address string against RFC 5322 +category: Text +author: George Pollard <porges@porg.es> + +source-repository head + type: git + location: git://github.com/Porges/email-validate-hs.git + +source-repository this + type: git + location: git://github.com/Porges/email-validate-hs.git + tag: v2.2.1.1 + +library + exposed-modules: + Text.Domain.Parser + Text.Email.Validate + Text.Email.Parser + build-depends: + base >=4.4 && <5, + attoparsec >=0.10.0 && <0.14, + bytestring >=0.9 && <0.11 + default-language: Haskell2010 + hs-source-dirs: src + ghc-options: -Wall + +test-suite Main + type: exitcode-stdio-1.0 + main-is: Main.hs + build-depends: + base ==4.*, + HUnit >=1.2 && <2, + email-validate >=2.2.1.1 && <2.3, + QuickCheck >=2.4 && <2.11, + test-framework >=0.4.1 && <0.9, + test-framework-quickcheck2 >=0.3.0.4 && <0.4, + test-framework-hunit >=0.3.0.2 && <0.4, + bytestring >=0.9 && <0.11 + default-language: Haskell2010 + hs-source-dirs: tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/email-validate-2.2.0/src/Text/Domain/Parser.hs new/email-validate-2.2.1.1/src/Text/Domain/Parser.hs --- old/email-validate-2.2.0/src/Text/Domain/Parser.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/email-validate-2.2.1.1/src/Text/Domain/Parser.hs 2017-06-26 01:25:25.000000000 +0200 @@ -0,0 +1,41 @@ +{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-} + +module Text.Domain.Parser + ( domainParser + ) +where + +import Control.Applicative +import Control.Monad (guard) +import Data.Attoparsec.ByteString.Char8 +import qualified Data.ByteString.Char8 as BS +import Data.ByteString (ByteString) + +domainParser :: Parser ByteString +domainParser = do + domain <- fst <$> match (label `sepBy1` char '.' >> optional (char '.')) + + -- trim off the excess '.' if it is there + let trimmed = + case BS.last domain of + '.' -> BS.init domain + _ -> domain + + -- domain name must be no greater than 253 chars + guard (BS.length trimmed <= 253) + return trimmed + +label :: Parser ByteString +label = do + lbl <- fst <$> match (alphaNum >> skipWhile isAlphaNumHyphen) + + -- label must be no greater than 63 chars and cannot end with '-' + guard (BS.length lbl <= 63 && BS.last lbl /= '-') + return lbl + +alphaNum :: Parser Char +alphaNum = satisfy isAlphaNum + where isAlphaNum x = isDigit x || isAlpha_ascii x + +isAlphaNumHyphen :: Char -> Bool +isAlphaNumHyphen x = isDigit x || isAlpha_ascii x || x == '-' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/email-validate-2.2.0/src/Text/Email/Parser.hs new/email-validate-2.2.1.1/src/Text/Email/Parser.hs --- old/email-validate-2.2.0/src/Text/Email/Parser.hs 2016-01-14 21:55:22.000000000 +0100 +++ new/email-validate-2.2.1.1/src/Text/Email/Parser.hs 2017-06-26 01:25:25.000000000 +0200 @@ -10,18 +10,17 @@ ) where -import Control.Applicative -import Control.Monad (void) - +import Control.Applicative +import Control.Monad (void) +import Data.Attoparsec.ByteString.Char8 +import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS -import Data.ByteString (ByteString) - -import Data.Attoparsec.ByteString.Char8 - -import Data.Data (Data, Typeable) -import GHC.Generics (Generic) +import Data.Data (Data, Typeable) +import GHC.Generics (Generic) import qualified Text.Read as Read +import Text.Domain.Parser (domainParser) + -- | Represents an email address. data EmailAddress = EmailAddress ByteString ByteString deriving (Eq, Ord, Data, Typeable, Generic) @@ -58,21 +57,25 @@ -- | A parser for email addresses. addrSpec :: Parser EmailAddress -addrSpec = do - l <- local - _ <- char '@' - d <- domain - return (EmailAddress l d) +addrSpec = unsafeEmailAddress <$> local <* char '@' <*> domain local :: Parser ByteString local = dottedAtoms domain :: Parser ByteString -domain = dottedAtoms <|> domainLiteral +domain = domainName <|> domainLiteral + +domainName :: Parser ByteString +domainName = do + raw <- BS.append <$> dottedAtoms <*> option BS.empty (string (BS.pack ".")) + case parseOnly (domainParser <* endOfInput) raw of + Left err -> fail err + Right result -> return result dottedAtoms :: Parser ByteString dottedAtoms = BS.intercalate (BS.singleton '.') <$> - between1 (optional cfws) (atom <|> quotedString) `sepBy1` char '.' + between1 (optional cfws) + (atom <|> quotedString) `sepBy1` char '.' atom :: Parser ByteString atom = takeWhile1 isAtomText @@ -91,8 +94,8 @@ quotedString :: Parser ByteString quotedString = - (\x -> BS.concat [BS.singleton '"', BS.concat x, BS.singleton '"']) <$> - between (char '"') (char '"') + (BS.cons '"' . flip BS.snoc '"' . BS.concat) <$> + between1 (char '"') (many (optional fws >> quotedContent) <* optional fws) quotedContent :: Parser ByteString diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/email-validate-2.2.0/src/Text/Email/Validate.hs new/email-validate-2.2.1.1/src/Text/Email/Validate.hs --- old/email-validate-2.2.0/src/Text/Email/Validate.hs 2016-01-14 21:57:18.000000000 +0100 +++ new/email-validate-2.2.1.1/src/Text/Email/Validate.hs 2017-06-26 01:25:25.000000000 +0200 @@ -11,12 +11,10 @@ ) where -import Control.Applicative ((<*)) - import Data.Attoparsec.ByteString (endOfInput, parseOnly) import Data.ByteString (ByteString) -import Text.Email.Parser (EmailAddress(..), addrSpec, domainPart, +import Text.Email.Parser (EmailAddress, addrSpec, domainPart, localPart, toByteString, unsafeEmailAddress) -- | Smart constructor for an email address @@ -36,4 +34,4 @@ -- | If you want to find out *why* a particular string is not -- an email address, use this. validate :: ByteString -> Either String EmailAddress -validate = parseOnly (addrSpec <* endOfInput) +validate = parseOnly (addrSpec >>= \r -> endOfInput >> return r) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/email-validate-2.2.0/tests/Main.hs new/email-validate-2.2.1.1/tests/Main.hs --- old/email-validate-2.2.0/tests/Main.hs 2016-01-14 21:52:57.000000000 +0100 +++ new/email-validate-2.2.1.1/tests/Main.hs 2017-06-26 01:25:25.000000000 +0200 @@ -1,61 +1,98 @@ -module Main where +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} -import Text.Email.Validate -import Test.HUnit +module Main where import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS +import Data.Maybe (Maybe(..), isNothing) import Test.Framework as TF (defaultMain, testGroup, Test) -import Test.Framework.Providers.HUnit +import Test.Framework.Providers.HUnit (testCase) import Test.Framework.Providers.QuickCheck2 (testProperty) -import Test.QuickCheck +import Test.HUnit ((@?=), assert) +import Test.QuickCheck (Arbitrary(..), suchThat) + +import Text.Email.Validate + ( EmailAddress + , canonicalizeEmail + , domainPart + , emailAddress + , localPart + , isValid + , toByteString + , validate + , unsafeEmailAddress + ) main :: IO () -main = defaultMain tests +main = defaultMain testGroups -tests :: [TF.Test] -tests = [ - testGroup "EmailAddress Show/Read instances" [ - testProperty "showLikeByteString" prop_showLikeByteString, - testProperty "showAndReadBackWithoutQuoteFails" prop_showAndReadBackWithoutQuoteFails, - testProperty "showAndReadBack" prop_showAndReadBack - ], - - testGroup "QuickCheck Text.Email.Validate" [ - testProperty "doubleCanonicalize" prop_doubleCanonicalize - ], - - testGroup "Unit tests Text.Email.Validate" $ flip concatMap units - (\(em, valid, _) -> let email = BS.pack em - in - [ - testCase ("doubleCanonicalize '" ++ em ++ "'") (True @=? case emailAddress email of { Nothing -> True; Just ok -> prop_doubleCanonicalize ok }), - testCase ("validity test '" ++ em ++ "'") (valid @=? isValid email) - ]), +{- Tests -} + +testGroups :: [Test] +testGroups = + [ showAndRead + , canonicalization + , exampleTests + , specificFailures + , simpleAccessors + ] + +canonicalization = + testGroup "QuickCheck Text.Email.Validate" + [ testProperty "doubleCanonicalize" prop_doubleCanonicalize + ] - testGroup "Issues" [ - testCase "#12" (let (Right em) = validate (BS.pack "\"\"@1") in em @=? read (show em)) +exampleTests = + testGroup "Unit tests Text.Email.Validate" (concatMap exampleTest examples) + where + exampleTest Example{example, valid, reason} = + if valid + then + [ testCase ("Ensure valid " ++ name) (assert (isValid example)) + , testCase ("doubleCanonicalize test " ++ name) (assert (case emailAddress example of { Just ok -> prop_doubleCanonicalize ok; Nothing -> False })) ] - ] + else + [ testCase ("Ensure invalid " ++ name) (assert (not (isValid example))) ] + + where name = show example ++ (if null reason then "" else " (" ++ reason ++ ")") + +showAndRead = + testGroup "EmailAddress Show/Read instances" + [ testProperty "showLikeByteString" prop_showLikeByteString + , testProperty "showAndReadBackWithoutQuoteFails" prop_showAndReadBackWithoutQuoteFails + , testProperty "showAndReadBack" prop_showAndReadBack + ] + +specificFailures = + testGroup "Specifics" + [ testCase "Issue #12" (let (Right em) = validate (BS.pack "\"\"@1") in em @?= read (show em)) + , testCase "Check canonicalization of trailing dot" (canonicalizeEmail "foo@bar.com." @?= Just "foo@bar.com") + ] + +simpleAccessors = + testGroup "Simple accessors" + [ testCase "local-part" (localPart (unsafeEmailAddress "local" undefined) @?= "local") + , testCase "domain-part" (domainPart (unsafeEmailAddress undefined "domain") @?= "domain") + ] instance Arbitrary ByteString where arbitrary = fmap BS.pack arbitrary instance Arbitrary EmailAddress where arbitrary = do - local <- suchThat arbitrary (\x -> isEmail x (BS.pack "example.com")) - domain <- suchThat arbitrary (isEmail (BS.pack "example")) - let email = makeEmailLike local domain - let (Just result) = emailAddress email - return result + local <- suchThat arbitrary (\l -> isEmail l (BS.pack "example.com")) + domain <- suchThat arbitrary (\d -> isEmail (BS.pack "example") d) + let (Just result) = emailAddress (makeEmailLike local domain) + pure result + + where + isEmail l d = isValid (makeEmailLike l d) + makeEmailLike l d = BS.concat [l, BS.singleton '@', d] -isEmail :: ByteString -> ByteString -> Bool -isEmail l d = isValid (makeEmailLike l d) - -makeEmailLike :: ByteString -> ByteString -> ByteString -makeEmailLike l d = BS.concat [l, BS.singleton '@', d] +{- Properties -} prop_doubleCanonicalize :: EmailAddress -> Bool prop_doubleCanonicalize email = Just email == emailAddress (toByteString email) @@ -66,247 +103,253 @@ prop_showAndReadBack :: EmailAddress -> Bool prop_showAndReadBack email = read (show email) == email -readMaybe :: String -> Maybe EmailAddress -readMaybe s = case reads s of - [(x, "")] -> Just x - _ -> Nothing - prop_showAndReadBackWithoutQuoteFails :: EmailAddress -> Bool prop_showAndReadBackWithoutQuoteFails email = - readMaybe (init s) == Nothing && - readMaybe (tail s) == Nothing - where s = show email - ---unitTest (x, y, z) = if not (isValid (BS.pack x) == y) then "" else (x ++" became "++ (case emailAddress (BS.pack x) of {Nothing -> "fail"; Just em -> show em}) ++": Should be "++show y ++", got "++show (not y)++"\n\t"++z++"\n") - -units :: [(String, Bool, String)] -units = [ - ("first.last@example.com", True, ""), - ("1234567890123456789012345678901234567890123456789012345678901234@example.com", True, ""), - ("\"first last\"@example.com", True, ""), - ("\"first\\\"last\"@example.com", True, ""), - ("first\\@last@example.com", False, "Escaping can only happen within a quoted string"), - ("\"first@last\"@example.com", True, ""), - ("\"first\\\\last\"@example.com", True, ""), - ("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x234", True, ""), - ("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.123456789012345678901234567890123456789012345678901234567890123.example.com", True, ""), - ("first.last@[12.34.56.78]", True, ""), - ("first.last@[IPv6:::12.34.56.78]", True, ""), - ("first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]", True, ""), - ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]", True, ""), - ("first.last@[IPv6:::1111:2222:3333:4444:5555:6666]", True, ""), - ("first.last@[IPv6:1111:2222:3333::4444:5555:6666]", True, ""), - ("first.last@[IPv6:1111:2222:3333:4444:5555:6666::]", True, ""), - ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]", True, ""), - ("first.last@x23456789012345678901234567890123456789012345678901234567890123.example.com", True, ""), - ("first.last@1xample.com", True, ""), - ("first.last@123.example.com", True, ""), - ("first.last", False, "No @"), - (".first.last@example.com", False, "Local part starts with a dot"), - ("first.last.@example.com", False, "Local part ends with a dot"), - ("first..last@example.com", False, "Local part has consecutive dots"), - ("\"first\"last\"@example.com", False, "Local part contains unescaped excluded characters"), - ("\"first\\last\"@example.com", True, "Any character can be escaped in a quoted string"), - ("\"\"\"@example.com", False, "Local part contains unescaped excluded characters"), - ("\"\\\"@example.com", False, "Local part cannot end with a backslash"), - ("first\\\\@last@example.com", False, "Local part contains unescaped excluded characters"), - ("first.last@", False, "No domain"), - ("\"Abc\\@def\"@example.com", True, ""), - ("\"Fred\\ Bloggs\"@example.com", True, ""), - ("\"Joe.\\\\Blow\"@example.com", True, ""), - ("\"Abc@def\"@example.com", True, ""), - ("\"Fred Bloggs\"@example.com", True, ""), - ("user+mailbox@example.com", True, ""), - ("customer/department=shipping@example.com", True, ""), - ("$A12345@example.com", True, ""), - ("!def!xyz%abc@example.com", True, ""), - ("_somename@example.com", True, ""), - ("dclo@us.ibm.com", True, ""), - ("abc\\@def@example.com", False, "This example from RFC3696 was corrected in an erratum"), - ("abc\\\\@example.com", False, "This example from RFC3696 was corrected in an erratum"), - ("peter.piper@example.com", True, ""), - ("Doug\\ \\\"Ace\\\"\\ Lovell@example.com", False, "Escaping can only happen in a quoted string"), - ("\"Doug \\\"Ace\\\" L.\"@example.com", True, ""), - ("abc@def@example.com", False, "Doug Lovell says this should fail"), - ("abc\\\\@def@example.com", False, "Doug Lovell says this should fail"), - ("abc\\@example.com", False, "Doug Lovell says this should fail"), - ("@example.com", False, "No local part"), - ("doug@", False, "Doug Lovell says this should fail"), - ("\"qu@example.com", False, "Doug Lovell says this should fail"), - ("ote\"@example.com", False, "Doug Lovell says this should fail"), - (".dot@example.com", False, "Doug Lovell says this should fail"), - ("dot.@example.com", False, "Doug Lovell says this should fail"), - ("two..dot@example.com", False, "Doug Lovell says this should fail"), - ("\"Doug \"Ace\" L.\"@example.com", False, "Doug Lovell says this should fail"), - ("Doug\\ \\\"Ace\\\"\\ L\\.@example.com", False, "Doug Lovell says this should fail"), - ("hello world@example.com", False, "Doug Lovell says this should fail"), - ("gatsby@f.sc.ot.t.f.i.tzg.era.l.d.", False, "Doug Lovell says this should fail"), - ("test@example.com", True, ""), - ("TEST@example.com", True, ""), - ("1234567890@example.com", True, ""), - ("test+test@example.com", True, ""), - ("test-test@example.com", True, ""), - ("t*est@example.com", True, ""), - ("+1~1+@example.com", True, ""), - ("{_test_}@example.com", True, ""), - ("\"[[ test ]]\"@example.com", True, ""), - ("test.test@example.com", True, ""), - ("\"test.test\"@example.com", True, ""), - ("test.\"test\"@example.com", True, "Obsolete form, but documented in RFC2822"), - ("\"test@test\"@example.com", True, ""), - ("test@123.123.123.x123", True, ""), - ("test@[123.123.123.123]", True, ""), - ("test@example.example.com", True, ""), - ("test@example.example.example.com", True, ""), - ("test.example.com", False, ""), - ("test.@example.com", False, ""), - ("test..test@example.com", False, ""), - (".test@example.com", False, ""), - ("test@test@example.com", False, ""), - ("test@@example.com", False, ""), - ("-- test --@example.com", False, "No spaces allowed in local part"), - ("[test]@example.com", False, "Square brackets only allowed within quotes"), - ("\"test\\test\"@example.com", True, "Any character can be escaped in a quoted string"), - ("\"test\"test\"@example.com", False, "Quotes cannot be nested"), - ("()[]\\;:,><@example.com", False, "Disallowed Characters"), - ("test@.", False, "Dave Child says so"), - ("test@example.", False, "Dave Child says so"), - ("test@.org", False, "Dave Child says so"), - ("test@[123.123.123.123", False, "Dave Child says so"), - ("test@123.123.123.123]", False, "Dave Child says so"), - ("NotAnEmail", False, "Phil Haack says so"), - ("@NotAnEmail", False, "Phil Haack says so"), - ("\"test\\\\blah\"@example.com", True, ""), - ("\"test\\blah\"@example.com", True, "Any character can be escaped in a quoted string"), - ("\"test\\\rblah\"@example.com", True, "Quoted string specifically excludes carriage returns unless escaped"), - ("\"test\rblah\"@example.com", False, "Quoted string specifically excludes carriage returns"), - ("\"test\\\"blah\"@example.com", True, ""), - ("\"test\"blah\"@example.com", False, "Phil Haack says so"), - ("customer/department@example.com", True, ""), - ("_Yosemite.Sam@example.com", True, ""), - ("~@example.com", True, ""), - (".wooly@example.com", False, "Phil Haack says so"), - ("wo..oly@example.com", False, "Phil Haack says so"), - ("pootietang.@example.com", False, "Phil Haack says so"), - (".@example.com", False, "Phil Haack says so"), - ("\"Austin@Powers\"@example.com", True, ""), - ("Ima.Fool@example.com", True, ""), - ("\"Ima.Fool\"@example.com", True, ""), - ("\"Ima Fool\"@example.com", True, ""), - ("Ima Fool@example.com", False, "Phil Haack says so"), - ("phil.h\\@\\@ck@haacked.com", False, "Escaping can only happen in a quoted string"), - ("\"first\".\"last\"@example.com", True, ""), - ("\"first\".middle.\"last\"@example.com", True, ""), - ("\"first\\\\\"last\"@example.com", False, "Contains an unescaped quote"), - ("\"first\".last@example.com", True, "obs-local-part form as described in RFC 2822"), - ("first.\"last\"@example.com", True, "obs-local-part form as described in RFC 2822"), - ("\"first\".\"middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822"), - ("\"first.middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822"), - ("\"first.middle.last\"@example.com", True, "obs-local-part form as described in RFC 2822"), - ("\"first..last\"@example.com", True, "obs-local-part form as described in RFC 2822"), - ("foo@[\\1.2.3.4]", False, "RFC 5321 specifies the syntax for address-literal and does not allow escaping"), - ("\"first\\\\\\\"last\"@example.com", True, ""), - ("first.\"mid\\dle\".\"last\"@example.com", True, "Backslash can escape anything but must escape something"), - ("Test.\r\n Folding.\r\n Whitespace@example.com", True, ""), - ("first\\last@example.com", False, "Unquoted string must be an atom"), - ("Abc\\@def@example.com", False, "Was incorrectly given as a valid address in the original RFC3696"), - ("Fred\\ Bloggs@example.com", False, "Was incorrectly given as a valid address in the original RFC3696"), - ("Joe.\\\\Blow@example.com", False, "Was incorrectly given as a valid address in the original RFC3696"), - ("\"test\\\r\n blah\"@example.com", False, "Folding white space can\'t appear within a quoted pair"), - ("\"test\r\n blah\"@example.com", True, "This is a valid quoted string with folding white space"), - ("{^c\\@**Dog^}@cartoon.com", False, "This is a throwaway example from Doug Lovell\'s article. Actually it\'s not a valid address."), - ("(foo)cal(bar)@(baz)iamcal.com(quux)", True, "A valid address containing comments"), - ("cal@iamcal(woo).(yay)com", True, "A valid address containing comments"), - ("cal(woo(yay)hoopla)@iamcal.com", True, "A valid address containing comments"), - ("cal(foo\\@bar)@iamcal.com", True, "A valid address containing comments"), - ("cal(foo\\)bar)@iamcal.com", True, "A valid address containing comments and an escaped parenthesis"), - ("cal(foo(bar)@iamcal.com", False, "Unclosed parenthesis in comment"), - ("cal(foo)bar)@iamcal.com", False, "Too many closing parentheses"), - ("cal(foo\\)@iamcal.com", False, "Backslash at end of comment has nothing to escape"), - ("first().last@example.com", True, "A valid address containing an empty comment"), - ("first.(\r\n middle\r\n )last@example.com", True, "Comment with folding white space"), - ("first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890)example.com", False, "Too long with comments, not too long without"), - ("first(Welcome to\r\n the (\"wonderful\" (!)) world\r\n of email)@example.com", True, "Silly example from my blog post"), - ("pete(his account)@silly.test(his host)", True, "Canonical example from RFC5322"), - ("c@(Chris\'s host.)public.example", True, "Canonical example from RFC5322"), - ("jdoe@machine(comment). example", True, "Canonical example from RFC5322"), - ("1234 @ local(blah) .machine .example", True, "Canonical example from RFC5322"), - ("first(middle)last@example.com", False, "Can\'t have a comment or white space except at an element boundary"), - ("first(abc.def).last@example.com", True, "Comment can contain a dot"), - ("first(a\"bc.def).last@example.com", True, "Comment can contain double quote"), - ("first.(\")middle.last(\")@example.com", True, "Comment can contain a quote"), - ("first(abc(\"def\".ghi).mno)middle(abc(\"def\".ghi).mno).last@(abc(\"def\".ghi).mno)example(abc(\"def\".ghi).mno).(abc(\"def\".ghi).mno)com(abc(\"def\".ghi).mno)", False, "Can\'t have comments or white space except at an element boundary"), - ("first(abc\\(def)@example.com", True, "Comment can contain quoted-pair"), - ("first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com", True, "Label is longer than 63 octets, but not with comment removed"), - ("a(a(b(c)d(e(f))g)h(i)j)@example.com", True, ""), - ("a(a(b(c)d(e(f))g)(h(i)j)@example.com", False, "Braces are not properly matched"), - ("name.lastname@domain.com", True, ""), - (".@", False, ""), - ("@bar.com", False, ""), - ("@@bar.com", False, ""), - ("a@bar.com", True, ""), - ("aaa.com", False, ""), - ("aaa@.com", False, ""), - ("aaa@.123", False, ""), - ("aaa@[123.123.123.123]", True, ""), - ("aaa@[123.123.123.123]a", False, "extra data outside ip"), - ("a@bar.com.", False, ""), - ("a-b@bar.com", True, ""), - ("+@b.c", True, "TLDs can be any length"), - ("+@b.com", True, ""), - ("-@..com", False, ""), - ("-@a..com", False, ""), - ("a@b.co-foo.uk", True, ""), - ("\"hello my name is\"@stutter.com", True, ""), - ("\"Test \\\"Fail\\\" Ing\"@example.com", True, ""), - ("valid@special.museum", True, ""), - ("shaitan@my-domain.thisisminekthx", True, "Disagree with Paul Gregg here"), - ("test@...........com", False, "......"), - ("\"Joe\\\\Blow\"@example.com", True, ""), - ("Invalid \\\n Folding \\\n Whitespace@example.com", False, "This isn\'t FWS so Dominic Sayers says it\'s invalid"), - ("HM2Kinsists@(that comments are allowed)this.is.ok", True, ""), - ("user%uucp!path@somehost.edu", True, ""), - ("\"first(last)\"@example.com", True, ""), - (" \r\n (\r\n x \r\n ) \r\n first\r\n ( \r\n x\r\n ) \r\n .\r\n ( \r\n x) \r\n last \r\n ( x \r\n ) \r\n @example.com", True, ""), - ("test.\r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines"), - ("test. \r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines (test 2: space before break)"), - ("test.\r\n\r\n obs@syntax.com", False, "obs-fws must have at least one WSP per line"), - ("\"null \\\0\"@char.com", True, "can have escaped null character"), - ("\"null \0\"@char.com", False, "cannot have unescaped null character") + isNothing (readMaybe (init s)) && isNothing (readMaybe (tail s)) + where + s = show email + readMaybe :: String -> Maybe EmailAddress + readMaybe s = case reads s of + [(x, "")] -> Just x + _ -> Nothing + +{- Examples -} + +data Example = Example { example :: ByteString, valid :: Bool, reason :: String } + +examples :: [Example] +examples = + map (\(e, v, r) -> Example e v r) + [ ("first.last@example.com", True, "") + , ("first.last@example.com.", True, "Dot allowed on end of domain") + , ("local@exam_ple.com", False, "Underscore not permitted in domain") + , ("1234567890123456789012345678901234567890123456789012345678901234@example.com", True, "") + , ("\"first last\"@example.com", True, "") + , ("\"first\\\"last\"@example.com", True, "") + , ("first\\@last@example.com", False, "Escaping can only happen within a quoted string") + , ("\"first@last\"@example.com", True, "") + , ("\"first\\\\last\"@example.com", True, "") + , ("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23", True, "Max length is 253") + , ("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23.", True, "Trailing dot doesn't increase length") + , ("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x234", False, "Max length is 253") + , ("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.123456789012345678901234567890123456789012345678901234567890123.example.com", True, "") + , ("first.last@[12.34.56.78]", True, "") + , ("first.last@[IPv6:::12.34.56.78]", True, "") + , ("first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]", True, "") + , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]", True, "") + , ("first.last@[IPv6:::1111:2222:3333:4444:5555:6666]", True, "") + , ("first.last@[IPv6:1111:2222:3333::4444:5555:6666]", True, "") + , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666::]", True, "") + , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]", True, "") + , ("first.last@x23456789012345678901234567890123456789012345678901234567890123.example.com", True, "") + , ("first.last@1xample.com", True, "") + , ("first.last@123.example.com", True, "") + , ("first.last", False, "No @") + , (".first.last@example.com", False, "Local part starts with a dot") + , ("first.last.@example.com", False, "Local part ends with a dot") + , ("first..last@example.com", False, "Local part has consecutive dots") + , ("\"first\"last\"@example.com", False, "Local part contains unescaped excluded characters") + , ("\"first\\last\"@example.com", True, "Any character can be escaped in a quoted string") + , ("\"\"\"@example.com", False, "Local part contains unescaped excluded characters") + , ("\"\\\"@example.com", False, "Local part cannot end with a backslash") + , ("first\\\\@last@example.com", False, "Local part contains unescaped excluded characters") + , ("first.last@", False, "No domain") + , ("\"Abc\\@def\"@example.com", True, "") + , ("\"Fred\\ Bloggs\"@example.com", True, "") + , ("\"Joe.\\\\Blow\"@example.com", True, "") + , ("\"Abc@def\"@example.com", True, "") + , ("\"Fred Bloggs\"@example.com", True, "") + , ("user+mailbox@example.com", True, "") + , ("customer/department=shipping@example.com", True, "") + , ("$A12345@example.com", True, "") + , ("!def!xyz%abc@example.com", True, "") + , ("_somename@example.com", True, "") + , ("dclo@us.ibm.com", True, "") + , ("abc\\@def@example.com", False, "This example from RFC3696 was corrected in an erratum") + , ("abc\\\\@example.com", False, "This example from RFC3696 was corrected in an erratum") + , ("peter.piper@example.com", True, "") + , ("Doug\\ \\\"Ace\\\"\\ Lovell@example.com", False, "Escaping can only happen in a quoted string") + , ("\"Doug \\\"Ace\\\" L.\"@example.com", True, "") + , ("abc@def@example.com", False, "Doug Lovell says this should fail") + , ("abc\\\\@def@example.com", False, "Doug Lovell says this should fail") + , ("abc\\@example.com", False, "Doug Lovell says this should fail") + , ("@example.com", False, "No local part") + , ("doug@", False, "Doug Lovell says this should fail") + , ("\"qu@example.com", False, "Doug Lovell says this should fail") + , ("ote\"@example.com", False, "Doug Lovell says this should fail") + , (".dot@example.com", False, "Doug Lovell says this should fail") + , ("dot.@example.com", False, "Doug Lovell says this should fail") + , ("two..dot@example.com", False, "Doug Lovell says this should fail") + , ("\"Doug \"Ace\" L.\"@example.com", False, "Doug Lovell says this should fail") + , ("Doug\\ \\\"Ace\\\"\\ L\\.@example.com", False, "Doug Lovell says this should fail") + , ("hello world@example.com", False, "Doug Lovell says this should fail") + , ("gatsby@f.sc.ot.t.f.i.tzg.era.l.d.", True, "") + , ("test@example.com", True, "") + , ("TEST@example.com", True, "") + , ("1234567890@example.com", True, "") + , ("test+test@example.com", True, "") + , ("test-test@example.com", True, "") + , ("t*est@example.com", True, "") + , ("+1~1+@example.com", True, "") + , ("{_test_}@example.com", True, "") + , ("\"[[ test ]]\"@example.com", True, "") + , ("test.test@example.com", True, "") + , ("\"test.test\"@example.com", True, "") + , ("test.\"test\"@example.com", True, "Obsolete form, but documented in RFC2822") + , ("\"test@test\"@example.com", True, "") + , ("test@123.123.123.x123", True, "") + , ("test@[123.123.123.123]", True, "") + , ("test@example.example.com", True, "") + , ("test@example.example.example.com", True, "") + , ("test.example.com", False, "") + , ("test.@example.com", False, "") + , ("test..test@example.com", False, "") + , (".test@example.com", False, "") + , ("test@test@example.com", False, "") + , ("test@@example.com", False, "") + , ("-- test --@example.com", False, "No spaces allowed in local part") + , ("[test]@example.com", False, "Square brackets only allowed within quotes") + , ("\"test\\test\"@example.com", True, "Any character can be escaped in a quoted string") + , ("\"test\"test\"@example.com", False, "Quotes cannot be nested") + , ("()[]\\;:,><@example.com", False, "Disallowed Characters") + , ("test@.", False, "Dave Child says so") + , ("test@example.", True, "") + , ("test@.org", False, "Dave Child says so") + , ("test@[123.123.123.123", False, "Dave Child says so") + , ("test@123.123.123.123]", False, "Dave Child says so") + , ("NotAnEmail", False, "Phil Haack says so") + , ("@NotAnEmail", False, "Phil Haack says so") + , ("\"test\\\\blah\"@example.com", True, "") + , ("\"test\\blah\"@example.com", True, "Any character can be escaped in a quoted string") + , ("\"test\\\rblah\"@example.com", True, "Quoted string specifically excludes carriage returns unless escaped") + , ("\"test\rblah\"@example.com", False, "Quoted string specifically excludes carriage returns") + , ("\"test\\\"blah\"@example.com", True, "") + , ("\"test\"blah\"@example.com", False, "Phil Haack says so") + , ("customer/department@example.com", True, "") + , ("_Yosemite.Sam@example.com", True, "") + , ("~@example.com", True, "") + , (".wooly@example.com", False, "Phil Haack says so") + , ("wo..oly@example.com", False, "Phil Haack says so") + , ("pootietang.@example.com", False, "Phil Haack says so") + , (".@example.com", False, "Phil Haack says so") + , ("\"Austin@Powers\"@example.com", True, "") + , ("Ima.Fool@example.com", True, "") + , ("\"Ima.Fool\"@example.com", True, "") + , ("\"Ima Fool\"@example.com", True, "") + , ("Ima Fool@example.com", False, "Phil Haack says so") + , ("phil.h\\@\\@ck@haacked.com", False, "Escaping can only happen in a quoted string") + , ("\"first\".\"last\"@example.com", True, "") + , ("\"first\".middle.\"last\"@example.com", True, "") + , ("\"first\\\\\"last\"@example.com", False, "Contains an unescaped quote") + , ("\"first\".last@example.com", True, "obs-local-part form as described in RFC 2822") + , ("first.\"last\"@example.com", True, "obs-local-part form as described in RFC 2822") + , ("\"first\".\"middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822") + , ("\"first.middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822") + , ("\"first.middle.last\"@example.com", True, "obs-local-part form as described in RFC 2822") + , ("\"first..last\"@example.com", True, "obs-local-part form as described in RFC 2822") + , ("foo@[\\1.2.3.4]", False, "RFC 5321 specifies the syntax for address-literal and does not allow escaping") + , ("\"first\\\\\\\"last\"@example.com", True, "") + , ("first.\"mid\\dle\".\"last\"@example.com", True, "Backslash can escape anything but must escape something") + , ("Test.\r\n Folding.\r\n Whitespace@example.com", True, "") + , ("first\\last@example.com", False, "Unquoted string must be an atom") + , ("Abc\\@def@example.com", False, "Was incorrectly given as a valid address in the original RFC3696") + , ("Fred\\ Bloggs@example.com", False, "Was incorrectly given as a valid address in the original RFC3696") + , ("Joe.\\\\Blow@example.com", False, "Was incorrectly given as a valid address in the original RFC3696") + , ("\"test\\\r\n blah\"@example.com", False, "Folding white space can\'t appear within a quoted pair") + , ("\"test\r\n blah\"@example.com", True, "This is a valid quoted string with folding white space") + , ("{^c\\@**Dog^}@cartoon.com", False, "This is a throwaway example from Doug Lovell\'s article. Actually it\'s not a valid address.") + , ("(foo)cal(bar)@(baz)iamcal.com(quux)", True, "A valid address containing comments") + , ("cal@iamcal(woo).(yay)com", True, "A valid address containing comments") + , ("cal(woo(yay)hoopla)@iamcal.com", True, "A valid address containing comments") + , ("cal(foo\\@bar)@iamcal.com", True, "A valid address containing comments") + , ("cal(foo\\)bar)@iamcal.com", True, "A valid address containing comments and an escaped parenthesis") + , ("cal(foo(bar)@iamcal.com", False, "Unclosed parenthesis in comment") + , ("cal(foo)bar)@iamcal.com", False, "Too many closing parentheses") + , ("cal(foo\\)@iamcal.com", False, "Backslash at end of comment has nothing to escape") + , ("first().last@example.com", True, "A valid address containing an empty comment") + , ("first.(\r\n middle\r\n )last@example.com", True, "Comment with folding white space") + , ("first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890)example.com", False, "Too long with comments, not too long without") + , ("first(Welcome to\r\n the (\"wonderful\" (!)) world\r\n of email)@example.com", True, "Silly example from my blog post") + , ("pete(his account)@silly.test(his host)", True, "Canonical example from RFC5322") + , ("c@(Chris\'s host.)public.example", True, "Canonical example from RFC5322") + , ("jdoe@machine(comment). example", True, "Canonical example from RFC5322") + , ("1234 @ local(blah) .machine .example", True, "Canonical example from RFC5322") + , ("first(middle)last@example.com", False, "Can\'t have a comment or white space except at an element boundary") + , ("first(abc.def).last@example.com", True, "Comment can contain a dot") + , ("first(a\"bc.def).last@example.com", True, "Comment can contain double quote") + , ("first.(\")middle.last(\")@example.com", True, "Comment can contain a quote") + , ("first(abc(\"def\".ghi).mno)middle(abc(\"def\".ghi).mno).last@(abc(\"def\".ghi).mno)example(abc(\"def\".ghi).mno).(abc(\"def\".ghi).mno)com(abc(\"def\".ghi).mno)", False, "Can\'t have comments or white space except at an element boundary") + , ("first(abc\\(def)@example.com", True, "Comment can contain quoted-pair") + , ("first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com", True, "Label is longer than 63 octets, but not with comment removed") + , ("a(a(b(c)d(e(f))g)h(i)j)@example.com", True, "") + , ("a(a(b(c)d(e(f))g)(h(i)j)@example.com", False, "Braces are not properly matched") + , ("name.lastname@domain.com", True, "") + , (".@", False, "") + , ("@bar.com", False, "") + , ("@@bar.com", False, "") + , ("a@bar.com", True, "") + , ("aaa.com", False, "") + , ("aaa@.com", False, "") + , ("aaa@.123", False, "") + , ("aaa@[123.123.123.123]", True, "") + , ("aaa@[123.123.123.123]a", False, "extra data outside ip") + , ("a@bar.com.", True, "") + , ("a-b@bar.com", True, "") + , ("+@b.c", True, "TLDs can be any length") + , ("+@b.com", True, "") + , ("-@..com", False, "") + , ("-@a..com", False, "") + , ("a@b.co-foo.uk", True, "") + , ("\"hello my name is\"@stutter.com", True, "") + , ("\"Test \\\"Fail\\\" Ing\"@example.com", True, "") + , ("valid@special.museum", True, "") + , ("shaitan@my-domain.thisisminekthx", True, "Disagree with Paul Gregg here") + , ("test@...........com", False, "......") + , ("\"Joe\\\\Blow\"@example.com", True, "") + , ("Invalid \\\n Folding \\\n Whitespace@example.com", False, "This isn\'t FWS so Dominic Sayers says it\'s invalid") + , ("HM2Kinsists@(that comments are allowed)this.is.ok", True, "") + , ("user%uucp!path@somehost.edu", True, "") + , ("\"first(last)\"@example.com", True, "") + , (" \r\n (\r\n x \r\n ) \r\n first\r\n ( \r\n x\r\n ) \r\n .\r\n ( \r\n x) \r\n last \r\n ( x \r\n ) \r\n @example.com", True, "") + , ("test.\r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines") + , ("test. \r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines (test 2: space before break)") + , ("test.\r\n\r\n obs@syntax.com", False, "obs-fws must have at least one WSP per line") + , ("\"null \\\0\"@char.com", True, "can have escaped null character") + , ("\"null \0\"@char.com", False, "cannot have unescaped null character") -- items below here are invalid according to other RFCs (or opinions) - --("\"\"@example.com", False, "Local part is effectively empty"), - --("foobar@192.168.0.1", False, "ip need to be []"), - --("first.last@[.12.34.56.78]", False, "Only char that can precede IPv4 address is \':\'"), - --("first.last@[12.34.56.789]", False, "Can\'t be interpreted as IPv4 so IPv6 tag is missing"), - --("first.last@[::12.34.56.78]", False, "IPv6 tag is missing"), - --("first.last@[IPv5:::12.34.56.78]", False, "IPv6 tag is wrong"), - --("first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]", False, "Too many IPv6 groups (4 max)"), - --("first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]", False, "Not enough IPv6 groups"), - --("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]", False, "Too many IPv6 groups (6 max)"), - --("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]", False, "Not enough IPv6 groups"), - --("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]", False, "Too many IPv6 groups (8 max)"), - --("first.last@[IPv6:1111:2222::3333::4444:5555:6666]", False, "Too many \'::\' (can be none or one)"), - --("first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]", False, "Too many IPv6 groups (6 max)"), - --("first.last@[IPv6:1111:2222:333x::4444:5555]", False, "x is not valid in an IPv6 address"), - --("first.last@[IPv6:1111:2222:33333::4444:5555]", False, "33333 is not a valid group in an IPv6 address"), - --("first.last@example.123", False, "TLD can\'t be all digits"), - --("aaa@[123.123.123.333]", False, "not a valid IP"), - --("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]", False, "IPv4 part contains an invalid octet"), - --("a@b", False, ""), - --("a@bar", False, ""), - --("invalid@special.museum-", False, ""), - --("a@-b.com", False, ""), - --("a@b-.com", False, ""), - --("\"foo\"(yay)@(hoopla)[1.2.3.4]", False, "Address literal can\'t be commented (RFC5321)"), - --("first.\"\".last@example.com", False, "Contains a zero-length element"), - --("test@example", False, "Dave Child says so"), - --("12345678901234567890123456789012345678901234567890123456789012345@example.com", False, "Local part more than 64 characters"), - --("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456", False, "Domain exceeds 255 chars"), - --("test@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com", False, "255 characters is maximum length for domain. This is 256."), - --("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1234.example.com", False, "Entire address is longer than 256 characters"), - --("test@123.123.123.123", False, "Top Level Domain won\'t be all-numeric (see RFC3696 Section 2). I disagree with Dave Child on this one."), - --("first.last@x234567890123456789012345678901234567890123456789012345678901234.example.com", False, "Label can\'t be longer than 63 octets"), - --("first.last@com", False, "Mail host must be second- or lower level"), - --("first.last@-xample.com", False, "Label can\'t begin with a hyphen"), - --("first.last@exampl-.com", False, "Label can\'t end with a hyphen"), + --, ("\"\"@example.com", False, "Local part is effectively empty") + --, ("foobar@192.168.0.1", False, "ip need to be []") + --, ("first.last@[.12.34.56.78]", False, "Only char that can precede IPv4 address is \':\'") + --, ("first.last@[12.34.56.789]", False, "Can\'t be interpreted as IPv4 so IPv6 tag is missing") + --, ("first.last@[::12.34.56.78]", False, "IPv6 tag is missing") + --, ("first.last@[IPv5:::12.34.56.78]", False, "IPv6 tag is wrong") + --, ("first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]", False, "Too many IPv6 groups (4 max)") + --, ("first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]", False, "Not enough IPv6 groups") + --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]", False, "Too many IPv6 groups (6 max)") + --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]", False, "Not enough IPv6 groups") + --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]", False, "Too many IPv6 groups (8 max)") + --, ("first.last@[IPv6:1111:2222::3333::4444:5555:6666]", False, "Too many \'::\' (can be none or one)") + --, ("first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]", False, "Too many IPv6 groups (6 max)") + --, ("first.last@[IPv6:1111:2222:333x::4444:5555]", False, "x is not valid in an IPv6 address") + --, ("first.last@[IPv6:1111:2222:33333::4444:5555]", False, "33333 is not a valid group in an IPv6 address") + --, ("first.last@example.123", False, "TLD can\'t be all digits") + --, ("aaa@[123.123.123.333]", False, "not a valid IP") + --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]", False, "IPv4 part contains an invalid octet") + --, ("a@b", False, "") + --, ("a@bar", False, "") + , ("invalid@special.museum-", False, "") + , ("a@-b.com", False, "") + , ("a@b-.com", False, "") + --, ("\"foo\"(yay)@(hoopla)[1.2.3.4]", False, "Address literal can\'t be commented (RFC5321)") + --, ("first.\"\".last@example.com", False, "Contains a zero-length element") + --, ("test@example", False, "Dave Child says so") + --, ("12345678901234567890123456789012345678901234567890123456789012345@example.com", False, "Local part more than 64 characters") + , ("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456", False, "Domain exceeds 255 chars") + , ("test@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com", False, "255 characters is maximum length for domain. This is 256.") + --, ("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1234.example.com", False, "Entire address is longer than 256 characters") + --, ("test@123.123.123.123", False, "Top Level Domain won\'t be all-numeric (see RFC3696 Section 2). I disagree with Dave Child on this one.") + , ("first.last@x234567890123456789012345678901234567890123456789012345678901234.example.com", False, "Label can\'t be longer than 63 octets") + --, ("first.last@com", False, "Mail host must be second- or lower level") + , ("first.last@-xample.com", False, "Label can\'t begin with a hyphen") + , ("first.last@exampl-.com", False, "Label can\'t end with a hyphen") ]