summaryrefslogtreecommitdiff
path: root/internal/email
diff options
context:
space:
mode:
Diffstat (limited to 'internal/email')
-rw-r--r--internal/email/confirm.go53
-rw-r--r--internal/email/email_test.go (renamed from internal/email/email.go)23
-rw-r--r--internal/email/noopsender.go82
-rw-r--r--internal/email/reset.go53
-rw-r--r--internal/email/sender.go60
-rw-r--r--internal/email/util.go56
-rw-r--r--internal/email/util_test.go60
7 files changed, 385 insertions, 2 deletions
diff --git a/internal/email/confirm.go b/internal/email/confirm.go
new file mode 100644
index 000000000..78f661fe1
--- /dev/null
+++ b/internal/email/confirm.go
@@ -0,0 +1,53 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email
+
+import (
+ "bytes"
+ "net/smtp"
+)
+
+const (
+ confirmTemplate = "email_confirm.tmpl"
+ confirmSubject = "Subject: GoToSocial Email Confirmation"
+)
+
+func (s *sender) SendConfirmEmail(toAddress string, data ConfirmData) error {
+ buf := &bytes.Buffer{}
+ if err := s.template.ExecuteTemplate(buf, confirmTemplate, data); err != nil {
+ return err
+ }
+ confirmBody := buf.String()
+
+ msg := assembleMessage(confirmSubject, confirmBody, toAddress, s.from)
+ return smtp.SendMail(s.hostAddress, s.auth, s.from, []string{toAddress}, msg)
+}
+
+// ConfirmData represents data passed into the confirm email address template.
+type ConfirmData struct {
+ // Username to be addressed.
+ Username string
+ // URL of the instance to present to the receiver.
+ InstanceURL string
+ // Name of the instance to present to the receiver.
+ InstanceName string
+ // Link to present to the receiver to click on and do the confirmation.
+ // Should be a full link with protocol eg., https://example.org/confirm_email?token=some-long-token
+ ConfirmLink string
+}
diff --git a/internal/email/email.go b/internal/email/email_test.go
index 3d6a9dd2d..4e2619546 100644
--- a/internal/email/email.go
+++ b/internal/email/email_test.go
@@ -16,5 +16,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Package email provides a service for interacting with an SMTP server
-package email
+package email_test
+
+import (
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/gotosocial/internal/email"
+ "github.com/superseriousbusiness/gotosocial/testrig"
+)
+
+type EmailTestSuite struct {
+ suite.Suite
+
+ sender email.Sender
+
+ sentEmails map[string]string
+}
+
+func (suite *EmailTestSuite) SetupTest() {
+ testrig.InitTestLog()
+ suite.sentEmails = make(map[string]string)
+ suite.sender = testrig.NewEmailSender("../../web/template/", suite.sentEmails)
+}
diff --git a/internal/email/noopsender.go b/internal/email/noopsender.go
new file mode 100644
index 000000000..82eb8db44
--- /dev/null
+++ b/internal/email/noopsender.go
@@ -0,0 +1,82 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email
+
+import (
+ "bytes"
+ "html/template"
+
+ "github.com/sirupsen/logrus"
+)
+
+// NewNoopSender returns a no-op email sender that will just execute the given sendCallback
+// every time it would otherwise send an email to the given toAddress with the given message value.
+//
+// Passing a nil function is also acceptable, in which case the send functions will just return nil.
+func NewNoopSender(templateBaseDir string, sendCallback func(toAddress string, message string)) (Sender, error) {
+ t, err := loadTemplates(templateBaseDir)
+ if err != nil {
+ return nil, err
+ }
+
+ return &noopSender{
+ sendCallback: sendCallback,
+ template: t,
+ }, nil
+}
+
+type noopSender struct {
+ sendCallback func(toAddress string, message string)
+ template *template.Template
+}
+
+func (s *noopSender) SendConfirmEmail(toAddress string, data ConfirmData) error {
+ buf := &bytes.Buffer{}
+ if err := s.template.ExecuteTemplate(buf, confirmTemplate, data); err != nil {
+ return err
+ }
+ confirmBody := buf.String()
+
+ msg := assembleMessage(confirmSubject, confirmBody, toAddress, "test@example.org")
+
+ logrus.Tracef("NOT SENDING confirmation email to %s with contents: %s", toAddress, msg)
+
+ if s.sendCallback != nil {
+ s.sendCallback(toAddress, string(msg))
+ }
+ return nil
+}
+
+func (s *noopSender) SendResetEmail(toAddress string, data ResetData) error {
+ buf := &bytes.Buffer{}
+ if err := s.template.ExecuteTemplate(buf, resetTemplate, data); err != nil {
+ return err
+ }
+ resetBody := buf.String()
+
+ msg := assembleMessage(resetSubject, resetBody, toAddress, "test@example.org")
+
+ logrus.Tracef("NOT SENDING reset email to %s with contents: %s", toAddress, msg)
+
+ if s.sendCallback != nil {
+ s.sendCallback(toAddress, string(msg))
+ }
+
+ return nil
+}
diff --git a/internal/email/reset.go b/internal/email/reset.go
new file mode 100644
index 000000000..786777f1b
--- /dev/null
+++ b/internal/email/reset.go
@@ -0,0 +1,53 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email
+
+import (
+ "bytes"
+ "net/smtp"
+)
+
+const (
+ resetTemplate = "email_reset.tmpl"
+ resetSubject = "Subject: GoToSocial Password Reset"
+)
+
+func (s *sender) SendResetEmail(toAddress string, data ResetData) error {
+ buf := &bytes.Buffer{}
+ if err := s.template.ExecuteTemplate(buf, resetTemplate, data); err != nil {
+ return err
+ }
+ resetBody := buf.String()
+
+ msg := assembleMessage(resetSubject, resetBody, toAddress, s.from)
+ return smtp.SendMail(s.hostAddress, s.auth, s.from, []string{toAddress}, msg)
+}
+
+// ResetData represents data passed into the reset email address template.
+type ResetData struct {
+ // Username to be addressed.
+ Username string
+ // URL of the instance to present to the receiver.
+ InstanceURL string
+ // Name of the instance to present to the receiver.
+ InstanceName string
+ // Link to present to the receiver to click on and begin the reset process.
+ // Should be a full link with protocol eg., https://example.org/reset_password?token=some-reset-password-token
+ ResetLink string
+}
diff --git a/internal/email/sender.go b/internal/email/sender.go
new file mode 100644
index 000000000..a6a9abe55
--- /dev/null
+++ b/internal/email/sender.go
@@ -0,0 +1,60 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email
+
+import (
+ "fmt"
+ "html/template"
+ "net/smtp"
+
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+)
+
+// Sender contains functions for sending emails to instance users/new signups.
+type Sender interface {
+ // SendConfirmEmail sends a 'please confirm your email' style email to the given toAddress, with the given data.
+ SendConfirmEmail(toAddress string, data ConfirmData) error
+
+ // SendResetEmail sends a 'reset your password' style email to the given toAddress, with the given data.
+ SendResetEmail(toAddress string, data ResetData) error
+}
+
+// NewSender returns a new email Sender interface with the given configuration, or an error if something goes wrong.
+func NewSender(cfg *config.Config) (Sender, error) {
+ t, err := loadTemplates(cfg.TemplateConfig.BaseDir)
+ if err != nil {
+ return nil, err
+ }
+
+ auth := smtp.PlainAuth("", cfg.SMTPConfig.Username, cfg.SMTPConfig.Password, cfg.SMTPConfig.Host)
+
+ return &sender{
+ hostAddress: fmt.Sprintf("%s:%d", cfg.SMTPConfig.Host, cfg.SMTPConfig.Port),
+ from: cfg.SMTPConfig.From,
+ auth: auth,
+ template: t,
+ }, nil
+}
+
+type sender struct {
+ hostAddress string
+ from string
+ auth smtp.Auth
+ template *template.Template
+}
diff --git a/internal/email/util.go b/internal/email/util.go
new file mode 100644
index 000000000..1d3db5802
--- /dev/null
+++ b/internal/email/util.go
@@ -0,0 +1,56 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email
+
+import (
+ "fmt"
+ "html/template"
+ "os"
+ "path/filepath"
+)
+
+const (
+ mime = `MIME-version: 1.0;
+Content-Type: text/html;`
+)
+
+func loadTemplates(templateBaseDir string) (*template.Template, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, fmt.Errorf("error getting current working directory: %s", err)
+ }
+
+ // look for all templates that start with 'email_'
+ tmPath := filepath.Join(cwd, fmt.Sprintf("%semail_*", templateBaseDir))
+ return template.ParseGlob(tmPath)
+}
+
+func assembleMessage(mailSubject string, mailBody string, mailTo string, mailFrom string) []byte {
+ from := fmt.Sprintf("From: GoToSocial <%s>", mailFrom)
+ to := fmt.Sprintf("To: %s", mailTo)
+
+ msg := []byte(
+ mailSubject + "\r\n" +
+ from + "\r\n" +
+ to + "\r\n" +
+ mime + "\r\n" +
+ mailBody + "\r\n")
+
+ return msg
+}
diff --git a/internal/email/util_test.go b/internal/email/util_test.go
new file mode 100644
index 000000000..201bcd92d
--- /dev/null
+++ b/internal/email/util_test.go
@@ -0,0 +1,60 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package email_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/gotosocial/internal/email"
+)
+
+type UtilTestSuite struct {
+ EmailTestSuite
+}
+
+func (suite *UtilTestSuite) TestTemplateConfirm() {
+ confirmData := email.ConfirmData{
+ Username: "test",
+ InstanceURL: "https://example.org",
+ InstanceName: "Test Instance",
+ ConfirmLink: "https://example.org/confirm_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa",
+ }
+
+ suite.sender.SendConfirmEmail("user@example.org", confirmData)
+ suite.Len(suite.sentEmails, 1)
+ suite.Equal("Subject: GoToSocial Email Confirmation\r\nFrom: GoToSocial <test@example.org>\r\nTo: user@example.org\r\nMIME-version: 1.0;\nContent-Type: text/html;\r\n<!DOCTYPE html>\n<html>\n </head>\n <body>\n <div>\n <h1>\n Hello test!\n </h1>\n </div>\n <div>\n <p>\n You are receiving this mail because you've requested an account on <a href=\"https://example.org\">Test Instance</a>.\n </p>\n <p>\n We just need to confirm that this is your email address. To confirm your email, <a href=\"https://example.org/confirm_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa\">click here</a> or paste the following in your browser's address bar:\n </p>\n <p>\n <code>\n https://example.org/confirm_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa\n </code>\n </p>\n </div>\n <div>\n <p>\n If you believe you've been sent this email in error, feel free to ignore it, or contact the administrator of <a href=\"https://example.org\">Test Instance</a>.\n </p>\n </div>\n </body>\n</html>\r\n", suite.sentEmails["user@example.org"])
+}
+
+func (suite *UtilTestSuite) TestTemplateReset() {
+ resetData := email.ResetData{
+ Username: "test",
+ InstanceURL: "https://example.org",
+ InstanceName: "Test Instance",
+ ResetLink: "https://example.org/reset_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa",
+ }
+
+ suite.sender.SendResetEmail("user@example.org", resetData)
+ suite.Len(suite.sentEmails, 1)
+ suite.Equal("Subject: GoToSocial Password Reset\r\nFrom: GoToSocial <test@example.org>\r\nTo: user@example.org\r\nMIME-version: 1.0;\nContent-Type: text/html;\r\n<!DOCTYPE html>\n<html>\n </head>\n <body>\n <div>\n <h1>\n Hello test!\n </h1>\n </div>\n <div>\n <p>\n You are receiving this mail because a password reset has been requested for your account on <a href=\"https://example.org\">Test Instance</a>.\n </p>\n <p>\n To reset your password, <a href=\"https://example.org/reset_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa\">click here</a> or paste the following in your browser's address bar:\n </p>\n <p>\n <code>\n https://example.org/reset_email?token=ee24f71d-e615-43f9-afae-385c0799b7fa\n </code>\n </p>\n </div>\n <div>\n <p>\n If you believe you've been sent this email in error, feel free to ignore it, or contact the administrator of <a href=\"https://example.org\">Test Instance</a>.\n </p>\n </div>\n </body>\n</html>\r\n", suite.sentEmails["user@example.org"])
+}
+
+func TestUtilTestSuite(t *testing.T) {
+ suite.Run(t, &UtilTestSuite{})
+}