diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b617664a..481e90fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ - Changed `System.GetFile()` to return directly `*blob.Reader` instead of the `io.ReadCloser` interface. +- **!** Changed `To`, `Cc` and `Bcc` of `mailer.Message` to `[]mail.Address` for consistency and to allow multiple recipients and optional name. + + If you are sending custom emails, you'll have to replace: + ```go + message := &mailer.Message{ + ... + + // (old) To: mail.Address{Address: "test@example.com"} + To: []mail.Address{{Address: "test@example.com", Name: "Some optional name"}} + + // (old) Cc: []string{"test@example.com"} + Cc: []mail.Address{{Address: "cc@example.com", Name: "Some optional name"}} + + // (old) Bcc: []string{"test@example.com"} + Bcc: []mail.Address{{Address: "cc@example.com", Name: "Some optional name"}} + + ... + } + ``` + ## v0.12.1 diff --git a/apis/settings_test.go b/apis/settings_test.go index eb4645500..a13cf24be 100644 --- a/apis/settings_test.go +++ b/apis/settings_test.go @@ -318,8 +318,12 @@ func TestSettingsTestEmail(t *testing.T) { t.Fatalf("[verification] Expected 1 sent email, got %d", app.TestMailer.TotalSend) } - if app.TestMailer.LastMessage.To.Address != "test@example.com" { - t.Fatalf("[verification] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To.Address) + if len(app.TestMailer.LastMessage.To) != 1 { + t.Fatalf("[verification] Expected 1 recipient, got %v", app.TestMailer.LastMessage.To) + } + + if app.TestMailer.LastMessage.To[0].Address != "test@example.com" { + t.Fatalf("[verification] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To[0].Address) } if !strings.Contains(app.TestMailer.LastMessage.HTML, "Verify") { @@ -349,8 +353,12 @@ func TestSettingsTestEmail(t *testing.T) { t.Fatalf("[password-reset] Expected 1 sent email, got %d", app.TestMailer.TotalSend) } - if app.TestMailer.LastMessage.To.Address != "test@example.com" { - t.Fatalf("[password-reset] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To.Address) + if len(app.TestMailer.LastMessage.To) != 1 { + t.Fatalf("[password-reset] Expected 1 recipient, got %v", app.TestMailer.LastMessage.To) + } + + if app.TestMailer.LastMessage.To[0].Address != "test@example.com" { + t.Fatalf("[password-reset] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To[0].Address) } if !strings.Contains(app.TestMailer.LastMessage.HTML, "Reset password") { @@ -380,8 +388,12 @@ func TestSettingsTestEmail(t *testing.T) { t.Fatalf("[email-change] Expected 1 sent email, got %d", app.TestMailer.TotalSend) } - if app.TestMailer.LastMessage.To.Address != "test@example.com" { - t.Fatalf("[email-change] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To.Address) + if len(app.TestMailer.LastMessage.To) != 1 { + t.Fatalf("[email-change] Expected 1 recipient, got %v", app.TestMailer.LastMessage.To) + } + + if app.TestMailer.LastMessage.To[0].Address != "test@example.com" { + t.Fatalf("[email-change] Expected the email to be sent to %s, got %s", "test@example.com", app.TestMailer.LastMessage.To[0].Address) } if !strings.Contains(app.TestMailer.LastMessage.HTML, "Confirm new email") { diff --git a/go.mod b/go.mod index 8a3549abf..fd57e069f 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.18 require ( github.com/AlecAivazis/survey/v2 v2.3.6 - github.com/aws/aws-sdk-go v1.44.187 + github.com/aws/aws-sdk-go v1.44.192 github.com/disintegration/imaging v1.6.2 - github.com/domodwyer/mailyak/v3 v3.3.4 + github.com/domodwyer/mailyak/v3 v3.3.5 github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86 github.com/dop251/goja_nodejs v0.0.0-20221009164102-3aa5028e57f6 github.com/fatih/color v1.14.1 @@ -31,10 +31,10 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.10 // indirect + github.com/aws/aws-sdk-go-v2/config v1.18.11 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.11 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.49 // indirect + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.50 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect @@ -62,7 +62,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect @@ -75,9 +75,9 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.108.0 // indirect + google.golang.org/api v0.109.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa // indirect + google.golang.org/genproto v0.0.0-20230131230820-1c016267d619 // indirect google.golang.org/grpc v1.52.3 // indirect google.golang.org/protobuf v1.28.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect diff --git a/go.sum b/go.sum index ec476c69b..c10af33c6 100644 --- a/go.sum +++ b/go.sum @@ -517,8 +517,8 @@ github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4 github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.128/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.151/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go v1.44.187 h1:D5CsRomPnlwDHJCanL2mtaLIcbhjiWxNh5j8zvaWdJA= -github.com/aws/aws-sdk-go v1.44.187/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.192 h1:KL54vCxRd5v5XBGjnF3FelzXXwl+aWHDmDTihFmRNgM= +github.com/aws/aws-sdk-go v1.44.192/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= @@ -527,17 +527,17 @@ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9/go.mod h1:vCmV1q1VK github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/config v1.18.3/go.mod h1:BYdrbeCse3ZnOD5+2/VE/nATOK8fEUpBtmPMdKSyhMU= -github.com/aws/aws-sdk-go-v2/config v1.18.10 h1:Znce11DWswdh+5kOsIp+QaNfY9igp1QUN+fZHCKmeCI= -github.com/aws/aws-sdk-go-v2/config v1.18.10/go.mod h1:VATKco+pl+Qe1WW+RzvZTlPPe/09Gg9+vM0ZXsqb16k= +github.com/aws/aws-sdk-go-v2/config v1.18.11 h1:7dJD4p90OyKYIihuwe/LbHfP7uw4yVm5P1hel+b8UZ8= +github.com/aws/aws-sdk-go-v2/config v1.18.11/go.mod h1:FTGKr2F7QL7IAg22dUmEB5NWpLPAOuhrONzXe7TVhAI= github.com/aws/aws-sdk-go-v2/credentials v1.13.3/go.mod h1:/rOMmqYBcFfNbRPU0iN9IgGqD5+V2yp3iWNmIlz0wI4= -github.com/aws/aws-sdk-go-v2/credentials v1.13.10 h1:T4Y39IhelTLg1f3xiKJssThnFxsndS8B6OnmcXtKK+8= -github.com/aws/aws-sdk-go-v2/credentials v1.13.10/go.mod h1:tqAm4JmQaShel+Qi38hmd1QglSnnxaYt50k/9yGQzzc= +github.com/aws/aws-sdk-go-v2/credentials v1.13.11 h1:QnvlTut1XXKkX4aaM1Ydo5X0CHriv0jmLu8PTVQQJJo= +github.com/aws/aws-sdk-go-v2/credentials v1.13.11/go.mod h1:tqAm4JmQaShel+Qi38hmd1QglSnnxaYt50k/9yGQzzc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.42/go.mod h1:LHOsygMiW/14CkFxdXxvzKyMh3jbk/QfZVaDtCbLkl8= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.49 h1:zPFhadkmXbXu3RVXTPU4HVW+g2DStMY+01cJaj//+Cw= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.49/go.mod h1:N9gSChQkKpdAj7vRpfKma4ND88zoZM+v6W2lJgWrDh4= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.50 h1:ATgzvd5DaU0Evx7yvaUw2ftwiWDGnDN59zowPF3jDk0= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.50/go.mod h1:naA7bah2/dpvwlyWysZ7yaAYI1Ti73HPaDyGryfJuiU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= @@ -817,8 +817,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/domodwyer/mailyak/v3 v3.3.4 h1:AG/pvcz2/ocFqZkPEG7lPAa0MhCq1warfUEKJt6Fagk= -github.com/domodwyer/mailyak/v3 v3.3.4/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= +github.com/domodwyer/mailyak/v3 v3.3.5 h1:4rQV4ADpFIizCR8VVCASO+7bLof3aPhdFGnzCrXMDRc= +github.com/domodwyer/mailyak/v3 v3.3.5/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20220815083517-0c74f9139fd6/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86 h1:E2wycakfddWJ26v+ZyEY91Lb/HEZyaiZhbMX+KQcdmc= @@ -1640,8 +1640,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2/go.mod h1:7jOTMgqac46PZcF54q6l2hkLEG8op93fZu61KmxWDV4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 h1:VstopitMQi3hZP0fzvnsLmzXZdQGc4bEcgu24cp+d4M= -github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -2457,8 +2457,8 @@ google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91 google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.108.0 h1:WVBc/faN0DkKtR43Q/7+tPny9ZoLZdIiAyG5Q9vFClg= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.109.0 h1:sW9hgHyX497PP5//NUM7nqfV8D0iDfBApqq7sOh1XR8= +google.golang.org/api v0.109.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2592,8 +2592,8 @@ google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZV google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa h1:qQPhfbPO23fwm/9lQr91L1u62Zo6cm+zI+slZT+uf+o= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230131230820-1c016267d619 h1:p0kMzw6AG0JEzd7Z+kXqOiLhC6gjUQTbtS2zR0Q3DbI= +google.golang.org/genproto v0.0.0-20230131230820-1c016267d619/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/mails/admin.go b/mails/admin.go index 2de8d051c..4c5e6cc56 100644 --- a/mails/admin.go +++ b/mails/admin.go @@ -56,7 +56,7 @@ func SendAdminPasswordReset(app core.App, admin *models.Admin) error { Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, - To: mail.Address{Address: admin.Email}, + To: []mail.Address{{Address: admin.Email}}, Subject: "Reset admin password", HTML: body, } diff --git a/mails/record.go b/mails/record.go index 7caebbd76..e848bd8ba 100644 --- a/mails/record.go +++ b/mails/record.go @@ -32,7 +32,7 @@ func SendRecordPasswordReset(app core.App, authRecord *models.Record) error { Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, - To: mail.Address{Address: authRecord.Email()}, + To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } @@ -76,7 +76,7 @@ func SendRecordVerification(app core.App, authRecord *models.Record) error { Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, - To: mail.Address{Address: authRecord.Email()}, + To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } @@ -120,7 +120,7 @@ func SendRecordChangeEmail(app core.App, record *models.Record, newEmail string) Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, - To: mail.Address{Address: newEmail}, + To: []mail.Address{{Address: newEmail}}, Subject: subject, HTML: body, } diff --git a/tools/mailer/mailer.go b/tools/mailer/mailer.go index d46ac4ea6..eb9cb8dcb 100644 --- a/tools/mailer/mailer.go +++ b/tools/mailer/mailer.go @@ -8,9 +8,9 @@ import ( // Message defines a generic email message struct. type Message struct { From mail.Address - To mail.Address - Bcc []string - Cc []string + To []mail.Address + Bcc []mail.Address + Cc []mail.Address Subject string HTML string Text string @@ -23,3 +23,21 @@ type Mailer interface { // Send sends an email with the provided Message. Send(message *Message) error } + +// addressesToStrings converts the provided address to a list of serialized RFC 5322 strings. +// +// To export only the email part of mail.Address, you can set withName to false. +func addressesToStrings(addresses []mail.Address, withName bool) []string { + result := make([]string, len(addresses)) + + for i, addr := range addresses { + if withName && addr.Name != "" { + result[i] = addr.String() + } else { + // keep only the email part to avoid wrapping in angle-brackets + result[i] = addr.Address + } + } + + return result +} diff --git a/tools/mailer/sendmail.go b/tools/mailer/sendmail.go index b40ef643a..6a40523c7 100644 --- a/tools/mailer/sendmail.go +++ b/tools/mailer/sendmail.go @@ -6,6 +6,7 @@ import ( "mime" "net/http" "os/exec" + "strings" ) var _ Mailer = (*Sendmail)(nil) @@ -19,11 +20,13 @@ type Sendmail struct { // Send implements `mailer.Mailer` interface. func (c *Sendmail) Send(m *Message) error { + toAddresses := addressesToStrings(m.To, false) + headers := make(http.Header) headers.Set("Subject", mime.QEncoding.Encode("utf-8", m.Subject)) headers.Set("From", m.From.String()) - headers.Set("To", m.To.String()) headers.Set("Content-Type", "text/html; charset=UTF-8") + headers.Set("To", strings.Join(toAddresses, ",")) cmdPath, err := findSendmailPath() if err != nil { @@ -51,7 +54,7 @@ func (c *Sendmail) Send(m *Message) error { } // --- - sendmail := exec.Command(cmdPath, m.To.Address) + sendmail := exec.Command(cmdPath, strings.Join(toAddresses, ",")) sendmail.Stdin = &buffer return sendmail.Run() diff --git a/tools/mailer/smtp.go b/tools/mailer/smtp.go index 7c81e8994..14017f4ff 100644 --- a/tools/mailer/smtp.go +++ b/tools/mailer/smtp.go @@ -75,7 +75,6 @@ func (c *SmtpClient) Send(m *Message) error { yak.FromName(m.From.Name) } yak.From(m.From.Address) - yak.To(m.To.Address) yak.Subject(m.Subject) yak.HTML().Set(m.HTML) @@ -88,12 +87,16 @@ func (c *SmtpClient) Send(m *Message) error { yak.Plain().Set(m.Text) } + if len(m.To) > 0 { + yak.To(addressesToStrings(m.To, true)...) + } + if len(m.Bcc) > 0 { - yak.Bcc(m.Bcc...) + yak.Bcc(addressesToStrings(m.Bcc, true)...) } if len(m.Cc) > 0 { - yak.Cc(m.Cc...) + yak.Cc(addressesToStrings(m.Cc, true)...) } // add attachements (if any)