packages feed

email-validate 0.3.2 → 1.0.0

raw patch · 62 files changed

+441/−823 lines, 62 filesdep +HUnitdep +QuickCheckdep +attoparsecdep −parsecdep −rangesdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, QuickCheck, attoparsec, bytestring, email-validate, test-framework, test-framework-hunit, test-framework-quickcheck2

Dependencies removed: parsec, ranges

Dependency ranges changed: base

API changes (from Hackage documentation)

- Text.Email.Validate: EmailAddress :: String -> String -> EmailAddress
- Text.Email.Validate: addrSpec :: Parsec String () Int
- Text.Email.Validate: instance Show EmailAddress
+ Text.Email.Parser: addrSpec :: Parser ByteString EmailAddress
+ Text.Email.Parser: data EmailAddress
+ Text.Email.Parser: domainPart :: EmailAddress -> ByteString
+ Text.Email.Parser: instance Eq EmailAddress
+ Text.Email.Parser: instance Ord EmailAddress
+ Text.Email.Parser: instance Show EmailAddress
+ Text.Email.Parser: localPart :: EmailAddress -> ByteString
+ Text.Email.Parser: toByteString :: EmailAddress -> ByteString
+ Text.Email.Validate: canonicalizeEmail :: ByteString -> Maybe ByteString
+ Text.Email.Validate: emailAddress :: ByteString -> Maybe EmailAddress
+ Text.Email.Validate: toByteString :: EmailAddress -> ByteString
- Text.Email.Validate: domainPart :: EmailAddress -> String
+ Text.Email.Validate: domainPart :: EmailAddress -> ByteString
- Text.Email.Validate: isValid :: String -> Bool
+ Text.Email.Validate: isValid :: ByteString -> Bool
- Text.Email.Validate: localPart :: EmailAddress -> String
+ Text.Email.Validate: localPart :: EmailAddress -> ByteString
- Text.Email.Validate: validate :: String -> Either ParseError EmailAddress
+ Text.Email.Validate: validate :: ByteString -> Either String EmailAddress

Files

