Compare commits
5 Commits
2028190971
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 537fbeebd9 | |||
| 34f9238cf0 | |||
| e04ed76fee | |||
| b57eb95497 | |||
| 9844ebc65d |
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
func main() {
|
||||
// Here fill with real data
|
||||
emailService := email.NewInsecure(email.MailServiceConfig{
|
||||
emailService := email.NewInsecure(email.SecureConfig{
|
||||
Auth: smtp.PlainAuth("", "your@email.com", "your-password", "smtp.youremail.com"),
|
||||
Host: "smtp.youremail.com",
|
||||
Port: "587",
|
||||
From: "your@email.com",
|
||||
})
|
||||
|
||||
emailService.SendEmail(email.EmailMessage{
|
||||
emailService.SendEmail(email.MessageWithAttachments{
|
||||
To: "other@email.com",
|
||||
Subject: "Test Email",
|
||||
Body: "<html><body><p>Here your body, you can attach as html<p/></body></html>",
|
||||
|
||||
@@ -18,6 +18,31 @@ const (
|
||||
delimeter = "**=myohmy689407924327"
|
||||
)
|
||||
|
||||
type InsecureConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
From string // Sender email address
|
||||
}
|
||||
|
||||
type SecureConfig struct {
|
||||
Auth smtp.Auth
|
||||
Host string
|
||||
Port string
|
||||
From string // Sender email address
|
||||
}
|
||||
|
||||
type MessageWithAttachments struct {
|
||||
To string
|
||||
Subject string
|
||||
Body string
|
||||
Attachments []EmailAttachment
|
||||
}
|
||||
|
||||
type RawMessage struct {
|
||||
To string
|
||||
Body string
|
||||
}
|
||||
|
||||
type SMTPClientIface interface {
|
||||
StartTLS(*tls.Config) error
|
||||
Auth(a smtp.Auth) error
|
||||
@@ -39,7 +64,16 @@ type EmailService struct {
|
||||
dial SmtpDialFn
|
||||
}
|
||||
|
||||
func NewInsecure(config MailServiceConfig) *EmailService {
|
||||
func NewInsecureNoAuth(config InsecureConfig) *EmailService {
|
||||
return &EmailService{
|
||||
host: config.Host,
|
||||
port: config.Port,
|
||||
from: config.From,
|
||||
dial: dial,
|
||||
}
|
||||
}
|
||||
|
||||
func NewInsecure(config SecureConfig) *EmailService {
|
||||
return &EmailService{
|
||||
auth: config.Auth,
|
||||
host: config.Host,
|
||||
@@ -57,8 +91,10 @@ var validCommonNames = []string{
|
||||
"ISRG Root X1",
|
||||
"R3",
|
||||
"R10",
|
||||
"R13",
|
||||
"R11",
|
||||
"E5",
|
||||
"E7",
|
||||
"DST Root CA X3",
|
||||
"DigiCert Global Root G2",
|
||||
"DigiCert Global G2 TLS RSA SHA256 2020 CA1",
|
||||
@@ -104,17 +140,22 @@ func customVerify(host string) func(cs tls.ConnectionState) error {
|
||||
return fmt.Errorf("untrusted certificate issuer: %s", cert.Issuer.CommonName)
|
||||
}
|
||||
|
||||
// Check that the public key algorithm is RSA.
|
||||
if cert.PublicKeyAlgorithm != x509.RSA {
|
||||
return fmt.Errorf("unsupported public key algorithm: %v", cert.PublicKeyAlgorithm)
|
||||
// Check that the public key algorithms
|
||||
switch cert.PublicKeyAlgorithm {
|
||||
case x509.RSA, x509.ECDSA:
|
||||
// OK
|
||||
default:
|
||||
return fmt.Errorf("unsupported public key algorithm: %v",
|
||||
cert.PublicKeyAlgorithm)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewSecure(config MailServiceConfig) *EmailService {
|
||||
func NewSecure(config SecureConfig) *EmailService {
|
||||
return &EmailService{
|
||||
auth: config.Auth,
|
||||
host: config.Host,
|
||||
@@ -129,7 +170,7 @@ func NewSecure(config MailServiceConfig) *EmailService {
|
||||
}
|
||||
}
|
||||
|
||||
func NewSecure465(config MailServiceConfig) *EmailService {
|
||||
func NewSecure465(config SecureConfig) *EmailService {
|
||||
tlsCfg := tls.Config{
|
||||
// Ideally, InsecureSkipVerify: false,
|
||||
// or do a proper certificate validation
|
||||
@@ -149,13 +190,6 @@ func NewSecure465(config MailServiceConfig) *EmailService {
|
||||
}
|
||||
}
|
||||
|
||||
type MailServiceConfig struct {
|
||||
Auth smtp.Auth
|
||||
Host string
|
||||
Port string
|
||||
From string // Sender email address
|
||||
}
|
||||
|
||||
func dial(hostPort string) (SMTPClientIface, error) {
|
||||
client, err := smtp.Dial(hostPort)
|
||||
if err != nil {
|
||||
@@ -183,7 +217,7 @@ func dialTLS(hostPort string, tlsConfig *tls.Config) (SMTPClientIface, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (e *EmailService) SendEmail(emailData EmailMessage) error {
|
||||
func (e *EmailService) SendEmail(emailData MessageWithAttachments) error {
|
||||
msg, err := newMessage(e.from, emailData.To, emailData.Subject).
|
||||
withAttachments(emailData.Body, emailData.Attachments)
|
||||
|
||||
@@ -199,19 +233,32 @@ func (e *EmailService) SendEmail(emailData EmailMessage) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EmailService) SendRaw(emailData RawMessage) error {
|
||||
switch e.port {
|
||||
case "465":
|
||||
return e.sendTLS(emailData.To, []byte(emailData.Body))
|
||||
default:
|
||||
return e.send(emailData.To, []byte(emailData.Body))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EmailService) send(to string, msg []byte) error {
|
||||
c, err := e.dial(e.host + ":" + e.port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DIAL: %s", err)
|
||||
}
|
||||
|
||||
if err = c.StartTLS(e.tlsconfig); err != nil {
|
||||
return fmt.Errorf("c.StartTLS: %s", err)
|
||||
if e.tlsconfig != nil {
|
||||
if err = c.StartTLS(e.tlsconfig); err != nil {
|
||||
return fmt.Errorf("c.StartTLS: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Auth
|
||||
if err = c.Auth(e.auth); err != nil {
|
||||
return fmt.Errorf("c.Auth: %s", err)
|
||||
if e.auth != nil {
|
||||
if err = c.Auth(e.auth); err != nil {
|
||||
return fmt.Errorf("c.Auth: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// To && From
|
||||
@@ -341,3 +388,16 @@ func (m message) withAttachments(body string, attachments []EmailAttachment) ([]
|
||||
message.WriteString("--" + delimeter + "--") // End the message
|
||||
return message.Bytes(), nil
|
||||
}
|
||||
|
||||
type EmailAttachment struct {
|
||||
File io.Reader
|
||||
Title string
|
||||
}
|
||||
|
||||
func (e EmailAttachment) ReadContent() ([]byte, error) {
|
||||
bts, err := io.ReadAll(e.File)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading attachment: %s", err)
|
||||
}
|
||||
return bts, nil
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestNewConfig_MissingEnvFile(t *testing.T) {
|
||||
func TestMockSendEmail(t *testing.T) {
|
||||
service := NewMockMailService(func(params ...interface{}) {})
|
||||
|
||||
emailData := EmailMessage{
|
||||
emailData := MessageWithAttachments{
|
||||
To: "test@example.com",
|
||||
Subject: "Test Email",
|
||||
Body: "This is a test email.",
|
||||
@@ -60,7 +60,7 @@ func TestMockSendEmail(t *testing.T) {
|
||||
func TestNewInsecure(t *testing.T) {
|
||||
cfg := newConfig(".env.test")
|
||||
|
||||
mailSrv := NewInsecure(MailServiceConfig{
|
||||
mailSrv := NewInsecure(SecureConfig{
|
||||
Auth: smtp.PlainAuth("", cfg.MailUser, cfg.MailPassword, cfg.MailHost),
|
||||
Host: cfg.MailHost,
|
||||
Port: cfg.MailPort,
|
||||
@@ -68,7 +68,7 @@ func TestNewInsecure(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestSendEmail", func(t *testing.T) {
|
||||
data := EmailMessage{
|
||||
data := MessageWithAttachments{
|
||||
To: cfg.MailTo,
|
||||
Subject: "Mail Sender",
|
||||
Body: "Hello this is a test email",
|
||||
@@ -89,7 +89,7 @@ func TestNewInsecure(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer reader3.Close()
|
||||
|
||||
data := EmailMessage{
|
||||
data := MessageWithAttachments{
|
||||
To: cfg.MailTo,
|
||||
Subject: "Mail Sender",
|
||||
Body: "Hello this is a test email",
|
||||
@@ -120,7 +120,7 @@ func TestNewInsecure(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestSendEmail_InvalidRecipient", func(t *testing.T) {
|
||||
data := EmailMessage{
|
||||
data := MessageWithAttachments{
|
||||
To: "invalid_email",
|
||||
Subject: "Test Email",
|
||||
Body: "This is a test email.",
|
||||
@@ -131,13 +131,13 @@ func TestNewInsecure(t *testing.T) {
|
||||
|
||||
t.Run("TestSendEmail_FailedAuthentication", func(t *testing.T) {
|
||||
// set up authentication to fail
|
||||
mailSrv := NewInsecure(MailServiceConfig{
|
||||
mailSrv := NewInsecure(SecureConfig{
|
||||
Auth: smtp.PlainAuth("", "wronguser", "wrongpassword", cfg.MailHost),
|
||||
Host: cfg.MailHost,
|
||||
Port: cfg.MailPort,
|
||||
From: cfg.MailFrom,
|
||||
})
|
||||
data := EmailMessage{
|
||||
data := MessageWithAttachments{
|
||||
To: cfg.MailTo,
|
||||
Subject: "Test Email",
|
||||
Body: "This is a test email.",
|
||||
@@ -150,7 +150,7 @@ func TestNewInsecure(t *testing.T) {
|
||||
func TestSecure(t *testing.T) {
|
||||
cfg := newConfig(".env.test")
|
||||
|
||||
emailService := NewSecure(MailServiceConfig{
|
||||
emailService := NewSecure(SecureConfig{
|
||||
Auth: smtp.PlainAuth("", cfg.MailUser, cfg.MailPassword, cfg.MailHost),
|
||||
Host: cfg.MailHost,
|
||||
Port: cfg.MailPort,
|
||||
@@ -172,7 +172,7 @@ func TestSecure(t *testing.T) {
|
||||
}
|
||||
emailService.dial = mockDialFn
|
||||
|
||||
data := EmailMessage{
|
||||
data := MessageWithAttachments{
|
||||
To: cfg.MailTo,
|
||||
Subject: "Mail Sender",
|
||||
Body: "Hello this is a test email",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type EmailMessage struct {
|
||||
To string
|
||||
Subject string
|
||||
Body string
|
||||
Attachments []EmailAttachment
|
||||
}
|
||||
|
||||
type EmailAttachment struct {
|
||||
File io.Reader
|
||||
Title string
|
||||
}
|
||||
|
||||
func (e EmailAttachment) ReadContent() ([]byte, error) {
|
||||
bts, err := io.ReadAll(e.File)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading attachment: %s", err)
|
||||
}
|
||||
return bts, nil
|
||||
}
|
||||
Reference in New Issue
Block a user