mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-18 18:17:03 +00:00
Initial version of ValidateLocalPart
Does not support any quoting yet, not RFC compliant
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package smtpd
|
package smtpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -89,3 +90,40 @@ func ValidateDomainPart(domain string) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateLocalPart returns true if the string complies with RFC3696 recommendations
|
||||||
|
func ValidateLocalPart(local string) bool {
|
||||||
|
length := len(local)
|
||||||
|
if 1 > length || length > 64 {
|
||||||
|
// Invalid length
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if local[length-1] == '.' {
|
||||||
|
// Cannot end with a period
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
prev := byte('.')
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
c := local[i]
|
||||||
|
switch {
|
||||||
|
case ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'):
|
||||||
|
// Letters are OK
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
// Numbers are OK
|
||||||
|
case bytes.IndexByte([]byte("!#$%&'*+-/=?^_`{|}~"), c) >= 0:
|
||||||
|
// These specials can be used unquoted
|
||||||
|
case c == '.':
|
||||||
|
// A single period is OK
|
||||||
|
if prev == '.' {
|
||||||
|
// Sequence of periods is not permitted
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prev = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ func TestHashMailboxName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateDomain(t *testing.T) {
|
func TestValidateDomain(t *testing.T) {
|
||||||
assert.True(t, ValidateDomainPart("jhillyerd.github.com"),
|
|
||||||
"Simple domain failed")
|
|
||||||
assert.False(t, ValidateDomainPart(""), "Empty domain is not valid")
|
|
||||||
assert.False(t, ValidateDomainPart(strings.Repeat("a", 256)),
|
assert.False(t, ValidateDomainPart(strings.Repeat("a", 256)),
|
||||||
"Max domain length is 255")
|
"Max domain length is 255")
|
||||||
assert.False(t, ValidateDomainPart(strings.Repeat("a", 64)+".com"),
|
assert.False(t, ValidateDomainPart(strings.Repeat("a", 64)+".com"),
|
||||||
@@ -32,6 +29,7 @@ func TestValidateDomain(t *testing.T) {
|
|||||||
expect bool
|
expect bool
|
||||||
msg string
|
msg string
|
||||||
}{
|
}{
|
||||||
|
{"", false, "Empty domain is not valid"},
|
||||||
{"hostname", true, "Just a hostname is valid"},
|
{"hostname", true, "Just a hostname is valid"},
|
||||||
{"github.com", true, "Two labels should be just fine"},
|
{"github.com", true, "Two labels should be just fine"},
|
||||||
{"my-domain.com", true, "Hyphen is allowed mid-label"},
|
{"my-domain.com", true, "Hyphen is allowed mid-label"},
|
||||||
@@ -52,3 +50,34 @@ func TestValidateDomain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateLocal(t *testing.T) {
|
||||||
|
var testTable = []struct {
|
||||||
|
input string
|
||||||
|
expect bool
|
||||||
|
msg string
|
||||||
|
}{
|
||||||
|
{"", false, "Empty local is not valid"},
|
||||||
|
{"a", true, "Single letter should be fine"},
|
||||||
|
{strings.Repeat("a", 65), false, "Only valid up to 64 characters"},
|
||||||
|
{"FirstLast", true, "Mixed case permitted"},
|
||||||
|
{"user123", true, "Numbers permitted"},
|
||||||
|
{"a!#$%&'*+-/=?^_`{|}~", true, "Any of !#$%&'*+-/=?^_`{|}~ are permitted"},
|
||||||
|
{"james@mail", false, "Unquoted @ not permitted"},
|
||||||
|
{"first.last", true, "Embedded period is permitted"},
|
||||||
|
{"first..last", false, "Sequence of periods is not allowed"},
|
||||||
|
{".user", false, "Cannot lead with a period"},
|
||||||
|
{"user.", false, "Cannot end with a period"},
|
||||||
|
{"user+mailbox", true, "RFC3696 test case should be valid"},
|
||||||
|
{"customer/department=shipping", true, "RFC3696 test case should be valid"},
|
||||||
|
{"$A12345", true, "RFC3696 test case should be valid"},
|
||||||
|
{"!def!xyz%abc", true, "RFC3696 test case should be valid"},
|
||||||
|
{"_somename", true, "RFC3696 test case should be valid"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testTable {
|
||||||
|
if ValidateLocalPart(tt.input) != tt.expect {
|
||||||
|
t.Errorf("Expected %v for %q: %s", tt.expect, tt.input, tt.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user