package structs
import (
"bytes"
"encoding/base64"
"fmt"
"html/template"
"net/smtp"
"strings"
"time"
"clickandjoin.app/emailserver/modules/cache"
"clickandjoin.app/emailserver/modules/config"
"clickandjoin.app/emailserver/modules/escaper"
gocnjhelper "git.ex.umbach.dev/ClickandJoin/go-cnj-helper"
"github.com/google/uuid"
)
type Mail struct {
To []string
Subject string
TemplateId string
LanguageId string
BodyData interface{}
}
func (m *Mail) Send(htmlBody, textBody string) error {
cfg := config.Cfg.Mail
// Generate unique message ID
messageID := uuid.New().String() + "@" + strings.Split(cfg.FromEmail, "@")[1]
// Generate unique boundary string
boundary := "boundary" + uuid.New().String()
// Create MIME message with multipart/alternative content type
msg := "From: " + cfg.FromName + " <" + cfg.FromEmail + ">\n" +
"To: " + strings.Join(m.To, ",") + "\n" +
"Subject: " + m.Subject + "\n" +
"Date: " + time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + "\n" +
"Message-ID: <" + messageID + ">\n" +
"MIME-version: 1.0;\n" +
"Content-Type: multipart/alternative; boundary=" + boundary + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: text/plain; charset=\"UTF-8\"\n\n" +
textBody + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: text/html; charset=\"UTF-8\"\n\n" +
htmlBody + "\n\n" +
"--" + boundary + "--"
err := smtp.SendMail(cfg.Host+":"+cfg.Port, cache.SmtpAuth, cfg.FromEmail, m.To, []byte(msg))
if err != nil {
gocnjhelper.LogErrorf("smtp error: %s", err)
return err
}
return nil
}
/*
func (m *Mail) Send(body string) error {
cfg := config.Cfg.Mail
msg := "From: " + cfg.FromName + " <" + cfg.FromEmail + ">\n" +
"To: " + strings.Join(m.To, ",") + "\n" +
"Subject: " + m.Subject + "\n" +
"Date: " + time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + "\n" +
"Message-ID: <" + uuid.New().String() + "@" + strings.Split(cfg.FromEmail, "@")[1] + ">\n" +
"MIME-version: 1.0;\nContent-Type: multipart/alternative; charset=\"UTF-8\";\n\n" +
body
err := smtp.SendMail(cfg.Host+":"+cfg.Port, cache.SmtpAuth, cfg.FromEmail, m.To, []byte(msg))
if err != nil {
gocnjhelper.LogErrorf("smtp error: %s", err)
return err
}
gocnjhelper.LogDebugf("SEND MAIL %s", msg)
return nil
} */
func (m *Mail) RenderTemplate() (htmlBody, textBody string, err error) {
body := cache.BodyTemplates[m.TemplateId]
for templateName, templateData := range cache.Templates.Templates {
// Skipping templates if the template ID is not the expected one
if templateName != m.TemplateId {
continue
}
m.Subject = templateData["mailSubject"][m.LanguageId]
// occurs if the requested language code does not exist
if m.Subject == "" {
m.Subject = templateData["mailSubject"][config.Cfg.DefaultLanguageCode]
}
// replace body %values% with values in templates config
for key, value := range templateData {
if key == "mailSubject" {
continue
}
v := value[m.LanguageId]
// occurs if the requested language code does not exist
if v == "" {
v = value[config.Cfg.DefaultLanguageCode]
}
// escaping so that umlauts are displayed correctly
if m.LanguageId == "de" {
v = escaper.EscapeHtmlEntites(v)
}
body.HTML = []byte(strings.Replace(string(body.HTML), "%"+key+"%", v, -1))
body.PlainText = []byte(strings.Replace(string(body.PlainText), "%"+key+"%", v, -1))
}
}
// The subject line of an email is an independent header and utf 8 must be set for it
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
if m.LanguageId == "de" {
m.Subject = EncodeBase64(m.Subject)
}
// Separate rendering for HTML and text bodies
htmlTemplate := template.Must(template.New("").Parse(string(body.HTML)))
textTemplate := template.Must(template.New("").Parse(string(body.PlainText)))
htmlBuf := new(bytes.Buffer)
textBuf := new(bytes.Buffer)
// parse body data to new map to avoid changing the original map and then replace all \n with
bodyData := make(map[string]interface{})
for key, value := range m.BodyData.(map[string]interface{}) {
// check if value is a string
if _, ok := value.(string); ok {
bodyData[key] = strings.ReplaceAll(value.(string), "\n", "
")
}
}
m.BodyData = bodyData
if err := htmlTemplate.Execute(htmlBuf, m.BodyData); err != nil {
return "", "", err
}
// the execute function replaces all < and > with < and > in html body
// so we have to replace them back
// replace all < and > with < and > in html body
htmlBody = strings.ReplaceAll(htmlBuf.String(), "<", "<")
htmlBody = strings.ReplaceAll(htmlBody, ">", ">")
// Execute text template
if err := textTemplate.Execute(textBuf, m.BodyData); err != nil {
return "", "", err
}
return htmlBody, textBuf.String(), nil
}
/*
func (m *Mail) RenderTemplate() (string, error) {
body := cache.BodyTemplates[m.TemplateId]
for templateName, templateData := range cache.Templates.Templates {
// Skipping templates if the template ID is not the expected one
if templateName != m.TemplateId {
continue
}
gocnjhelper.LogDebugf("RENDER TEMPLATE %s %s", templateName, templateData)
m.Subject = templateData["mailSubject"][m.LanguageId]
// occurs if the requested language code does not exist
if m.Subject == "" {
m.Subject = templateData["mailSubject"][config.Cfg.DefaultLanguageCode]
}
// replace body %values% with values in templates config
for key, value := range templateData {
if key == "mailSubject" {
continue
}
v := value[m.LanguageId]
// occurs if the requested language code does not exist
if v == "" {
v = value[config.Cfg.DefaultLanguageCode]
}
// escaping so that umlauts are displayed correctly
if m.LanguageId == "de" {
v = escaper.EscapeHtmlEntites(v)
}
body.HTML = []byte(strings.Replace(string(body.HTML), "%"+key+"%", v, -1))
}
}
// The subject line of an email is an independent header and utf 8 must be set for it
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
if m.LanguageId == "de" {
m.Subject = EncodeBase64(m.Subject)
}
t := template.Must(template.New("").Parse(string(body.HTML)))
buf := new(bytes.Buffer)
if err := t.Execute(buf, m.BodyData); err != nil {
return "", err
}
return buf.String(), nil
} */
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
func EncodeBase64(s string) string {
return fmt.Sprintf("=?utf-8?B?%s?=", base64.StdEncoding.EncodeToString([]byte(s)))
}