package googleapi import ( "gogs.mikescher.com/BlackForestBytes/goext/langext" "mime" "strings" "time" ) // https://datatracker.ietf.org/doc/html/rfc2822 func encodeMimeMail(from string, recipients []string, cc []string, bcc []string, subject string, body MailBody, attachments []MailAttachment) string { data := make([]string, 0, 32) data = append(data, "Date: "+time.Now().Format(time.RFC1123Z)) data = append(data, "MIME-Version: 1.0") data = append(data, "From: "+mime.QEncoding.Encode("UTF-8", from)) data = append(data, "To: "+strings.Join(langext.ArrMap(recipients, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", ")) if len(cc) > 0 { data = append(data, "To: "+strings.Join(langext.ArrMap(cc, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", ")) } if len(bcc) > 0 { data = append(data, "Bcc: "+strings.Join(langext.ArrMap(bcc, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", ")) } data = append(data, "Subject: "+mime.QEncoding.Encode("UTF-8", subject)) hasInlineAttachments := langext.ArrAny(attachments, func(v MailAttachment) bool { return v.IsInline }) hasNormalAttachments := langext.ArrAny(attachments, func(v MailAttachment) bool { return !v.IsInline }) hasPlain := body.Plain != "" hasHTML := body.HTML != "" mixedBoundary := langext.MustRawHexUUID() relatedBoundary := langext.MustRawHexUUID() altBoundary := langext.MustRawHexUUID() inlineAttachments := langext.ArrFilter(attachments, func(v MailAttachment) bool { return v.IsInline }) normalAttachments := langext.ArrFilter(attachments, func(v MailAttachment) bool { return !v.IsInline }) if hasInlineAttachments && hasNormalAttachments { // "mixed+related" data = append(data, "Content-Type: multipart/mixed; boundary="+mixedBoundary) data = append(data, "") data = append(data, "--"+mixedBoundary) data = append(data, "Content-Type: multipart/related; boundary="+relatedBoundary) data = append(data, "") data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, relatedBoundary, altBoundary)...) data = append(data, "") for i, attachment := range inlineAttachments { data = append(data, "--"+relatedBoundary) data = append(data, attachment.dump()...) if i < len(inlineAttachments)-1 { data = append(data, "") } } data = append(data, "--"+relatedBoundary+"--") for i, attachment := range normalAttachments { data = append(data, "--"+mixedBoundary) data = append(data, attachment.dump()...) if i < len(normalAttachments)-1 { data = append(data, "") } } data = append(data, "--"+mixedBoundary+"--") } else if hasNormalAttachments { // "mixed" data = append(data, "Content-Type: multipart/mixed; boundary="+mixedBoundary) data = append(data, "") data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, mixedBoundary, altBoundary)...) if hasPlain && hasHTML { data = append(data, "") } for i, attachment := range normalAttachments { data = append(data, "--"+mixedBoundary) data = append(data, attachment.dump()...) if i < len(normalAttachments)-1 { data = append(data, "") } } data = append(data, "--"+mixedBoundary+"--") } else if hasInlineAttachments { // "related" data = append(data, "Content-Type: multipart/related; boundary="+relatedBoundary) data = append(data, "") data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, relatedBoundary, altBoundary)...) data = append(data, "") for i, attachment := range inlineAttachments { data = append(data, "--"+relatedBoundary) data = append(data, attachment.dump()...) if i < len(inlineAttachments)-1 { data = append(data, "") } } data = append(data, "--"+relatedBoundary+"--") } else if hasPlain && hasHTML { // "alternative" data = append(data, "Content-Type: multipart/alternative; boundary="+altBoundary) data = append(data, "") data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, altBoundary, altBoundary)...) data = append(data, "") data = append(data, "--"+altBoundary+"--") } else if hasPlain { // "plain" data = append(data, "Content-Type: text/plain; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.Plain) } else if hasHTML { // "plain" data = append(data, "Content-Type: text/html; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.HTML) } else { // "empty??" } return strings.Join(data, "\r\n") } func dumpMailBody(body MailBody, hasInlineAttachments bool, hasNormalAttachments bool, boundary string, boundaryAlt string) []string { if body.HTML != "" && body.Plain != "" && !hasInlineAttachments && hasNormalAttachments { data := make([]string, 0, 16) data = append(data, "--"+boundary) data = append(data, "Content-Type: multipart/alternative; boundary="+boundaryAlt) data = append(data, "") data = append(data, "--"+boundaryAlt) data = append(data, "Content-Type: text/plain; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.Plain) data = append(data, "") data = append(data, "--"+boundaryAlt) data = append(data, "Content-Type: text/html; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.HTML) data = append(data, "") data = append(data, "--"+boundaryAlt+"--") return data } if body.HTML != "" && body.Plain != "" && hasInlineAttachments { data := make([]string, 0, 2) data = append(data, "--"+boundary) data = append(data, body.HTML) return data } if body.HTML != "" && body.Plain != "" { data := make([]string, 0, 8) data = append(data, "--"+boundary) data = append(data, "Content-Type: text/plain; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.Plain) data = append(data, "") data = append(data, "--"+boundary) data = append(data, "Content-Type: text/html; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.HTML) return data } if body.HTML != "" { data := make([]string, 0, 2) data = append(data, "--"+boundary) data = append(data, "Content-Type: text/html; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.HTML) return data } if body.Plain != "" { data := make([]string, 0, 2) data = append(data, "--"+boundary) data = append(data, "Content-Type: text/plain; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, body.Plain) return data } data := make([]string, 0, 16) data = append(data, "--"+boundary) data = append(data, "Content-Type: text/plain; charset=UTF-8") data = append(data, "Content-Transfer-Encoding: 7bit") data = append(data, "") data = append(data, "") // no content ?!? return data }