1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

policy: Add support for MailboxNaming to ExtractMailbox for #33

This commit is contained in:
James Hillyerd
2018-04-04 20:22:40 -07:00
parent 939ff19991
commit ff2121fbb9
5 changed files with 248 additions and 95 deletions

View File

@@ -17,7 +17,24 @@ type Addressing struct {
// ExtractMailbox extracts the mailbox name from a partial email address.
func (a *Addressing) ExtractMailbox(address string) (string, error) {
return parseMailboxName(address)
local, domain, err := parseEmailAddress(address)
if err != nil {
return "", err
}
local, err = parseMailboxName(local)
if err != nil {
return "", err
}
if a.Config.MailboxNaming == "local" {
return local, nil
}
if domain == "" {
return local, nil
}
if !ValidateDomainPart(domain) {
return "", fmt.Errorf("Domain part %q in %q failed validation", domain, address)
}
return local + "@" + domain, nil
}
// NewRecipient parses an address into a Recipient.
@@ -75,6 +92,69 @@ func (a *Addressing) ShouldStoreDomain(domain string) bool {
// An error is returned if the local or domain parts fail validation following the guidelines
// in RFC3696.
func ParseEmailAddress(address string) (local string, domain string, err error) {
local, domain, err = parseEmailAddress(address)
if err != nil {
return "", "", err
}
if !ValidateDomainPart(domain) {
return "", "", fmt.Errorf("Domain part validation failed")
}
return local, domain, nil
}
// ValidateDomainPart returns true if the domain part complies to RFC3696, RFC1035. Used by
// ParseEmailAddress().
func ValidateDomainPart(domain string) bool {
if len(domain) == 0 {
return false
}
if len(domain) > 255 {
return false
}
if domain[len(domain)-1] != '.' {
domain += "."
}
prev := '.'
labelLen := 0
hasAlphaNum := false
for _, c := range domain {
switch {
case ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') || c == '_':
// Must contain some of these to be a valid label.
hasAlphaNum = true
labelLen++
case c == '-':
if prev == '.' {
// Cannot lead with hyphen.
return false
}
case c == '.':
if prev == '.' || prev == '-' {
// Cannot end with hyphen or double-dot.
return false
}
if labelLen > 63 {
return false
}
if !hasAlphaNum {
return false
}
labelLen = 0
hasAlphaNum = false
default:
// Unknown character.
return false
}
prev = c
}
return true
}
// parseEmailAddress unescapes an email address, and splits the local part from the domain part. An
// error is returned if the local part fails validation following the guidelines in RFC3696. The
// domain part is optional and not validated.
func parseEmailAddress(address string) (local string, domain string, err error) {
if address == "" {
return "", "", fmt.Errorf("Empty address")
}
@@ -185,61 +265,9 @@ LOOP:
if inStringQuote {
return "", "", fmt.Errorf("Cannot end address with unterminated string quote")
}
if !ValidateDomainPart(domain) {
return "", "", fmt.Errorf("Domain part validation failed")
}
return buf.String(), domain, nil
}
// ValidateDomainPart returns true if the domain part complies to RFC3696, RFC1035. Used by
// ParseEmailAddress().
func ValidateDomainPart(domain string) bool {
if len(domain) == 0 {
return false
}
if len(domain) > 255 {
return false
}
if domain[len(domain)-1] != '.' {
domain += "."
}
prev := '.'
labelLen := 0
hasAlphaNum := false
for _, c := range domain {
switch {
case ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') || c == '_':
// Must contain some of these to be a valid label.
hasAlphaNum = true
labelLen++
case c == '-':
if prev == '.' {
// Cannot lead with hyphen.
return false
}
case c == '.':
if prev == '.' || prev == '-' {
// Cannot end with hyphen or double-dot.
return false
}
if labelLen > 63 {
return false
}
if !hasAlphaNum {
return false
}
labelLen = 0
hasAlphaNum = false
default:
// Unknown character.
return false
}
prev = c
}
return true
}
// ParseMailboxName takes a localPart string (ex: "user+ext" without "@domain")
// and returns just the mailbox name (ex: "user"). Returns an error if
// localPart contains invalid characters; it won't accept any that must be