既存のライブラリとかを色々調べた結果、うーんという感じだったので実装
テキストメールとHTMLメールの文字列を受け取って
・両方とも存在すればマルチパートで配信
・どちらか一方が空文字だったらマルチパートにはしないようにする。
※添付ファイルとかには未対応。
改めて、メールの作り方の勉強になりました。
参考(大部分を流用させて頂いてます。)
https://qiita.com/yamasaki-masahide/items/a9f8b43eeeaddbfb6b44
// SendMailAtSMTP SMTPを使用してメール送信
// textBody テキストメールの内容
// htmlBody HTMLメールの内容
func (serv *SendMailService) SendMailAtSMTP(subject, textBody, htmlBody string, toAddresses []string) error {
c, err := smtp.Dial("localhost:25")
if err != nil {
return err
}
for _, email := range toAddresses {
c.Mail(test@localhost) // 送り主
c.Rcpt(email) // 受信者
wc, err := c.Data()
if err != nil {
return err
}
uuid := "乱数" // 何かしらで乱数生成
var textBodyBuffer bytes.Buffer
textBodyBuffer.WriteString(textBody)
var htmlBodyBuffer bytes.Buffer
htmlBodyBuffer.WriteString(htmlBody)
var message bytes.Buffer
boundary := ""
// mail header
message.WriteString("To: " + email + "\r\n")
message.WriteString(encodeSubject(subject))
message.WriteString("Mime-Version: 1.0\r\n")
if textBody != "" && htmlBody != "" {
message.WriteString("Content-Type: multipart/alternative; boundary=\"" + uuid + "\"\r\n")
message.WriteString("\r\n")
boundary = "--" + uuid + "\r\n"
}
// text/plain mail body
if textBody != "" {
message.WriteString(boundary)
message.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n")
message.WriteString("Content-Transfer-Encoding: base64\r\n")
message.WriteString("\r\n")
message.WriteString(add76crlf(base64.StdEncoding.EncodeToString(textBodyBuffer.Bytes())))
message.WriteString("\r\n")
message.WriteString("\r\n")
}
// text/html mail body
if htmlBody != "" {
message.WriteString(boundary)
message.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n")
message.WriteString("Content-Transfer-Encoding: base64\r\n")
message.WriteString("\r\n")
message.WriteString(add76crlf(base64.StdEncoding.EncodeToString(htmlBodyBuffer.Bytes())))
message.WriteString("\r\n")
message.WriteString("\r\n")
}
// mail body end
if textBody != "" && htmlBody != "" {
message.WriteString("--" + uuid + "--\r\n")
}
if _, err = message.WriteTo(wc); err != nil {
return err
}
wc.Close()
}
return c.Quit()
}
// 76バイト毎にCRLFを挿入する
func add76crlf(msg string) string {
var buffer bytes.Buffer
for k, c := range strings.Split(msg, "") {
buffer.WriteString(c)
if k%76 == 75 {
buffer.WriteString("\r\n")
}
}
return buffer.String()
}
// UTF8文字列を指定文字数で分割
func utf8Split(utf8string string, length int) []string {
resultString := []string{}
var buffer bytes.Buffer
for k, c := range strings.Split(utf8string, "") {
buffer.WriteString(c)
if k%length == length-1 {
resultString = append(resultString, buffer.String())
buffer.Reset()
}
}
if buffer.Len() > 0 {
resultString = append(resultString, buffer.String())
}
return resultString
}
// サブジェクトをMIMEエンコードする
func encodeSubject(subject string) string {
var buffer bytes.Buffer
buffer.WriteString("Subject:")
for _, line := range utf8Split(subject, 13) {
buffer.WriteString(" =?utf-8?B?")
buffer.WriteString(base64.StdEncoding.EncodeToString([]byte(line)))
buffer.WriteString("?=\r\n")
}
return buffer.String()
}