git » chasquid » commit 362ef6f

Introduce an "envelope" package

author Alberto Bertogli
2016-07-19 22:18:40 UTC
committer Alberto Bertogli
2016-07-22 00:44:45 UTC
parent 831ef131325a6aa3ed9be9f9dec10e02c9bf43c3

Introduce an "envelope" package

This patch introduces an "envelope" package which, for now, provides simple
utilities for getting the user and domain of an address.

It also changes the couriers to use it (but other implementations remain, will
be moved over in subsequent patches).

internal/courier/courier.go +2 -22
internal/courier/procmail.go +8 -3
internal/courier/procmail_test.go +4 -2
internal/courier/smtp.go +2 -1
internal/envelope/envelope.go +38 -0
internal/envelope/envelope_test.go +43 -0

diff --git a/internal/courier/courier.go b/internal/courier/courier.go
index 42635f5..a6e2acd 100644
--- a/internal/courier/courier.go
+++ b/internal/courier/courier.go
@@ -1,7 +1,7 @@
 // Package courier implements various couriers for delivering messages.
 package courier
 
-import "strings"
+import "blitiri.com.ar/go/chasquid/internal/envelope"
 
 // Courier delivers mail to a single recipient.
 // It is implemented by different couriers, for both local and remote
@@ -19,30 +19,10 @@ type Router struct {
 }
 
 func (r *Router) Deliver(from string, to string, data []byte) error {
-	d := domainOf(to)
+	d := envelope.DomainOf(to)
 	if r.LocalDomains[d] {
 		return r.Local.Deliver(from, to, data)
 	} else {
 		return r.Remote.Deliver(from, to, data)
 	}
 }
-
-// Split an user@domain address into user and domain.
-func split(addr string) (string, string) {
-	ps := strings.SplitN(addr, "@", 2)
-	if len(ps) != 2 {
-		return addr, ""
-	}
-
-	return ps[0], ps[1]
-}
-
-func userOf(addr string) string {
-	user, _ := split(addr)
-	return user
-}
-
-func domainOf(addr string) string {
-	_, domain := split(addr)
-	return domain
-}
diff --git a/internal/courier/procmail.go b/internal/courier/procmail.go
index c307500..ceeebcd 100644
--- a/internal/courier/procmail.go
+++ b/internal/courier/procmail.go
@@ -8,6 +8,7 @@ import (
 	"time"
 	"unicode"
 
+	"blitiri.com.ar/go/chasquid/internal/envelope"
 	"blitiri.com.ar/go/chasquid/internal/trace"
 )
 
