diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce2e03..b1c157c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). [Unreleased] ------------ +### Added +- Storage of To: header in messages (likely breaks existing datastores) + ### Fixed - We should no longer run out of file handles when dealing with a large number of recipients on a single message. diff --git a/etc/rest-apiv1.sh b/etc/rest-apiv1.sh index 0a30433..880bb99 100755 --- a/etc/rest-apiv1.sh +++ b/etc/rest-apiv1.sh @@ -103,7 +103,7 @@ main() { esac # Use jq to pretty-print if installed and we are expecting JSON output - if [ $pretty ] && [ $is_json ] && type -P jq; then + if [ $pretty ] && [ $is_json ] && type -P jq >/dev/null; then curl -s $curl_opts -H "Accept: application/json" --noproxy "$API_HOST" -X "$method" "$url" | jq . else curl -s $curl_opts -H "Accept: application/json" --noproxy "$API_HOST" -X "$method" "$url" diff --git a/rest/apiv1_controller.go b/rest/apiv1_controller.go index 365b838..5e5d543 100644 --- a/rest/apiv1_controller.go +++ b/rest/apiv1_controller.go @@ -17,6 +17,7 @@ type JSONMessageHeaderV1 struct { Mailbox string `json:"mailbox"` ID string `json:"id"` From string `json:"from"` + To []string `json:"to"` Subject string `json:"subject"` Date time.Time `json:"date"` Size int64 `json:"size"` @@ -27,6 +28,7 @@ type JSONMessageV1 struct { Mailbox string `json:"mailbox"` ID string `json:"id"` From string `json:"from"` + To []string `json:"to"` Subject string `json:"subject"` Date time.Time `json:"date"` Size int64 `json:"size"` @@ -65,6 +67,7 @@ func MailboxListV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) Mailbox: name, ID: msg.ID(), From: msg.From(), + To: msg.To(), Subject: msg.Subject(), Date: msg.Date(), Size: msg.Size(), @@ -109,6 +112,7 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) Mailbox: name, ID: msg.ID(), From: msg.From(), + To: msg.To(), Subject: msg.Subject(), Date: msg.Date(), Size: msg.Size(), diff --git a/rest/apiv1_controller_test.go b/rest/apiv1_controller_test.go index 74aaa4e..9297f88 100644 --- a/rest/apiv1_controller_test.go +++ b/rest/apiv1_controller_test.go @@ -19,6 +19,7 @@ const ( mailboxKey = "mailbox" idKey = "id" fromKey = "from" + toKey = "to" subjectKey = "subject" dateKey = "date" sizeKey = "size" @@ -94,6 +95,7 @@ func TestRestMailboxList(t *testing.T) { Mailbox: "good", ID: "0001", From: "from1", + To: []string{"to1"}, Subject: "subject 1", Date: time.Date(2012, 2, 1, 10, 11, 12, 253, time.FixedZone("PST", -800)), } @@ -101,6 +103,7 @@ func TestRestMailboxList(t *testing.T) { Mailbox: "good", ID: "0002", From: "from2", + To: []string{"to1"}, Subject: "subject 2", Date: time.Date(2012, 7, 1, 10, 11, 12, 253, time.FixedZone("PDT", -700)), } diff --git a/rest/testmocks_test.go b/rest/testmocks_test.go index 0c5c610..30a46bf 100644 --- a/rest/testmocks_test.go +++ b/rest/testmocks_test.go @@ -70,6 +70,11 @@ func (m *MockMessage) From() string { return args.String(0) } +func (m *MockMessage) To() []string { + args := m.Called() + return args.Get(0).([]string) +} + func (m *MockMessage) Date() time.Time { args := m.Called() return args.Get(0).(time.Time) diff --git a/rest/testutils_test.go b/rest/testutils_test.go index d63e1ce..3d4f86d 100644 --- a/rest/testutils_test.go +++ b/rest/testutils_test.go @@ -17,6 +17,7 @@ import ( type InputMessageData struct { Mailbox, ID, From, Subject string + To []string Date time.Time Size int Header mail.Header @@ -27,6 +28,7 @@ func (d *InputMessageData) MockMessage() *MockMessage { msg := &MockMessage{} msg.On("ID").Return(d.ID) msg.On("From").Return(d.From) + msg.On("To").Return(d.To) msg.On("Subject").Return(d.Subject) msg.On("Date").Return(d.Date) msg.On("Size").Return(d.Size) @@ -79,6 +81,11 @@ func (d *InputMessageData) CompareToJSONHeaderMap(json interface{}) (errors []st if msg, ok := isJSONStringEqual(fromKey, d.From, m[fromKey]); !ok { errors = append(errors, msg) } + for i, inputTo := range d.To { + if msg, ok := isJSONStringEqual(toKey, inputTo, m[toKey].([]interface{})[i]); !ok { + errors = append(errors, msg) + } + } if msg, ok := isJSONStringEqual(subjectKey, d.Subject, m[subjectKey]); !ok { errors = append(errors, msg) } diff --git a/smtpd/datastore.go b/smtpd/datastore.go index ea1256d..746be78 100644 --- a/smtpd/datastore.go +++ b/smtpd/datastore.go @@ -36,6 +36,7 @@ type Mailbox interface { type Message interface { ID() string From() string + To() []string Date() time.Time Subject() string RawReader() (reader io.ReadCloser, err error) diff --git a/smtpd/filestore.go b/smtpd/filestore.go index dc46141..b40869f 100644 --- a/smtpd/filestore.go +++ b/smtpd/filestore.go @@ -295,6 +295,7 @@ type FileMessage struct { Fid string Fdate time.Time Ffrom string + Fto []string Fsubject string Fsize int64 // These are for creating new messages only @@ -343,6 +344,11 @@ func (m *FileMessage) From() string { return m.Ffrom } +// From returns the value of the Message To header +func (m *FileMessage) To() []string { + return m.Fto +} + // Subject returns the value of the Message Subject header func (m *FileMessage) Subject() string { return m.Fsubject @@ -484,10 +490,24 @@ func (m *FileMessage) Close() error { return err } - // Only public fields are stored in gob - m.Ffrom = body.GetHeader("From") + // Only public fields are stored in gob, hence starting with capital F + // Parse From address + if address, err := mail.ParseAddress(body.GetHeader("From")); err == nil { + m.Ffrom = address.String() + } else { + m.Ffrom = body.GetHeader("From") + } m.Fsubject = body.GetHeader("Subject") + // Turn the To header into a slice + if addresses, err := body.AddressList("To"); err == nil { + for _, a := range addresses { + m.Fto = append(m.Fto, a.String()) + } + } else { + m.Fto = []string{body.GetHeader("To")} + } + // Refresh the index before adding our message err = m.mailbox.readIndex() if err != nil { diff --git a/smtpd/retention_test.go b/smtpd/retention_test.go index 4c32780..1344498 100644 --- a/smtpd/retention_test.go +++ b/smtpd/retention_test.go @@ -126,6 +126,11 @@ func (m *MockMessage) From() string { return args.String(0) } +func (m *MockMessage) To() []string { + args := m.Called() + return args.Get(0).([]string) +} + func (m *MockMessage) Date() time.Time { args := m.Called() return args.Get(0).(time.Time) diff --git a/swaks-tests/run-tests.sh b/swaks-tests/run-tests.sh index 62e6bd3..f7c3564 100755 --- a/swaks-tests/run-tests.sh +++ b/swaks-tests/run-tests.sh @@ -3,6 +3,7 @@ # description: Generate test emails for Inbucket set -eo pipefail +[ $TRACE ] && set -x # We need to be in swaks-tests directory cmdpath="$(dirname "$0")" @@ -19,6 +20,7 @@ case "$1" in ;; *) to="$1" + shift ;; esac @@ -28,6 +30,10 @@ export SWAKS_OPT_to="$to@inbucket.local" # Basic test swaks $* --h-Subject: "Swaks Plain Text" --body text.txt +# Multi-recipient test +swaks $* --to="$to@inbucket.local,Alt User " --h-Subject: "Swaks Multi-Recipient" \ + --body text.txt + # HTML test swaks $* --h-Subject: "Swaks HTML" --data mime-html.raw @@ -35,7 +41,8 @@ swaks $* --h-Subject: "Swaks HTML" --data mime-html.raw swaks $* --h-Subject: "Swaks Top Level HTML" --data nonmime-html.raw # Attachment test -swaks $* --h-Subject: "Swaks Attachment" --attach-type image/png --attach favicon.png --body text.txt +swaks $* --h-Subject: "Swaks Attachment" --attach-type image/png --attach favicon.png \ + --body text.txt # Encoded subject line test swaks $* --data utf8-subject.raw diff --git a/themes/bootstrap/templates/mailbox/_show.html b/themes/bootstrap/templates/mailbox/_show.html index 3825e5c..474e0cf 100644 --- a/themes/bootstrap/templates/mailbox/_show.html +++ b/themes/bootstrap/templates/mailbox/_show.html @@ -52,6 +52,13 @@
From:
{{.message.From}}
+
To:
+
+ {{range $i, $addr := .message.To}} + {{- if $i}},{{end}} + {{$addr -}} + {{end}} +
Date:
{{.message.Date}}
Subject:
diff --git a/themes/bootstrap/templates/mailbox/index.html b/themes/bootstrap/templates/mailbox/index.html index 6644fd4..2059562 100644 --- a/themes/bootstrap/templates/mailbox/index.html +++ b/themes/bootstrap/templates/mailbox/index.html @@ -15,9 +15,9 @@ $(document).ready(function() {