− .git/COMMIT_EDITMSG
@@ -1,3 +0,0 @@-Revert "revert removal of eof marker"--This reverts commit caabd581f6cf7157ce211a91112cf5047e527be7.
− .git/FETCH_HEAD
@@ -1,1 +0,0 @@-2353b6eaeee1c824371c12bad78d4053ec52041d		branch 'master' of https://github.com/Porges/email-validate-hs
− .git/HEAD
@@ -1,1 +0,0 @@-ref: refs/heads/master
− .git/ORIG_HEAD
@@ -1,1 +0,0 @@-6eb270b673a8ae8db8e5e2a5e765065933943523
− .git/config
@@ -1,14 +0,0 @@-[core]-	repositoryformatversion = 0-	filemode = false-	bare = false-	logallrefupdates = true-	symlinks = false-	ignorecase = true-	hideDotFiles = dotGitOnly-[remote "origin"]-	fetch = +refs/heads/*:refs/remotes/origin/*-	url = https://github.com/Porges/email-validate-hs.git-[branch "master"]-	remote = origin-	merge = refs/heads/master
− .git/description
@@ -1,1 +0,0 @@-Unnamed repository; edit this file 'description' to name the repository.
− .git/hooks/applypatch-msg.sample
@@ -1,15 +0,0 @@-#!/bin/sh-#-# An example hook script to check the commit log message taken by-# applypatch from an e-mail message.-#-# The hook should exit with non-zero status after issuing an-# appropriate message if it wants to stop the commit.  The hook is-# allowed to edit the commit message file.-#-# To enable this hook, rename this file to "applypatch-msg".--. git-sh-setup-test -x "$GIT_DIR/hooks/commit-msg" &&-	exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}-:
− .git/hooks/commit-msg.sample
@@ -1,24 +0,0 @@-#!/bin/sh-#-# An example hook script to check the commit log message.-# Called by "git commit" with one argument, the name of the file-# that has the commit message.  The hook should exit with non-zero-# status after issuing an appropriate message if it wants to stop the-# commit.  The hook is allowed to edit the commit message file.-#-# To enable this hook, rename this file to "commit-msg".--# Uncomment the below to add a Signed-off-by line to the message.-# Doing this in a hook is a bad idea in general, but the prepare-commit-msg-# hook is more suited to it.-#-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"--# This example catches duplicate Signed-off-by lines.--test "" = "$(grep '^Signed-off-by: ' "$1" |-	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {-	echo >&2 Duplicate Signed-off-by lines.-	exit 1-}
− .git/hooks/post-update.sample
@@ -1,8 +0,0 @@-#!/bin/sh-#-# An example hook script to prepare a packed repository for use over-# dumb transports.-#-# To enable this hook, rename this file to "post-update".--exec git update-server-info
− .git/hooks/pre-applypatch.sample
@@ -1,14 +0,0 @@-#!/bin/sh-#-# An example hook script to verify what is about to be committed-# by applypatch from an e-mail message.-#-# The hook should exit with non-zero status after issuing an-# appropriate message if it wants to stop the commit.-#-# To enable this hook, rename this file to "pre-applypatch".--. git-sh-setup-test -x "$GIT_DIR/hooks/pre-commit" &&-	exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}-:
− .git/hooks/pre-commit.sample
@@ -1,50 +0,0 @@-#!/bin/sh-#-# An example hook script to verify what is about to be committed.-# Called by "git commit" with no arguments.  The hook should-# exit with non-zero status after issuing an appropriate message if-# it wants to stop the commit.-#-# To enable this hook, rename this file to "pre-commit".--if git rev-parse --verify HEAD >/dev/null 2>&1-then-	against=HEAD-else-	# Initial commit: diff against an empty tree object-	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904-fi--# If you want to allow non-ascii filenames set this variable to true.-allownonascii=$(git config hooks.allownonascii)--# Redirect output to stderr.-exec 1>&2--# Cross platform projects tend to avoid non-ascii filenames; prevent-# them from being added to the repository. We exploit the fact that the-# printable range starts at the space character and ends with tilde.-if [ "$allownonascii" != "true" ] &&-	# Note that the use of brackets around a tr range is ok here, (it's-	# even required, for portability to Solaris 10's /usr/bin/tr), since-	# the square bracket bytes happen to fall in the designated range.-	test $(git diff --cached --name-only --diff-filter=A -z $against |-	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0-then-	echo "Error: Attempt to add a non-ascii file name."-	echo-	echo "This can cause problems if you want to work"-	echo "with people on other platforms."-	echo-	echo "To be portable it is advisable to rename the file ..."-	echo-	echo "If you know what you are doing you can disable this"-	echo "check using:"-	echo-	echo "  git config hooks.allownonascii true"-	echo-	exit 1-fi--# If there are whitespace errors, print the offending file names and fail.-exec git diff-index --check --cached $against --
− .git/hooks/pre-rebase.sample
@@ -1,169 +0,0 @@-#!/bin/sh-#-# Copyright (c) 2006, 2008 Junio C Hamano-#-# The "pre-rebase" hook is run just before "git rebase" starts doing-# its job, and can prevent the command from running by exiting with-# non-zero status.-#-# The hook is called with the following parameters:-#-# $1 -- the upstream the series was forked from.-# $2 -- the branch being rebased (or empty when rebasing the current branch).-#-# This sample shows how to prevent topic branches that are already-# merged to 'next' branch from getting rebased, because allowing it-# would result in rebasing already published history.--publish=next-basebranch="$1"-if test "$#" = 2-then-	topic="refs/heads/$2"-else-	topic=`git symbolic-ref HEAD` ||-	exit 0 ;# we do not interrupt rebasing detached HEAD-fi--case "$topic" in-refs/heads/??/*)-	;;-*)-	exit 0 ;# we do not interrupt others.-	;;-esac--# Now we are dealing with a topic branch being rebased-# on top of master.  Is it OK to rebase it?--# Does the topic really exist?-git show-ref -q "$topic" || {-	echo >&2 "No such branch $topic"-	exit 1-}--# Is topic fully merged to master?-not_in_master=`git rev-list --pretty=oneline ^master "$topic"`-if test -z "$not_in_master"-then-	echo >&2 "$topic is fully merged to master; better remove it."-	exit 1 ;# we could allow it, but there is no point.-fi--# Is topic ever merged to next?  If so you should not be rebasing it.-only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`-only_next_2=`git rev-list ^master           ${publish} | sort`-if test "$only_next_1" = "$only_next_2"-then-	not_in_topic=`git rev-list "^$topic" master`-	if test -z "$not_in_topic"-	then-		echo >&2 "$topic is already up-to-date with master"-		exit 1 ;# we could allow it, but there is no point.-	else-		exit 0-	fi-else-	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`-	/usr/bin/perl -e '-		my $topic = $ARGV[0];-		my $msg = "* $topic has commits already merged to public branch:\n";-		my (%not_in_next) = map {-			/^([0-9a-f]+) /;-			($1 => 1);-		} split(/\n/, $ARGV[1]);-		for my $elem (map {-				/^([0-9a-f]+) (.*)$/;-				[$1 => $2];-			} split(/\n/, $ARGV[2])) {-			if (!exists $not_in_next{$elem->[0]}) {-				if ($msg) {-					print STDERR $msg;-					undef $msg;-				}-				print STDERR " $elem->[1]\n";-			}-		}-	' "$topic" "$not_in_next" "$not_in_master"-	exit 1-fi--exit 0--################################################################--This sample hook safeguards topic branches that have been-published from being rewound.--The workflow assumed here is:-- * Once a topic branch forks from "master", "master" is never-   merged into it again (either directly or indirectly).-- * Once a topic branch is fully cooked and merged into "master",-   it is deleted.  If you need to build on top of it to correct-   earlier mistakes, a new topic branch is created by forking at-   the tip of the "master".  This is not strictly necessary, but-   it makes it easier to keep your history simple.-- * Whenever you need to test or publish your changes to topic-   branches, merge them into "next" branch.--The script, being an example, hardcodes the publish branch name-to be "next", but it is trivial to make it configurable via-$GIT_DIR/config mechanism.--With this workflow, you would want to know:--(1) ... if a topic branch has ever been merged to "next".  Young-    topic branches can have stupid mistakes you would rather-    clean up before publishing, and things that have not been-    merged into other branches can be easily rebased without-    affecting other people.  But once it is published, you would-    not want to rewind it.--(2) ... if a topic branch has been fully merged to "master".-    Then you can delete it.  More importantly, you should not-    build on top of it -- other people may already want to-    change things related to the topic as patches against your-    "master", so if you need further changes, it is better to-    fork the topic (perhaps with the same name) afresh from the-    tip of "master".--Let's look at this example:--		   o---o---o---o---o---o---o---o---o---o "next"-		  /       /           /           /-		 /   a---a---b A     /           /-		/   /               /           /-	       /   /   c---c---c---c B         /-	      /   /   /             \         /-	     /   /   /   b---b C     \       /-	    /   /   /   /             \     /-    ---o---o---o---o---o---o---o---o---o---o---o "master"---A, B and C are topic branches.-- * A has one fix since it was merged up to "next".-- * B has finished.  It has been fully merged up to "master" and "next",-   and is ready to be deleted.-- * C has not merged to "next" at all.--We would want to allow C to be rebased, refuse A, and encourage-B to be deleted.--To compute (1):--	git rev-list ^master ^topic next-	git rev-list ^master        next--	if these match, topic has not merged in next at all.--To compute (2):--	git rev-list master..topic--	if this is empty, it is fully merged to "master".
− .git/hooks/prepare-commit-msg.sample
@@ -1,36 +0,0 @@-#!/bin/sh-#-# An example hook script to prepare the commit log message.-# Called by "git commit" with the name of the file that has the-# commit message, followed by the description of the commit-# message's source.  The hook's purpose is to edit the commit-# message file.  If the hook fails with a non-zero status,-# the commit is aborted.-#-# To enable this hook, rename this file to "prepare-commit-msg".--# This hook includes three examples.  The first comments out the-# "Conflicts:" part of a merge commit.-#-# The second includes the output of "git diff --name-status -r"-# into the message, just before the "git status" output.  It is-# commented because it doesn't cope with --amend or with squashed-# commits.-#-# The third example adds a Signed-off-by line to the message, that can-# still be edited.  This is rarely a good idea.--case "$2,$3" in-  merge,)-    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;--# ,|template,)-#   /usr/bin/perl -i.bak -pe '-#      print "\n" . `git diff --cached --name-status -r`-#	 if /^#/ && $first++ == 0' "$1" ;;--  *) ;;-esac--# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
− .git/hooks/update.sample
@@ -1,128 +0,0 @@-#!/bin/sh-#-# An example hook script to blocks unannotated tags from entering.-# Called by "git receive-pack" with arguments: refname sha1-old sha1-new-#-# To enable this hook, rename this file to "update".-#-# Config-# -------# hooks.allowunannotated-#   This boolean sets whether unannotated tags will be allowed into the-#   repository.  By default they won't be.-# hooks.allowdeletetag-#   This boolean sets whether deleting tags will be allowed in the-#   repository.  By default they won't be.-# hooks.allowmodifytag-#   This boolean sets whether a tag may be modified after creation. By default-#   it won't be.-# hooks.allowdeletebranch-#   This boolean sets whether deleting branches will be allowed in the-#   repository.  By default they won't be.-# hooks.denycreatebranch-#   This boolean sets whether remotely creating branches will be denied-#   in the repository.  By default this is allowed.-#--# --- Command line-refname="$1"-oldrev="$2"-newrev="$3"--# --- Safety check-if [ -z "$GIT_DIR" ]; then-	echo "Don't run this script from the command line." >&2-	echo " (if you want, you could supply GIT_DIR then run" >&2-	echo "  $0 <ref> <oldrev> <newrev>)" >&2-	exit 1-fi--if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then-	echo "Usage: $0 <ref> <oldrev> <newrev>" >&2-	exit 1-fi--# --- Config-allowunannotated=$(git config --bool hooks.allowunannotated)-allowdeletebranch=$(git config --bool hooks.allowdeletebranch)-denycreatebranch=$(git config --bool hooks.denycreatebranch)-allowdeletetag=$(git config --bool hooks.allowdeletetag)-allowmodifytag=$(git config --bool hooks.allowmodifytag)--# check for no description-projectdesc=$(sed -e '1q' "$GIT_DIR/description")-case "$projectdesc" in-"Unnamed repository"* | "")-	echo "*** Project description file hasn't been set" >&2-	exit 1-	;;-esac--# --- Check types-# if $newrev is 0000...0000, it's a commit to delete a ref.-zero="0000000000000000000000000000000000000000"-if [ "$newrev" = "$zero" ]; then-	newrev_type=delete-else-	newrev_type=$(git cat-file -t $newrev)-fi--case "$refname","$newrev_type" in-	refs/tags/*,commit)-		# un-annotated tag-		short_refname=${refname##refs/tags/}-		if [ "$allowunannotated" != "true" ]; then-			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2-			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2-			exit 1-		fi-		;;-	refs/tags/*,delete)-		# delete tag-		if [ "$allowdeletetag" != "true" ]; then-			echo "*** Deleting a tag is not allowed in this repository" >&2-			exit 1-		fi-		;;-	refs/tags/*,tag)-		# annotated tag-		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1-		then-			echo "*** Tag '$refname' already exists." >&2-			echo "*** Modifying a tag is not allowed in this repository." >&2-			exit 1-		fi-		;;-	refs/heads/*,commit)-		# branch-		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then-			echo "*** Creating a branch is not allowed in this repository" >&2-			exit 1-		fi-		;;-	refs/heads/*,delete)-		# delete branch-		if [ "$allowdeletebranch" != "true" ]; then-			echo "*** Deleting a branch is not allowed in this repository" >&2-			exit 1-		fi-		;;-	refs/remotes/*,commit)-		# tracking branch-		;;-	refs/remotes/*,delete)-		# delete tracking branch-		if [ "$allowdeletebranch" != "true" ]; then-			echo "*** Deleting a tracking branch is not allowed in this repository" >&2-			exit 1-		fi-		;;-	*)-		# Anything else (is there anything else?)-		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2-		exit 1-		;;-esac--# --- Finished-exit 0
− .git/index

binary file changed (352 → absent bytes)

− .git/info/exclude
@@ -1,6 +0,0 @@-# git ls-files --others --exclude-from=.git/info/exclude-# Lines that start with '#' are comments.-# For a project mostly in C, the following would be a good set of-# exclude patterns (uncomment them if you want to use them):-# *.[oa]-# *~
− .git/logs/HEAD
@@ -1,4 +0,0 @@-0000000000000000000000000000000000000000 2353b6eaeee1c824371c12bad78d4053ec52041d George Pollard <porges@porg.es> 1353548294 +1300	clone: from https://github.com/Porges/email-validate-hs.git-2353b6eaeee1c824371c12bad78d4053ec52041d e890c931c46c7b2a4f53db4edcdfbff25943bfb9 George Pollard <porges@porg.es> 1353548540 +1300	revert: Revert "Allow external use"-caabd581f6cf7157ce211a91112cf5047e527be7 6eb270b673a8ae8db8e5e2a5e765065933943523 George Pollard <porges@porg.es> 1353548588 +1300	revert: Revert "revert removal of eof marker"-6eb270b673a8ae8db8e5e2a5e765065933943523 2353b6eaeee1c824371c12bad78d4053ec52041d George Pollard <porges@porg.es> 1353548599 +1300	reset: moving to e890c931c46c7b2a4f53db4edcdfbff25943bfb9^
− .git/logs/refs/heads/master
@@ -1,4 +0,0 @@-0000000000000000000000000000000000000000 2353b6eaeee1c824371c12bad78d4053ec52041d George Pollard <porges@porg.es> 1353548294 +1300	clone: from https://github.com/Porges/email-validate-hs.git-2353b6eaeee1c824371c12bad78d4053ec52041d e890c931c46c7b2a4f53db4edcdfbff25943bfb9 George Pollard <porges@porg.es> 1353548540 +1300	revert: Revert "Allow external use"-caabd581f6cf7157ce211a91112cf5047e527be7 6eb270b673a8ae8db8e5e2a5e765065933943523 George Pollard <porges@porg.es> 1353548588 +1300	revert: Revert "revert removal of eof marker"-6eb270b673a8ae8db8e5e2a5e765065933943523 2353b6eaeee1c824371c12bad78d4053ec52041d George Pollard <porges@porg.es> 1353548599 +1300	reset: moving to e890c931c46c7b2a4f53db4edcdfbff25943bfb9^
− .git/logs/refs/remotes/origin/HEAD
@@ -1,1 +0,0 @@-0000000000000000000000000000000000000000 2353b6eaeee1c824371c12bad78d4053ec52041d George Pollard <porges@porg.es> 1353548294 +1300	clone: from https://github.com/Porges/email-validate-hs.git
− .git/logs/refs/remotes/origin/master
@@ -1,1 +0,0 @@-2353b6eaeee1c824371c12bad78d4053ec52041d 60436f01329fb1e98b20c972b8f2dff34a605e4f George Pollard <porges@porg.es> 1353548635 +1300	update by push
− .git/objects/15/b5b61cf94c1a3f745d249cb27c277506e6b163

binary file changed (47 → absent bytes)

− .git/objects/21/2b997b8e6ea14f49212506a6f24add7af4a497

binary file changed (178 → absent bytes)

− .git/objects/23/53b6eaeee1c824371c12bad78d4053ec52041d

binary file changed (235 → absent bytes)

− .git/objects/24/d18fba10dee5c4ce4aa2af9d83aec2514b3fe8

binary file changed (160 → absent bytes)

− .git/objects/2d/9f07a20bc1f037e1729cef6419adb04e31cb6a
@@ -1,2 +0,0 @@-xRÁjÜ0í5úŠ9åP"9ñ²MY’PºÝ–BI—né]¶&¶@¶„FYÈÇw¤µ7ôZƒ-üf43ï½iœoàövýnÔnàíÁA['Ÿµ³F'ÏÉúñœq­jõQ8ÛâHo÷>¾¬P>YW"?¾ow‡ÐSê}<W€oèc‡°÷Îéh÷¿8§ü†»³éS>҃èý€Awçæ}JaSUs¼jœïª	mLD¢…Ӑd‡à0Ê'M‰ÁG”­Û$ZæÛùx\&ý/IÐqô,-àŸ“0vì@PúÀÜ(łw̆üúº…õª®…Aj£
iÑñj4“uF¦c˜y-AI7ÖÙ´/£uL¨ÑlåÙÁ‡{¸Q„ä§Ø¢Œ<ÙĔ¡GmÄÅ©|g“¸pžå(ó/ËËß~jTë‡j_™5^Eö¤òEނ&êxy£NC8V°Ñ„ÀCÔpy	w°¾‚ #a›±•º†×W¸ç¨ºQï¯ ê‘mÏ¡¼ru©×õ­ôEG®&Ö¢ l/#ÏÊàv¿Ÿ1¦††
6“CÚpYòl¬Úå=W³(þ,eö
− .git/objects/30/795b93e4270a5a2a3df24d1e41b7e10a24614a

binary file changed (56 → absent bytes)

− .git/objects/3c/218af146e571eb4c82060eb761dcdb78d51ce6

binary file changed (160 → absent bytes)

− .git/objects/3c/eee01bed32bce1a61c10fd5f2e1446fa00ce0e

binary file changed (5311 → absent bytes)

− .git/objects/3f/7084c8c996d45e41dd9ddcb0d1e03c6d4693f0
@@ -1,1 +0,0 @@-x+)JMU0¶d040031QKÌÉLI,IÕË(fÈ®³õ{ßϹà_ÛÌbUǝªfwceï
− .git/objects/59/a24bf0dd1195866e5f971ac6d70abdb34a29d3

binary file changed (47 → absent bytes)

− .git/objects/5b/de0de962274e26328ecffabb4e9f6430a10cc9

binary file changed (88 → absent bytes)

− .git/objects/60/436f01329fb1e98b20c972b8f2dff34a605e4f

binary file changed (197 → absent bytes)

− .git/objects/6b/7e3d59554eef8f09a0fe8699732541b92536dd

binary file changed (5283 → absent bytes)

− .git/objects/6e/b270b673a8ae8db8e5e2a5e765065933943523
@@ -1,2 +0,0 @@-xOKj1ëÚ§xd[óü©¥»nKèÞØr2‡7nÎߤsÒ…	I¹Íó©“c~ê-õ…S…‡„ì3¼ˆ•º+É	²
ìGW‘ÌU—NYd,!q}É5rˆ–YvÌls
ƒ6ŽˆF¾û±)½£éè£M“h¡ýõ!—·m±¼»à‚O!%zf7&ÿìøGÔpƒvÚèʊ¹Ýd¢V	wÌ¢gèÆ˜Ïãi¡Õ³ÐÚ÷ç_[óòcv
− .git/objects/70/b31b49e6f1ff5ad7b25c262ecbdc1d6fc0e1c4

binary file changed (56 → absent bytes)

− .git/objects/79/14ad75ef8004bc68244408f494abb69913f35c

binary file changed (161 → absent bytes)

− .git/objects/79/4e34c8dcac0093b4b669bf4847ce4fd4b1fd4c
@@ -1,1 +0,0 @@-xRÁjÜ0íY_1§J$g½¤
KJ·ÛR(ÍÒ
¹KÖÄȖÐÈ!ùøŽµö†^k°…ߌfæ½7ƟoVÝãÞìµóòE{guFñ‚‰\ÎWªV7»z¿÷õðm½€òÙùùõs»û}Ø	=æ.¤sø!µûà½NVp¿!ó‹sÊ¿a¸S6}™…t/ºÐcÔí¹y—sÜTÕ¯Œmu"¡­MH´a’\=&ù¬)óч„²	)a“EÃ|ېŽË¤øš‡ÉÑ>„qCz€Òæ>@9¼e6”áÏ÷-\¯ëZX¤&¹˜ÿ§†·2ãÌûPˆÊÚ8ïò24¾FL®GVÔ3!£Ùʳƒ÷w°RŸ„ÆÔ L¹Ì”¡CmÅ´§­ËåÏ¥ŒÍ‹Ìßn4ª	}µ/¶ÌJ/ë";RÓ]ޓt:–"§Ñ-F,ëh4!ð(5\\À-\_Bԉ°™°µº‚·7¸ã¨Z©—ôÀæO¡iñêR¯íŠš\M>Ô¢ l¯$ÏÊàv¿Ÿ1&ˆ–m¶£GÚpYõÉ^µ›¶]͆ øI÷ˆ
− .git/objects/7a/b9d07f7dc5409bfbd9d154fd1e16ec97360c39

binary file changed (459 → absent bytes)

− .git/objects/7c/cafa1430896619ca670cc5849ba034b871972b

binary file changed (809 → absent bytes)

− .git/objects/83/a5e0534a073a57d8bf04c7957cb88be1a3f675

binary file changed (160 → absent bytes)

− .git/objects/8c/e32485dfe28fdbee865fe42cb2e9992110d022

binary file changed (5277 → absent bytes)

− .git/objects/8d/d5f4f7a7503628bf518fb265434b1017385554

binary file changed (161 → absent bytes)

− .git/objects/a3/62c7ee447b16dedc823854165c0178c1d37d12

binary file changed (136 → absent bytes)

− .git/objects/ab/98b6067c8dea629a6d423255d8f6eca4d9addd

binary file changed (161 → absent bytes)

− .git/objects/ca/abd581f6cf7157ce211a91112cf5047e527be7

binary file changed (203 → absent bytes)

− .git/objects/ce/21b8e64947926b006ca76fe444f5536c355703

binary file changed (182 → absent bytes)

− .git/objects/d5/3be38fca3a8e4e1a736800e156f0e8be0c0d74

binary file changed (56 → absent bytes)

− .git/objects/dc/d45a039547b065e629b98f3a069216070a4032

binary file changed (440 → absent bytes)

− .git/objects/e2/b34424ef1cefd7197309a466e1c98bc940215b

binary file changed (48 → absent bytes)

− .git/objects/e6/ab6abbd47b7761ca51305e98621f6ed101fd4d

binary file changed (48 → absent bytes)

− .git/objects/e8/90c931c46c7b2a4f53db4edcdfbff25943bfb9

binary file changed (213 → absent bytes)

− .git/objects/e8/9c8a1a38fe8ad397e3dcc9b0eeb30bbfb78433

binary file changed (167 → absent bytes)

− .git/objects/f4/d0360b79923043eee16f821b2073b866c56fec

binary file changed (5281 → absent bytes)

− .git/packed-refs
@@ -1,2 +0,0 @@-# pack-refs with: peeled -2353b6eaeee1c824371c12bad78d4053ec52041d refs/remotes/origin/master
− .git/refs/heads/master
@@ -1,1 +0,0 @@-60436f01329fb1e98b20c972b8f2dff34a605e4f
− .git/refs/remotes/origin/HEAD
@@ -1,1 +0,0 @@-ref: refs/remotes/origin/master
− .git/refs/remotes/origin/master
@@ -1,1 +0,0 @@-60436f01329fb1e98b20c972b8f2dff34a605e4f
− Text/Email/Validate.hs
@@ -1,324 +0,0 @@-{-# LANGUAGE CPP #-}
-module Text.Email.Validate (addrSpec,isValid,validate,EmailAddress(..))
-where
-
-import Control.Arrow ((***))
-import qualified Data.Ranges as Range
-import Data.Char (chr)
-
-#if MIN_VERSION_parsec(3,0,0)
-import Text.Parsec
-import Text.Parsec.Char
-#else 
-import Text.ParserCombinators.Parsec
-#endif
-
--- | Constructor does no checking for invalid emails, so use at own risk.
-data EmailAddress = EmailAddress
-	{
-		localPart :: String,
-		domainPart :: String
-	}
-
-instance Show EmailAddress where
-	show (EmailAddress l d) = l ++ ('@' : d)
-
--- | Validates whether a particular string is an email address
---   according to RFC5322.
-isValid :: String -> Bool
-isValid x = let result = validate x in
-	either (const False) (const True) result
-
-simply = (>> return ())
-
--- | If you want to find out why a particular string is not
---   an email address, use this!
-validate :: String -> Either ParseError EmailAddress
-validate x = case parse addrSpec "" x of
-	Right n -> Right $ EmailAddress local domain
-		where (local,at:domain) = splitAt (length x - n) x
-	Left e -> Left e
-
-#if MIN_VERSION_parsec(3,0,0)
-addrSpec :: Parsec String () Int
-#else
-addrSpec :: CharParser () Int
-#endif
-addrSpec = do
-	localPartParser
-	s1 <- getInput
-	char '@'
-	domain
-	eof
-	return (length s1)
-
-localPartParser = dottedAtoms
-domain = dottedAtoms <|> domainLiteral 
-
-dottedAtoms = simply $ (optional cfws >> (atom <|> quotedString) >> optional cfws)
-	`sepBy1` (char '.')
-atom = simply $ many1 atomText
-atomText = simply $ alphaNum <|> oneOf "!#$%&'*+-/=?^_`{|}~"
-
-domainLiteral =  between (optional cfws >> char '[') (char ']' >> optional cfws) $
-	many (optional fws >> domainText) >> optional fws
-domainText = ranges [(33,90),(94,126)] <|> obsNoWsCtl
-
-
-quotedString = between (char '"') (char '"') $
-	many (optional fws >> quotedContent) >> optional fws
-quotedContent = quotedText <|> quotedPair
-quotedText = ranges [(33,33),(35,91),(93,126)] <|> obsNoWsCtl
-quotedPair = char '\\' >> (vchar <|> wsp <|> lf <|> cr <|> obsNoWsCtl <|> nullChar)
-
-fws = (many1 wsp >> optional (crlf >> many1 wsp))
-	<|> (many1 (crlf >> many1 wsp) >> return ())
-
-cfws = simply $ many (comment <|> fws)
-comment = simply $ between (char '(') (char ')') $
-	many (commentContent <|> fws)
-
-commentContent = commentText <|> quotedPair <|> comment
-commentText = ranges [(33,39),(42,91),(93,126)] <|> obsNoWsCtl
-
-nullChar = simply $ char '\0'
-wsp = simply $ oneOf " \t"
-cr = simply $ char '\r'
-lf = simply $ char '\n'
-crlf = simply $ cr >> lf
-vchar = ranges [(0x21,0x7e)]
-obsNoWsCtl = ranges [(1,8),(11,12),(14,31),(127,127)]
-ranges xs = simply $ satisfy (\c -> Range.inRanges c $ Range.ranges $ map (uncurry Range.range . (chr***chr)) $ xs)
-
-unitTest (x, y, z) = if isValid x == y then "" else (x ++": Should be "++show y ++", got "++show (not y)++"\n\t"++z++"\n")
-
-doSomeTests = do
-	putStr$unitTest("first.last@example.com", True, "")
-	putStr$unitTest("1234567890123456789012345678901234567890123456789012345678901234@example.com", True, "")
-	putStr$unitTest("\"first last\"@example.com", True, "")
-	putStr$unitTest("\"first\\\"last\"@example.com", True, "")
-	putStr$unitTest("first\\@last@example.com", False, "Escaping can only happen within a quoted string")
-	putStr$unitTest("\"first@last\"@example.com", True, "")
-	putStr$unitTest("\"first\\\\last\"@example.com", True, "")
-	putStr$unitTest("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, "")
-	putStr$unitTest("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.123456789012345678901234567890123456789012345678901234567890123.example.com", True, "")
-	putStr$unitTest("first.last@[12.34.56.78]", True, "")
-	putStr$unitTest("first.last@[IPv6:::12.34.56.78]", True, "")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]", True, "")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]", True, "")
-	putStr$unitTest("first.last@[IPv6:::1111:2222:3333:4444:5555:6666]", True, "")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333::4444:5555:6666]", True, "")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666::]", True, "")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]", True, "")
-	putStr$unitTest("first.last@x23456789012345678901234567890123456789012345678901234567890123.example.com", True, "")
-	putStr$unitTest("first.last@1xample.com", True, "")
-	putStr$unitTest("first.last@123.example.com", True, "")
-	putStr$unitTest("123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1234.example.com", False, "Entire address is longer than 256 characters")
-	putStr$unitTest("first.last", False, "No @")
-	putStr$unitTest("12345678901234567890123456789012345678901234567890123456789012345@example.com", False, "Local part more than 64 characters")
-	putStr$unitTest(".first.last@example.com", False, "Local part starts with a dot")
-	putStr$unitTest("first.last.@example.com", False, "Local part ends with a dot")
-	putStr$unitTest("first..last@example.com", False, "Local part has consecutive dots")
-	putStr$unitTest("\"first\"last\"@example.com", False, "Local part contains unescaped excluded characters")
-	putStr$unitTest("\"first\\last\"@example.com", True, "Any character can be escaped in a quoted string")
-	putStr$unitTest("\"\"\"@example.com", False, "Local part contains unescaped excluded characters")
-	putStr$unitTest("\"\\\"@example.com", False, "Local part cannot end with a backslash")
-	putStr$unitTest("\"\"@example.com", False, "Local part is effectively empty")
-	putStr$unitTest("first\\\\@last@example.com", False, "Local part contains unescaped excluded characters")
-	putStr$unitTest("first.last@", False, "No domain")
-	putStr$unitTest("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")
-	putStr$unitTest("first.last@[.12.34.56.78]", False, "Only char that can precede IPv4 address is \':\'")
-	putStr$unitTest("first.last@[12.34.56.789]", False, "Can\'t be interpreted as IPv4 so IPv6 tag is missing")
-	putStr$unitTest("first.last@[::12.34.56.78]", False, "IPv6 tag is missing")
-	putStr$unitTest("first.last@[IPv5:::12.34.56.78]", False, "IPv6 tag is wrong")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]", False, "Too many IPv6 groups (4 max)")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]", False, "Not enough IPv6 groups")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]", False, "Too many IPv6 groups (6 max)")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]", False, "Not enough IPv6 groups")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]", False, "Too many IPv6 groups (8 max)")
-	putStr$unitTest("first.last@[IPv6:1111:2222::3333::4444:5555:6666]", False, "Too many \'::\' (can be none or one)")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]", False, "Too many IPv6 groups (6 max)")
-	putStr$unitTest("first.last@[IPv6:1111:2222:333x::4444:5555]", False, "x is not valid in an IPv6 address")
-	putStr$unitTest("first.last@[IPv6:1111:2222:33333::4444:5555]", False, "33333 is not a valid group in an IPv6 address")
-	putStr$unitTest("first.last@example.123", False, "TLD can\'t be all digits")
-	putStr$unitTest("first.last@com", False, "Mail host must be second- or lower level")
-	putStr$unitTest("first.last@-xample.com", False, "Label can\'t begin with a hyphen")
-	putStr$unitTest("first.last@exampl-.com", False, "Label can\'t end with a hyphen")
-	putStr$unitTest("first.last@x234567890123456789012345678901234567890123456789012345678901234.example.com", False, "Label can\'t be longer than 63 octets")
-	putStr$unitTest("\"Abc\\@def\"@example.com", True, "")
-	putStr$unitTest("\"Fred\\ Bloggs\"@example.com", True, "")
-	putStr$unitTest("\"Joe.\\\\Blow\"@example.com", True, "")
-	putStr$unitTest("\"Abc@def\"@example.com", True, "")
-	putStr$unitTest("\"Fred Bloggs\"@example.com", True, "")
-	putStr$unitTest("user+mailbox@example.com", True, "")
-	putStr$unitTest("customer/department=shipping@example.com", True, "")
-	putStr$unitTest("$A12345@example.com", True, "")
-	putStr$unitTest("!def!xyz%abc@example.com", True, "")
-	putStr$unitTest("_somename@example.com", True, "")
-	putStr$unitTest("dclo@us.ibm.com", True, "")
-	putStr$unitTest("abc\\@def@example.com", False, "This example from RFC3696 was corrected in an erratum")
-	putStr$unitTest("abc\\\\@example.com", False, "This example from RFC3696 was corrected in an erratum")
-	putStr$unitTest("peter.piper@example.com", True, "")
-	putStr$unitTest("Doug\\ \\\"Ace\\\"\\ Lovell@example.com", False, "Escaping can only happen in a quoted string")
-	putStr$unitTest("\"Doug \\\"Ace\\\" L.\"@example.com", True, "")
-	putStr$unitTest("abc@def@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("abc\\\\@def@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("abc\\@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("@example.com", False, "No local part")
-	putStr$unitTest("doug@", False, "Doug Lovell says this should fail")
-	putStr$unitTest("\"qu@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("ote\"@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest(".dot@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("dot.@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("two..dot@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("\"Doug \"Ace\" L.\"@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("Doug\\ \\\"Ace\\\"\\ L\\.@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("hello world@example.com", False, "Doug Lovell says this should fail")
-	putStr$unitTest("gatsby@f.sc.ot.t.f.i.tzg.era.l.d.", False, "Doug Lovell says this should fail")
-	putStr$unitTest("test@example.com", True, "")
-	putStr$unitTest("TEST@example.com", True, "")
-	putStr$unitTest("1234567890@example.com", True, "")
-	putStr$unitTest("test+test@example.com", True, "")
-	putStr$unitTest("test-test@example.com", True, "")
-	putStr$unitTest("t*est@example.com", True, "")
-	putStr$unitTest("+1~1+@example.com", True, "")
-	putStr$unitTest("{_test_}@example.com", True, "")
-	putStr$unitTest("\"[[ test ]]\"@example.com", True, "")
-	putStr$unitTest("test.test@example.com", True, "")
-	putStr$unitTest("\"test.test\"@example.com", True, "")
-	putStr$unitTest("test.\"test\"@example.com", True, "Obsolete form, but documented in RFC2822")
-	putStr$unitTest("\"test@test\"@example.com", True, "")
-	putStr$unitTest("test@123.123.123.x123", True, "")
-	putStr$unitTest("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.")
-	putStr$unitTest("test@[123.123.123.123]", True, "")
-	putStr$unitTest("test@example.example.com", True, "")
-	putStr$unitTest("test@example.example.example.com", True, "")
-	putStr$unitTest("test.example.com", False, "")
-	putStr$unitTest("test.@example.com", False, "")
-	putStr$unitTest("test..test@example.com", False, "")
-	putStr$unitTest(".test@example.com", False, "")
-	putStr$unitTest("test@test@example.com", False, "")
-	putStr$unitTest("test@@example.com", False, "")
-	putStr$unitTest("-- test --@example.com", False, "No spaces allowed in local part")
-	putStr$unitTest("[test]@example.com", False, "Square brackets only allowed within quotes")
-	putStr$unitTest("\"test\\test\"@example.com", True, "Any character can be escaped in a quoted string")
-	putStr$unitTest("\"test\"test\"@example.com", False, "Quotes cannot be nested")
-	putStr$unitTest("()[]\\;:,><@example.com", False, "Disallowed Characters")
-	putStr$unitTest("test@.", False, "Dave Child says so")
-	putStr$unitTest("test@example.", False, "Dave Child says so")
-	putStr$unitTest("test@.org", False, "Dave Child says so")
-	putStr$unitTest("test@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com", False, "255 characters is maximum length for domain. This is 256.")
-	putStr$unitTest("test@example", False, "Dave Child says so")
-	putStr$unitTest("test@[123.123.123.123", False, "Dave Child says so")
-	putStr$unitTest("test@123.123.123.123]", False, "Dave Child says so")
-	putStr$unitTest("NotAnEmail", False, "Phil Haack says so")
-	putStr$unitTest("@NotAnEmail", False, "Phil Haack says so")
-	putStr$unitTest("\"test\\\\blah\"@example.com", True, "")
-	putStr$unitTest("\"test\\blah\"@example.com", True, "Any character can be escaped in a quoted string")
-	putStr$unitTest("\"test\\\rblah\"@example.com", True, "Quoted string specifically excludes carriage returns unless escaped")
-	putStr$unitTest("\"test\rblah\"@example.com", False, "Quoted string specifically excludes carriage returns")
-	putStr$unitTest("\"test\\\"blah\"@example.com", True, "")
-	putStr$unitTest("\"test\"blah\"@example.com", False, "Phil Haack says so")
-	putStr$unitTest("customer/department@example.com", True, "")
-	putStr$unitTest("_Yosemite.Sam@example.com", True, "")
-	putStr$unitTest("~@example.com", True, "")
-	putStr$unitTest(".wooly@example.com", False, "Phil Haack says so")
-	putStr$unitTest("wo..oly@example.com", False, "Phil Haack says so")
-	putStr$unitTest("pootietang.@example.com", False, "Phil Haack says so")
-	putStr$unitTest(".@example.com", False, "Phil Haack says so")
-	putStr$unitTest("\"Austin@Powers\"@example.com", True, "")
-	putStr$unitTest("Ima.Fool@example.com", True, "")
-	putStr$unitTest("\"Ima.Fool\"@example.com", True, "")
-	putStr$unitTest("\"Ima Fool\"@example.com", True, "")
-	putStr$unitTest("Ima Fool@example.com", False, "Phil Haack says so")
-	putStr$unitTest("phil.h\\@\\@ck@haacked.com", False, "Escaping can only happen in a quoted string")
-	putStr$unitTest("\"first\".\"last\"@example.com", True, "")
-	putStr$unitTest("\"first\".middle.\"last\"@example.com", True, "")
-	putStr$unitTest("\"first\\\\\"last\"@example.com", False, "Contains an unescaped quote")
-	putStr$unitTest("\"first\".last@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("first.\"last\"@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("\"first\".\"middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("\"first.middle\".\"last\"@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("\"first.middle.last\"@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("\"first..last\"@example.com", True, "obs-local-part form as described in RFC 2822")
-	putStr$unitTest("foo@[\\1.2.3.4]", False, "RFC 5321 specifies the syntax for address-literal and does not allow escaping")
-	putStr$unitTest("\"first\\\\\\\"last\"@example.com", True, "")
-	putStr$unitTest("first.\"mid\\dle\".\"last\"@example.com", True, "Backslash can escape anything but must escape something")
-	putStr$unitTest("Test.\r\n Folding.\r\n Whitespace@example.com", True, "")
-	putStr$unitTest("first.\"\".last@example.com", False, "Contains a zero-length element")
-	putStr$unitTest("first\\last@example.com", False, "Unquoted string must be an atom")
-	putStr$unitTest("Abc\\@def@example.com", False, "Was incorrectly given as a valid address in the original RFC3696")
-	putStr$unitTest("Fred\\ Bloggs@example.com", False, "Was incorrectly given as a valid address in the original RFC3696")
-	putStr$unitTest("Joe.\\\\Blow@example.com", False, "Was incorrectly given as a valid address in the original RFC3696")
-	putStr$unitTest("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]", False, "IPv4 part contains an invalid octet")
-	putStr$unitTest("\"test\\\r\n blah\"@example.com", False, "Folding white space can\'t appear within a quoted pair")
-	putStr$unitTest("\"test\r\n blah\"@example.com", True, "This is a valid quoted string with folding white space")
-	putStr$unitTest("{^c\\@**Dog^}@cartoon.com", False, "This is a throwaway example from Doug Lovell\'s article. Actually it\'s not a valid address.")
-	putStr$unitTest("(foo)cal(bar)@(baz)iamcal.com(quux)", True, "A valid address containing comments")
-	putStr$unitTest("cal@iamcal(woo).(yay)com", True, "A valid address containing comments")
-	putStr$unitTest("\"foo\"(yay)@(hoopla)[1.2.3.4]", False, "Address literal can\'t be commented (RFC5321)")
-	putStr$unitTest("cal(woo(yay)hoopla)@iamcal.com", True, "A valid address containing comments")
-	putStr$unitTest("cal(foo\\@bar)@iamcal.com", True, "A valid address containing comments")
-	putStr$unitTest("cal(foo\\)bar)@iamcal.com", True, "A valid address containing comments and an escaped parenthesis")
-	putStr$unitTest("cal(foo(bar)@iamcal.com", False, "Unclosed parenthesis in comment")
-	putStr$unitTest("cal(foo)bar)@iamcal.com", False, "Too many closing parentheses")
-	putStr$unitTest("cal(foo\\)@iamcal.com", False, "Backslash at end of comment has nothing to escape")
-	putStr$unitTest("first().last@example.com", True, "A valid address containing an empty comment")
-	putStr$unitTest("first.(\r\n middle\r\n )last@example.com", True, "Comment with folding white space")
-	putStr$unitTest("first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890)example.com", False, "Too long with comments, not too long without")
-	putStr$unitTest("first(Welcome to\r\n the (\"wonderful\" (!)) world\r\n of email)@example.com", True, "Silly example from my blog post")
-	putStr$unitTest("pete(his account)@silly.test(his host)", True, "Canonical example from RFC5322")
-	putStr$unitTest("c@(Chris\'s host.)public.example", True, "Canonical example from RFC5322")
-	putStr$unitTest("jdoe@machine(comment).  example", True, "Canonical example from RFC5322")
-	putStr$unitTest("1234   @   local(blah)  .machine .example", True, "Canonical example from RFC5322")
-	putStr$unitTest("first(middle)last@example.com", False, "Can\'t have a comment or white space except at an element boundary")
-	putStr$unitTest("first(abc.def).last@example.com", True, "Comment can contain a dot")
-	putStr$unitTest("first(a\"bc.def).last@example.com", True, "Comment can contain double quote")
-	putStr$unitTest("first.(\")middle.last(\")@example.com", True, "Comment can contain a quote")
-	putStr$unitTest("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")
-	putStr$unitTest("first(abc\\(def)@example.com", True, "Comment can contain quoted-pair")
-	putStr$unitTest("first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com", True, "Label is longer than 63 octets, but not with comment removed")
-	putStr$unitTest("a(a(b(c)d(e(f))g)h(i)j)@example.com", True, "")
-	putStr$unitTest("a(a(b(c)d(e(f))g)(h(i)j)@example.com", False, "Braces are not properly matched")
-	putStr$unitTest("name.lastname@domain.com", True, "")
-	putStr$unitTest(".@", False, "")
-	putStr$unitTest("a@b", False, "")
-	putStr$unitTest("@bar.com", False, "")
-	putStr$unitTest("@@bar.com", False, "")
-	putStr$unitTest("a@bar.com", True, "")
-	putStr$unitTest("aaa.com", False, "")
-	putStr$unitTest("aaa@.com", False, "")
-	putStr$unitTest("aaa@.123", False, "")
-	putStr$unitTest("aaa@[123.123.123.123]", True, "")
-	putStr$unitTest("aaa@[123.123.123.123]a", False, "extra data outside ip")
-	putStr$unitTest("aaa@[123.123.123.333]", False, "not a valid IP")
-	putStr$unitTest("a@bar.com.", False, "")
-	putStr$unitTest("a@bar", False, "")
-	putStr$unitTest("a-b@bar.com", True, "")
-	putStr$unitTest("+@b.c", True, "TLDs can be any length")
-	putStr$unitTest("+@b.com", True, "")
-	putStr$unitTest("a@-b.com", False, "")
-	putStr$unitTest("a@b-.com", False, "")
-	putStr$unitTest("-@..com", False, "")
-	putStr$unitTest("-@a..com", False, "")
-	putStr$unitTest("a@b.co-foo.uk", True, "")
-	putStr$unitTest("\"hello my name is\"@stutter.com", True, "")
-	putStr$unitTest("\"Test \\\"Fail\\\" Ing\"@example.com", True, "")
-	putStr$unitTest("valid@special.museum", True, "")
-	putStr$unitTest("invalid@special.museum-", False, "")
-	putStr$unitTest("shaitan@my-domain.thisisminekthx", True, "Disagree with Paul Gregg here")
-	putStr$unitTest("test@...........com", False, "......")
-	putStr$unitTest("foobar@192.168.0.1", False, "ip need to be []")
-	putStr$unitTest("\"Joe\\\\Blow\"@example.com", True, "")
-	putStr$unitTest("Invalid \\\n Folding \\\n Whitespace@example.com", False, "This isn\'t FWS so Dominic Sayers says it\'s invalid")
-	putStr$unitTest("HM2Kinsists@(that comments are allowed)this.is.ok", True, "")
-	putStr$unitTest("user%uucp!path@somehost.edu", True, "")
-	putStr$unitTest("\"first(last)\"@example.com", True, "")
-	putStr$unitTest(" \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, "")
-	putStr$unitTest("test.\r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines")
-	putStr$unitTest("test. \r\n \r\n obs@syntax.com", True, "obs-fws allows multiple lines (test 2: space before break)")
-	putStr$unitTest("test.\r\n\r\n obs@syntax.com", False, "obs-fws must have at least one WSP per line")
-	putStr$unitTest("\"null \\\0\"@char.com", True, "can have escaped null character")
-	putStr$unitTest("\"null \0\"@char.com", False, "cannot have unescaped null character")
-	
-
email-validate.cabal view
@@ -1,5 +1,5 @@ name:           email-validate
-version:        0.3.2
+version:        1.0.0
 license:        BSD3
 license-file:   LICENSE
 author:         George Pollard
@@ -10,17 +10,31 @@ description:    Validating an email address string against RFC 5322
 build-type:     Simple
 stability:      experimental
-cabal-version:  >= 1.6
-
+cabal-version:  >= 1.8
 
 source-repository head
     type: git
     location: git://github.com/Porges/email-validate-hs.git
 
 library
-    build-depends: base >= 2 && < 5, parsec >= 3.0 || == 2.1.*, ranges >= 0.2.2
+    build-depends: base >= 4 && < 5, attoparsec >= 0.10.0, bytestring >= 0.9
     ghc-options: -O2
-
-    extensions: CPP
+    hs-source-dirs: src
     exposed-modules:
-            Text.Email.Validate
+            Text.Email.Validate, Text.Email.Parser
+
+Test-Suite Main
+    type: exitcode-stdio-1.0
+    main-is: Main.hs
+    hs-source-dirs: tests
+    test-module Test.Text.Email.Validate
+    x-uses-tf: true
+    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
+ src/Text/Email/Parser.hs view
@@ -0,0 +1,100 @@+module Text.Email.Parser
+	(addrSpec
+	,localPart
+	,domainPart
+	,EmailAddress
+	,toByteString)
+where
+
+import Control.Applicative
+
+import qualified Data.ByteString.Char8 as BS
+import Data.ByteString (ByteString)
+
+import Data.Char (chr)
+import Data.Attoparsec.ByteString.Char8
+import Data.Attoparsec.Combinator
+
+-- | Represents an email address.
+data EmailAddress = EmailAddress ByteString ByteString deriving (Eq,Ord)
+
+instance Show EmailAddress where
+	show = BS.unpack . toByteString
+
+-- | Converts an email address back to a ByteString
+toByteString (EmailAddress l d) = BS.concat [l, BS.singleton '@', d]
+
+-- | Extracts the local part of an email address. 
+localPart :: EmailAddress -> ByteString
+localPart (EmailAddress local _) = local
+
+-- | Extracts the domain part of an email address.
+domainPart :: EmailAddress -> ByteString
+domainPart (EmailAddress _ domain) = domain
+
+-- | A parser for email addresses.
+addrSpec = do
+	localPart <- local
+	char '@'
+	domainPart <- domain
+	endOfInput
+	return (EmailAddress localPart domainPart)
+
+local = dottedAtoms
+domain = dottedAtoms <|> domainLiteral 
+
+dottedAtoms = BS.intercalate (BS.singleton '.') <$> 
+	(optional cfws *> (atom <|> quotedString) <* optional cfws)	`sepBy1` (char '.')
+atom = takeWhile1 isAtomText
+
+isAtomText x = isAlphaNum x || inClass "!#$%&'*+/=?^_`{|}~-" x 
+
+domainLiteral = (BS.cons '[' . flip BS.snoc ']' . BS.concat) <$> (between (optional cfws *> char '[') (char ']' <* optional cfws) $
+	many (optional fws >> takeWhile1 isDomainText) <* optional fws)
+isDomainText x = inClass "\33-\90\94-\126" x || isObsNoWsCtl x
+
+quotedString = (\x -> BS.concat $ [BS.singleton '"', BS.concat x, BS.singleton '"']) <$> (between (char '"') (char '"') $
+	many (optional fws >> quotedContent) <* optional fws)
+
+quotedContent = takeWhile1 isQuotedText <|> quotedPair
+isQuotedText x = inClass "\33\35-\91\93-\126" x || isObsNoWsCtl x
+
+quotedPair = (BS.cons '\\' . BS.singleton) <$> (char '\\' *> (vchar <|> wsp <|> lf <|> cr <|> obsNoWsCtl <|> nullChar))
+
+cfws = ignore $ many (comment <|> fws) 
+
+fws :: Parser ()
+fws = ignore $
+	ignore (wsp1 >> optional (crlf >> wsp1))
+	<|> ignore (many1 (crlf >> wsp1))
+
+ignore :: Parser a -> Parser ()
+ignore x = x >> return ()
+
+between l r x = l *> x <* r
+
+comment :: Parser ()
+comment = ignore ((between (char '(') (char ')') $
+	many (ignore commentContent <|> fws)))
+
+commentContent = skipWhile1 isCommentText <|> ignore quotedPair <|> comment
+isCommentText x = inClass "\33-\39\42-\91\93-\126" x || isObsNoWsCtl x
+
+nullChar = char '\0'
+
+skipWhile1 x = satisfy x >> skipWhile x
+
+wsp1 = skipWhile1 isWsp
+wsp = satisfy isWsp
+isWsp x = x == ' ' || x == '\t'
+
+isAlphaNum x = isDigit x || isAlpha_ascii x
+cr = char '\r'
+lf = char '\n'
+crlf = cr >> lf >> return ()
+
+isVchar = inClass "\x21-\x7e"
+vchar = satisfy isVchar
+
+isObsNoWsCtl = inClass "\1-\8\11-\12\14-\31\127"
+obsNoWsCtl = satisfy isObsNoWsCtl
+ src/Text/Email/Validate.hs view
@@ -0,0 +1,39 @@+module Text.Email.Validate
+	(isValid
+	,validate
+	,emailAddress
+	,canonicalizeEmail 
+	,EmailAddress -- re-exported
+	,localPart
+	,domainPart
+	,toByteString) 
+where
+
+import qualified Data.ByteString.Char8 as BS
+import Data.ByteString (ByteString)
+import Data.Attoparsec (parseOnly)
+
+import Text.Email.Parser
+
+-- | Smart constructor for an email address
+emailAddress :: ByteString -> Maybe EmailAddress
+emailAddress x =
+	case validate x of
+		Left _  -> Nothing
+		Right em -> Just em
+
+-- | Checks that an email is valid and returns a version of it
+--   where comments and whitespace have been removed.
+canonicalizeEmail :: ByteString -> Maybe ByteString
+canonicalizeEmail = fmap toByteString . emailAddress
+
+-- | Validates whether a particular string is an email address
+--   according to RFC5322.
+isValid :: ByteString -> Bool
+isValid x = let result = validate x in
+	either (const False) (const True) result
+
+-- | 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
+ tests/Main.hs view
@@ -0,0 +1,281 @@+module Main where
+
+import Data.Maybe (catMaybes)
+import Text.Email.Validate
+import Test.HUnit
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as BS
+
+import Test.Framework as TF (defaultMain, testGroup, Test)
+import Test.Framework.Providers.HUnit
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Test.QuickCheck
+
+main :: IO ()
+main = defaultMain tests
+
+tests :: [TF.Test]
+tests = [
+        testGroup "QuickCheck Text.Email.Validate" [
+                testProperty "doubleCanonicalize" prop_doubleCanonicalize
+                ],
+        testGroup "Unit tests Text.Email.Validate" $ flip concatMap units 
+            (\(em, valid, why) -> 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)
+                    ])
+       ]
+
+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 (\x -> isEmail (BS.pack "example") x)
+        let email = makeEmailLike local domain
+        let (Just result) = emailAddress email
+        return result
+
+isEmail l d = isValid (makeEmailLike l d)
+
+makeEmailLike l d = BS.concat [l, BS.singleton '@', d]
+
+prop_doubleCanonicalize email =  Just email == emailAddress (toByteString 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 = [
+    ("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")
+    -- 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"),
+    ]