initial public commit

This commit is contained in:
Gani Georgiev
2022-07-07 00:19:05 +03:00
commit 3d07f0211d
484 changed files with 92412 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
package mailer
import (
"io"
"net/mail"
)
// Mailer defines a base mail client interface.
type Mailer interface {
// Send sends an email with HTML body to the specified recipient.
Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlBody string,
attachments map[string]io.Reader,
) error
}
+79
View File
@@ -0,0 +1,79 @@
package mailer
import (
"bytes"
"errors"
"io"
"mime"
"net/http"
"net/mail"
"os/exec"
)
var _ Mailer = (*Sendmail)(nil)
// Sendmail implements `mailer.Mailer` interface and defines a mail
// client that sends emails via the `sendmail` *nix command.
//
// This client is usually recommended only for development and testing.
type Sendmail struct {
}
// Send implements `mailer.Mailer` interface.
//
// Attachments are currently not supported.
func (m *Sendmail) Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlBody string,
attachments map[string]io.Reader,
) error {
headers := make(http.Header)
headers.Set("Subject", mime.QEncoding.Encode("utf-8", subject))
headers.Set("From", fromEmail.String())
headers.Set("To", toEmail.String())
headers.Set("Content-Type", "text/html; charset=UTF-8")
cmdPath, err := findSendmailPath()
if err != nil {
return err
}
var buffer bytes.Buffer
// write
// ---
if err := headers.Write(&buffer); err != nil {
return err
}
if _, err := buffer.Write([]byte("\r\n")); err != nil {
return err
}
if _, err := buffer.Write([]byte(htmlBody)); err != nil {
return err
}
// ---
sendmail := exec.Command(cmdPath, toEmail.Address)
sendmail.Stdin = &buffer
return sendmail.Run()
}
func findSendmailPath() (string, error) {
options := []string{
"/usr/sbin/sendmail",
"/usr/bin/sendmail",
"sendmail",
}
for _, option := range options {
path, err := exec.LookPath(option)
if err == nil {
return path, err
}
}
return "", errors.New("Failed to locate a sendmail executable path.")
}
+88
View File
@@ -0,0 +1,88 @@
package mailer
import (
"fmt"
"io"
"net/mail"
"net/smtp"
"regexp"
"strings"
"github.com/domodwyer/mailyak/v3"
"github.com/microcosm-cc/bluemonday"
)
var _ Mailer = (*SmtpClient)(nil)
// regex to select all tabs
var tabsRegex = regexp.MustCompile(`\t+`)
// NewSmtpClient creates new `SmtpClient` with the provided configuration.
func NewSmtpClient(
host string,
port int,
username string,
password string,
tls bool,
) *SmtpClient {
return &SmtpClient{
host: host,
port: port,
username: username,
password: password,
tls: tls,
}
}
// SmtpClient defines a SMTP mail client structure that implements
// `mailer.Mailer` interface.
type SmtpClient struct {
mail *mailyak.MailYak
host string
port int
username string
password string
tls bool
}
// Send implements `mailer.Mailer` interface.
func (m *SmtpClient) Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlBody string,
attachments map[string]io.Reader,
) error {
smtpAuth := smtp.PlainAuth("", m.username, m.password, m.host)
// create mail instance
var yak *mailyak.MailYak
if m.tls {
var tlsErr error
yak, tlsErr = mailyak.NewWithTLS(fmt.Sprintf("%s:%d", m.host, m.port), smtpAuth, nil)
if tlsErr != nil {
return tlsErr
}
} else {
yak = mailyak.New(fmt.Sprintf("%s:%d", m.host, m.port), smtpAuth)
}
if fromEmail.Name != "" {
yak.FromName(fromEmail.Name)
}
yak.From(fromEmail.Address)
yak.To(toEmail.Address)
yak.Subject(subject)
yak.HTML().Set(htmlBody)
// set also plain text content
policy := bluemonday.StrictPolicy() // strips all tags
yak.Plain().Set(strings.TrimSpace(tabsRegex.ReplaceAllString(policy.Sanitize(htmlBody), "")))
for name, data := range attachments {
yak.Attach(name, data)
}
return yak.Send()
}