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) 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))) }