-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
6 changed files
with
229 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Email Checker | ||
|
||
The ```email``` checker checks if the given string is an email address. If the given string is not a valid email address, the checker will return the ```NOT_EMAIL``` result. Here is an example: | ||
|
||
```golang | ||
type User struct { | ||
Email string `checkers:"email"` | ||
} | ||
|
||
user := &User{ | ||
Email: "[email protected]", | ||
} | ||
|
||
_, valid := Check(user) | ||
if !valid { | ||
// Send the mistakes back to the user | ||
} | ||
``` | ||
|
||
In your custom checkers, you can call the ```ascii``` checker function ```IsAscii``` to validate the user input. Here is an example: | ||
|
||
```golang | ||
result := IsEmail("[email protected]") | ||
|
||
if result != ResultValid { | ||
// Send the mistakes back to the user | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package checker | ||
|
||
import ( | ||
"reflect" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
// CheckerEmail is the name of the checker. | ||
const CheckerEmail = "email" | ||
|
||
// ResultNotFqdn indicates that the given string is not a valid email. | ||
const ResultNotEmail = "NOT_EMAIL" | ||
|
||
// ipV6Prefix is the IPv6 prefix for the domain. | ||
const ipV6Prefix = "[IPv6:" | ||
|
||
// notQuotedChars is the valid not quoted characters. | ||
var notQuotedChars = regexp.MustCompile("[a-zA-Z0-9!#$%&'*+-/=?^_`{|}~]") | ||
|
||
// IsEmail checks if the given string is an email address. | ||
func IsEmail(email string) Result { | ||
atIndex := strings.LastIndex(email, "@") | ||
if atIndex == -1 || atIndex == len(email)-1 { | ||
return ResultNotEmail | ||
} | ||
|
||
domain := email[atIndex+1:] | ||
if isValidEmailDomain(domain) != ResultValid { | ||
return ResultNotEmail | ||
} | ||
|
||
user := email[:atIndex] | ||
|
||
// Cannot be empty user | ||
if len(user) == 0 || len(user) > 64 { | ||
return ResultNotEmail | ||
} | ||
|
||
// Cannot start or end with dot | ||
if user[0] == '.' || user[len(user)-1] == '.' { | ||
return ResultNotEmail | ||
} | ||
|
||
quoted := false | ||
start := true | ||
prev := ' ' | ||
|
||
for _, c := range user { | ||
// Cannot have a double dot unless quoted | ||
if !quoted && c == '.' && prev == '.' { | ||
return ResultNotEmail | ||
} | ||
|
||
if start { | ||
start = false | ||
|
||
if c == '"' { | ||
quoted = true | ||
prev = c | ||
continue | ||
} | ||
} | ||
|
||
if !quoted { | ||
if c == '.' { | ||
start = true | ||
} else if !notQuotedChars.MatchString(string(c)) { | ||
return ResultNotEmail | ||
} | ||
} else { | ||
if c == '"' && prev != '\\' { | ||
quoted = false | ||
} | ||
} | ||
|
||
prev = c | ||
} | ||
|
||
return ResultValid | ||
} | ||
|
||
// makeEmail makes a checker function for the email checker. | ||
func makeEmail(_ string) CheckFunc { | ||
return checkEmail | ||
} | ||
|
||
// checkEmail checks if the given string is an email address. | ||
func checkEmail(value, _ reflect.Value) Result { | ||
if value.Kind() != reflect.String { | ||
panic("string expected") | ||
} | ||
|
||
return IsEmail(value.String()) | ||
} | ||
|
||
// isValidEmailDomain checks if the email domain is a IPv4 or IPv6 address, or a FQDN. | ||
func isValidEmailDomain(domain string) Result { | ||
if len(domain) > 255 { | ||
return ResultNotEmail | ||
} | ||
|
||
if domain[0] == '[' { | ||
if strings.HasPrefix(domain, ipV6Prefix) { | ||
// postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334] | ||
return IsIpV6(domain[len(ipV6Prefix) : len(domain)-1]) | ||
} | ||
|
||
// postmaster@[123.123.123.123] | ||
return IsIpV4(domain[1 : len(domain)-1]) | ||
} | ||
|
||
return IsFqdn(domain) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package checker | ||
|
||
import "testing" | ||
|
||
func TestCheckEmailNonString(t *testing.T) { | ||
defer FailIfNoPanic(t) | ||
|
||
type User struct { | ||
Email int `checkers:"email"` | ||
} | ||
|
||
user := &User{} | ||
|
||
Check(user) | ||
} | ||
|
||
func TestCheckEmailValid(t *testing.T) { | ||
type User struct { | ||
Email string `checkers:"email"` | ||
} | ||
|
||
user := &User{ | ||
Email: "[email protected]", | ||
} | ||
|
||
_, valid := Check(user) | ||
if !valid { | ||
t.Fail() | ||
} | ||
} | ||
|
||
func TestIsEmailValid(t *testing.T) { | ||
validEmails := []string{ | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"test/[email protected]", | ||
"[email protected]", | ||
"\" \"@example.org", | ||
"\"john..doe\"@example.org", | ||
"[email protected]", | ||
"\"very.(),:;<>[]\\\".VERY.\\\"very@\\\\ \\\"very\\\".unusual\"@strange.example.com", | ||
"user%[email protected]", | ||
"[email protected]", | ||
"postmaster@[123.123.123.123]", | ||
"postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]", | ||
} | ||
|
||
for _, email := range validEmails { | ||
if IsEmail(email) != ResultValid { | ||
t.Fatal(email) | ||
} | ||
} | ||
} | ||
|
||
func TestIsEmailInvalid(t *testing.T) { | ||
validEmails := []string{ | ||
"Abc.example.com", | ||
"A@b@[email protected]", | ||
"a\"b(c)d,e:f;g<h>i[j\\k][email protected]", | ||
"just\"not\"[email protected]", | ||
"this is\"not\\[email protected]", | ||
"this\\ still\\\"not\\\\[email protected]", | ||
"1234567890123456789012345678901234567890123456789012345678901234+x@example.com", | ||
"i_like_underscore@but_its_not_allowed_in_this_part.example.com", | ||
"QA[icon]CHOCOLATE[icon]@test.com", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"user@domaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255charactersdomaincannotbemorethan255characters.com", | ||
} | ||
|
||
for _, email := range validEmails { | ||
if IsEmail(email) == ResultValid { | ||
t.Fatal(email) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters