diff --git a/config/iris.go b/config/iris.go index 8b4641b4..0180ea65 100644 --- a/config/iris.go +++ b/config/iris.go @@ -83,7 +83,10 @@ type ( // Render contains the configs for template and rest configuration Render Render + // Websocket contains the configs for Websocket's server integration Websocket Websocket + // Mail contains the config for the mail sender service + Mail Mail } // Render struct keeps organise all configuration about rendering, templates and rest currently. @@ -121,6 +124,7 @@ func Default() Iris { Sessions: DefaultSessions(), Render: DefaultRender(), Websocket: DefaultWebsocket(), + Mail: DefaultMail(), } } diff --git a/config/logger.go b/config/logger.go index 39d19aa4..1aa47584 100644 --- a/config/logger.go +++ b/config/logger.go @@ -38,7 +38,7 @@ func (c Logger) Merge(cfg []Logger) (config Logger) { return } -// Merge MergeSingle the default with the given config and returns the result +// MergeSingle merges the default with the given config and returns the result func (c Logger) MergeSingle(cfg Logger) (config Logger) { config = cfg diff --git a/config/mail.go b/config/mail.go new file mode 100644 index 00000000..72a2ce86 --- /dev/null +++ b/config/mail.go @@ -0,0 +1,19 @@ +package config + +// Mail keeps the configs for mail sender service +type Mail struct { + // Host is the server mail host, IP or address + Host string + // Port is the listening port + Port int + // Username is the auth username@domain.com for the sender + Username string + // Password is the auth password for the sender + Password string +} + +// DefaultMail returns the default configs for Mail +// returns just an empty Mail struct +func DefaultMail() Mail { + return Mail{} +} diff --git a/iris.go b/iris.go index 6da1433c..5ba599b8 100644 --- a/iris.go +++ b/iris.go @@ -15,6 +15,7 @@ import ( "github.com/fatih/color" "github.com/kataras/iris/config" "github.com/kataras/iris/logger" + "github.com/kataras/iris/mail" "github.com/kataras/iris/render/rest" "github.com/kataras/iris/render/template" "github.com/kataras/iris/server" @@ -80,6 +81,7 @@ type ( templates *template.Template sessionManager *sessions.Manager websocketServer websocket.Server + mailService mail.Service logger *logger.Logger gzipWriterPool sync.Pool // this pool is used everywhere needed in the iris for example inside party-> StaticSimple } @@ -130,6 +132,15 @@ func (s *Iris) initWebsocketServer() { } } +func (s *Iris) initMailService() { + if s.mailService == nil { + // enable mail sender service if configs are valid + if s.config.Mail.Host != "" && s.config.Mail.Username != "" && s.config.Mail.Password != "" { + s.mailService = mail.New(s.config.Mail) + } + } +} + func (s *Iris) printBanner() { c := color.New(color.FgHiBlue).Add(color.Bold) printTicker := utils.NewTicker() @@ -321,3 +332,9 @@ func (s *Iris) Websocket() websocket.Server { s.initWebsocketServer() // for any case the user called .Websocket() before server's listen return s.websocketServer } + +// Mail returns the mail sender service +func (s *Iris) Mail() mail.Service { + s.initMailService() + return s.mailService +} diff --git a/iris_singleton.go b/iris_singleton.go index caaf7411..fac55cf1 100644 --- a/iris_singleton.go +++ b/iris_singleton.go @@ -3,6 +3,7 @@ package iris import ( "github.com/kataras/iris/config" "github.com/kataras/iris/logger" + "github.com/kataras/iris/mail" "github.com/kataras/iris/render/rest" "github.com/kataras/iris/render/template" "github.com/kataras/iris/server" @@ -393,3 +394,8 @@ func Templates() *template.Template { func Websocket() websocket.Server { return DefaultIris.Websocket() } + +// Mail returns the mail sender service +func Mail() mail.Service { + return DefaultIris.Mail() +} diff --git a/mail/service.go b/mail/service.go new file mode 100644 index 00000000..a60dbfdb --- /dev/null +++ b/mail/service.go @@ -0,0 +1,72 @@ +package mail + +import ( + "fmt" + "net/smtp" + "strings" + "text/template" + + "github.com/kataras/iris/config" + "github.com/kataras/iris/utils" +) + +const tmpl = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}
MIME-version: 1.0
Content-Type: text/html; charset="UTF-8"

{{.Body}}` + +var buf = utils.NewBufferPool(64) + +type ( + // Service is the interface which mail sender should implement + Service interface { + // Send sends a mail to recipients + // the body can be html also + Send(to []string, subject, body string) error + } + + mailer struct { + config config.Mail + auth smtp.Auth + authenticated bool + } +) + +// New creates and returns a new Service +func New(cfg config.Mail) Service { + return &mailer{config: cfg} +} + +func (m *mailer) authenticate() error { + if m.config.Username == "" || m.config.Password == "" || m.config.Host == "" { + return fmt.Errorf("Username, Password & Host cannot be empty!") + } + m.auth = smtp.PlainAuth("", m.config.Username, m.config.Password, m.config.Host) + m.authenticated = true + return nil +} + +// Send sends a mail to recipients +// the body can be html also +func (m *mailer) Send(to []string, subject, body string) error { + buffer := buf.Get() + defer buf.Put(buffer) + + if !m.authenticated { + if err := m.authenticate(); err != nil { + return err + } + } + + mailArgs := map[string]string{"To": strings.Join(to, ","), "Subject": subject, "Body": body} + template := template.Must(template.New("mailTmpl").Parse(tmpl)) + + if err := template.Execute(buffer, mailArgs); err != nil { + return err + } + + return smtp.SendMail( + fmt.Sprintf("%s:%d", m.config.Host, m.config.Port), + m.auth, + m.config.Username, + to, + buffer.Bytes(), + ) +}