Last active
September 20, 2019 08:24
-
-
Save Loupax/7bf90b6fe9c1c0dd72a543aa6ac58398 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mailer | |
import ( | |
"bytes" | |
"crypto/tls" | |
"encoding/base64" | |
"fmt" | |
"io" | |
"net/http" | |
"net/mail" | |
"net/smtp" | |
"strings" | |
) | |
type MailerConfig struct { | |
Hostname string | |
User string | |
Password string | |
Port int | |
} | |
type Mailer struct { | |
config MailerConfig | |
auth smtp.Auth | |
} | |
type MessageBody struct { | |
Content string | |
ContentType string | |
} | |
type Message struct { | |
Subject string | |
HTMLContent string | |
Attachments map[string]io.Reader | |
} | |
func NewMailer(config config.Mailer) Mailer { | |
return Mailer{ | |
config: config, | |
auth: smtp.PlainAuth("", config.User, config.Password, config.Hostname), | |
} | |
} | |
func (m Mailer) Send(from mail.Address, to []mail.Address, cc []mail.Address, bcc []mail.Address, message Message) error { | |
client, dialErr := smtp.Dial(fmt.Sprintf("%s:%d", m.config.Hostname, m.config.Port)) | |
if dialErr != nil { | |
return dialErr | |
} | |
defer client.Close() | |
tlsErr := client.StartTLS(&tls.Config{ServerName: fmt.Sprintf("%s:%d", m.config.Hostname, m.config.Port), InsecureSkipVerify: true}) | |
if tlsErr != nil { | |
return tlsErr | |
} | |
if m.config.User != "" { | |
if err := client.Auth(m.auth); err != nil { | |
return err | |
} | |
} | |
if err := client.Mail(from.Address); err != nil { | |
return err | |
} | |
var recepients []mail.Address | |
recepients = append(recepients, to...) | |
recepients = append(recepients, cc...) | |
recepients = append(recepients, bcc...) | |
for _, rcpt := range recepients { | |
if err := client.Rcpt(rcpt.Address); err != nil { | |
return err | |
} | |
} | |
writer, wErr := client.Data() | |
if wErr != nil { | |
return wErr | |
} | |
lines := []string{ | |
fmt.Sprintf( | |
"From: %s\r\n"+ | |
"To: %s\r\n", | |
from.String(), | |
strings.Join(addrToStringSlice(to), ";"), | |
), | |
} | |
if len(cc) > 0 { | |
lines = append(lines, fmt.Sprintf("Cc: %s\r\n", strings.Join(addrToStringSlice(cc), ";"))) | |
} | |
lines = append(lines, fmt.Sprintf("Subject: %s\r\nMIME-Version: 1.0\r\n", message.Subject)) | |
boundary := "**=omgkaitrialol_5d83432910261_35f93fdd8157706c5341c5" | |
// Append Content | |
lines = append( | |
lines, | |
fmt.Sprintf( | |
"Content-Type: multipart/mixed; boundary=\"%s\"\r\n"+ | |
"\r\n--%s\r\n"+ | |
"Content-Type: %s; charset=\"utf-8\"\r\n"+ | |
"Content-Transfer-Encoding: 7bit\r\n"+ | |
"\r\n%s\r\n", | |
boundary, | |
boundary, | |
"text/html", | |
message.HTMLContent, | |
), | |
) | |
for _, line := range lines { | |
_, err := writer.Write([]byte(line)) | |
if err != nil { | |
return err | |
} | |
} | |
lines = nil | |
// Append attachments | |
for filename, attachment := range message.Attachments { | |
err := sendAttachment(filename, boundary, attachment, writer) | |
if err != nil { | |
return err | |
} | |
} | |
if err := client.Quit(); err != nil { | |
return err | |
} | |
return nil | |
} | |
func sendAttachment(filename string, boundary string, attachment io.Reader, writer io.Writer) error { | |
var attachmentCopy bytes.Buffer | |
mimeDetectionReader := io.TeeReader(attachment, &attachmentCopy) | |
mimePart := make([]byte, 512) | |
io.ReadFull(mimeDetectionReader, mimePart) | |
_, err := writer.Write([]byte(fmt.Sprintf( | |
"\r\n--%s\r\n"+ | |
"Content-Type: %s; charset=\"utf-8\"\r\n"+ | |
"Content-Transfer-Encoding: base64\r\n"+ | |
"Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", | |
boundary, | |
http.DetectContentType(mimePart), | |
filename, | |
))) | |
if err != nil { | |
return err | |
} | |
encoder := base64.NewEncoder(base64.StdEncoding, writer) | |
defer encoder.Close() | |
_, cpErr := io.Copy(encoder, &attachmentCopy) | |
if cpErr != nil { | |
return cpErr | |
} | |
return nil | |
} | |
func addrToStringSlice(addresses []mail.Address) []string { | |
out := make([]string, len(addresses)) | |
for i, addr := range addresses { | |
out[i] = addr.String() | |
} | |
return out | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment