package email import ( "bytes" "crypto/sha256" "fmt" "mime" "mime/multipart" "mime/quotedprintable" "net/mail" "net/textproto" "strings" "time" "git.sendico.io/tech/sxchnge/notifications/model" merrors "git.sendico.io/tech/sxchnge/shared/errors" ) func smtpMessage(s smtpSettings, key string, email model.Email) ([]byte, error) { if !ValidAddress(email.Address) || strings.ContainsAny(email.Subject, "\r\n") || (email.Text == "" && email.HTML == "") { return nil, merrors.InvalidArgument("invalid email content") } var body bytes.Buffer parts := multipart.NewWriter(&body) for _, part := range []struct{ kind, value string }{{"text/plain", email.Text}, {"text/html", email.HTML}} { if part.value == "" { continue } writer, err := parts.CreatePart(textproto.MIMEHeader{"Content-Type": {part.kind + "; charset=utf-8"}, "Content-Transfer-Encoding": {"quoted-printable"}}) if err != nil { return nil, merrors.InternalWrap(err, "create email MIME part") } encoded := quotedprintable.NewWriter(writer) if _, err = encoded.Write([]byte(part.value)); err != nil { return nil, merrors.InternalWrap(err, "encode email MIME part") } if err = encoded.Close(); err != nil { return nil, merrors.InternalWrap(err, "finish email MIME part") } } if err := parts.Close(); err != nil { return nil, merrors.InternalWrap(err, "finish email MIME body") } from := (&mail.Address{Name: s.SenderName, Address: s.SenderAddress}).String() to := (&mail.Address{Address: email.Address}).String() domain := s.SenderAddress[strings.LastIndex(s.SenderAddress, "@")+1:] headers := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\nMessage-ID: <%x@%s>\r\nMIME-Version: 1.0\r\nContent-Type: multipart/alternative; boundary=%q\r\n\r\n", from, to, mime.QEncoding.Encode("utf-8", email.Subject), time.Now().UTC().Format(time.RFC1123Z), sha256.Sum256([]byte(key)), domain, parts.Boundary()) return append([]byte(headers), body.Bytes()...), nil }