@@ -36,13 +37,17 @@ func (p *Procmail) Deliver(from string, to string, data []byte) error {
 	defer tr.Finish()
 
 	// Get the user, and sanitize to be extra paranoid.
-	user := sanitizeForProcmail(userOf(to))
-	tr.LazyPrintf("%s  ->  %s (%s)", from, user, to)
+	user := sanitizeForProcmail(envelope.UserOf(to))
+	domain := sanitizeForProcmail(envelope.DomainOf(to))
+	tr.LazyPrintf("%s  ->  %s (%s @ %s)", from, user, to, domain)
 
 	// Prepare the command, replacing the necessary arguments.
+	replacer := strings.NewReplacer(
+		"%user%", user,
+		"%domain%", domain)
 	args := []string{}
 	for _, a := range MailDeliveryAgentArgs {
-		args = append(args, strings.Replace(a, "%user%", user, -1))
+		args = append(args, replacer.Replace(a))
 	}
 	cmd := exec.Command(MailDeliveryAgentBin, args...)
 
diff --git a/internal/courier/procmail_test.go b/internal/courier/procmail_test.go
index c806acc..7e69d33 100644
--- a/internal/courier/procmail_test.go
+++ b/internal/courier/procmail_test.go
@@ -19,7 +19,8 @@ func TestProcmail(t *testing.T) {
 	MailDeliveryAgentArgs = []string{dir + "/%user%"}
 
 	p := Procmail{}
-	err = p.Deliver("from@x", "to@y", []byte("data"))
+
+	err = p.Deliver("from@x", "to@local", []byte("data"))
 	if err != nil {
 		t.Fatalf("Deliver: %v", err)
 	}
@@ -36,7 +37,8 @@ func TestProcmailTimeout(t *testing.T) {
 	procmailTimeout = 100 * time.Millisecond
 
 	p := Procmail{}
-	err := p.Deliver("from", "to", []byte("data"))
+
+	err := p.Deliver("from", "to@local", []byte("data"))
 	if err != timeoutError {
 		t.Errorf("Unexpected error: %v", err)
 	}
diff --git a/internal/courier/smtp.go b/internal/courier/smtp.go
index 4750cd1..423cb50 100644
--- a/internal/courier/smtp.go
+++ b/internal/courier/smtp.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/golang/glog"
 
+	"blitiri.com.ar/go/chasquid/internal/envelope"
 	"blitiri.com.ar/go/chasquid/internal/trace"
 )
 
@@ -33,7 +34,7 @@ func (s *SMTP) Deliver(from string, to string, data []byte) error {
 	defer tr.Finish()
 	tr.LazyPrintf("%s  ->  %s", from, to)
 
-	mx, err := lookupMX(domainOf(to))
+	mx, err := lookupMX(envelope.DomainOf(to))
 	if err != nil {
 		return tr.Errorf("Could not find mail server: %v", err)
 	}
diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go
new file mode 100644
index 0000000..c1d4233
--- /dev/null
+++ b/internal/envelope/envelope.go
@@ -0,0 +1,38 @@
+// Package envelope implements functions related to handling email envelopes
+// (basically tuples of (from, to, data).
+package envelope
+
+import (
+	"strings"
+
+	"blitiri.com.ar/go/chasquid/internal/set"
+)
+
+// Split an user@domain address into user and domain.
+func split(addr string) (string, string) {
+	ps := strings.SplitN(addr, "@", 2)
+	if len(ps) != 2 {
+		return addr, ""
+	}
+
+	return ps[0], ps[1]
+}
+
+func UserOf(addr string) string {
+	user, _ := split(addr)
+	return user
+}
+
+func DomainOf(addr string) string {
+	_, domain := split(addr)
+	return domain
+}
+
+func DomainIn(addr string, locals *set.String) bool {
+	domain := DomainOf(addr)
+	if domain == "" {
+		return true
+	}
+
+	return locals.Has(domain)
+}
diff --git a/internal/envelope/envelope_test.go b/internal/envelope/envelope_test.go
new file mode 100644
index 0000000..e8746a7
--- /dev/null
+++ b/internal/envelope/envelope_test.go
@@ -0,0 +1,43 @@
+package envelope
+
+import (
+	"testing"
+
+	"blitiri.com.ar/go/chasquid/internal/set"
+)
+
+func TestSplit(t *testing.T) {
+	cases := []struct {
+		addr, user, domain string
+	}{
+		{"lalala@lelele", "lalala", "lelele"},
+	}
+
+	for _, c := range cases {
+		if user := UserOf(c.addr); user != c.user {
+			t.Errorf("%q: expected user %q, got %q", c.addr, c.user, user)
+		}
+		if domain := DomainOf(c.addr); domain != c.domain {
+			t.Errorf("%q: expected domain %q, got %q",
+				c.addr, c.domain, domain)
+		}
+	}
+}
+
+func TestDomainIn(t *testing.T) {
+	ls := set.NewString("domain1", "domain2")
+	cases := []struct {
+		addr string
+		in   bool
+	}{
+		{"u@domain1", true},
+		{"u@domain2", true},
+		{"u@domain3", false},
+		{"u", true},
+	}
+	for _, c := range cases {
+		if in := DomainIn(c.addr, ls); in != c.in {
+			t.Errorf("%q: expected %v, got %v", c.addr, c.in, in)
+		}
+	}
+}