1
0
Fork 0
golang-github-domodwyer-mai.../attachments.go
Daniel Baumann cb9cbb7a25
Adding upstream version 3.6.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-22 21:13:13 +02:00

166 lines
4.5 KiB
Go

package mailyak
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"net/textproto"
)
// DetectContentType needs at most 512 bytes
const sniffLen = 512
type partCreator interface {
CreatePart(header textproto.MIMEHeader) (io.Writer, error)
}
type writeWrapper interface {
new(w io.Writer) io.Writer
}
type attachment struct {
filename string
content io.Reader
inline bool
mimeType string
}
// Attach adds the contents of r to the email as an attachment with name as the
// filename.
//
// r is not read until Send is called and the MIME type will be detected
// using https://golang.org/pkg/net/http/#DetectContentType
func (m *MailYak) Attach(name string, r io.Reader) {
m.attachments = append(m.attachments, attachment{
filename: name,
content: r,
inline: false,
})
}
// AttachWithMimeType adds the contents of r to the email as an attachment with
// name as the filename and mimeType as the specified MIME type of the content.
// It is up to the user to ensure the mimeType is correct.
//
// r is not read until Send is called.
func (m *MailYak) AttachWithMimeType(name string, r io.Reader, mimeType string) {
m.attachments = append(m.attachments, attachment{
filename: name,
content: r,
inline: false,
mimeType: mimeType,
})
}
// AttachInline adds the contents of r to the email as an inline attachment.
// Inline attachments are typically used within the email body, such as a logo
// or header image. It is up to the user to ensure name is unique.
//
// Files can be referenced by their name within the email using the cid URL
// protocol:
//
// <img src="cid:myFileName"/>
//
// r is not read until Send is called and the MIME type will be detected
// using https://golang.org/pkg/net/http/#DetectContentType
func (m *MailYak) AttachInline(name string, r io.Reader) {
m.attachments = append(m.attachments, attachment{
filename: name,
content: r,
inline: true,
})
}
// AttachInlineWithMimeType adds the contents of r to the email as an inline attachment
// with mimeType as the specified MIME type of the content. Inline attachments are
// typically used within the email body, such as a logo or header image. It is up to the
// user to ensure name is unique and the specified mimeType is correct.
//
// Files can be referenced by their name within the email using the cid URL
// protocol:
//
// <img src="cid:myFileName"/>
//
// r is not read until Send is called.
func (m *MailYak) AttachInlineWithMimeType(name string, r io.Reader, mimeType string) {
m.attachments = append(m.attachments, attachment{
filename: name,
content: r,
inline: true,
mimeType: mimeType,
})
}
// ClearAttachments removes all current attachments.
func (m *MailYak) ClearAttachments() {
m.attachments = []attachment{}
}
// writeAttachments loops over the attachments, guesses their content-type and
// writes the data as a line-broken base64 string (using the splitter mutator).
func (m *MailYak) writeAttachments(mixed partCreator, splitter writeWrapper) error {
h := make([]byte, sniffLen)
for _, item := range m.attachments {
hLen, err := io.ReadFull(item.content, h)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
return err
}
if item.mimeType == "" {
item.mimeType = http.DetectContentType(h[:hLen])
}
ctype := fmt.Sprintf("%s;\n\tfilename=%q; name=%q", item.mimeType, item.filename, item.filename)
part, err := mixed.CreatePart(getMIMEHeader(item, ctype))
if err != nil {
return err
}
encoder := base64.NewEncoder(base64.StdEncoding, splitter.new(part))
if _, err := encoder.Write(h[:hLen]); err != nil {
return err
}
// More to write?
if hLen == sniffLen {
if _, err := io.Copy(encoder, item.content); err != nil {
return err
}
}
if err := encoder.Close(); err != nil {
return err
}
}
return nil
}
func getMIMEHeader(a attachment, ctype string) textproto.MIMEHeader {
var disp string
var header textproto.MIMEHeader
cid := fmt.Sprintf("<%s>", a.filename)
if a.inline {
disp = fmt.Sprintf("inline;\n\tfilename=%q; name=%q", a.filename, a.filename)
header = textproto.MIMEHeader{
"Content-Type": {ctype},
"Content-Disposition": {disp},
"Content-Transfer-Encoding": {"base64"},
"Content-ID": {cid},
}
} else {
disp = fmt.Sprintf("attachment;\n\tfilename=%q; name=%q", a.filename, a.filename)
header = textproto.MIMEHeader{
"Content-Type": {ctype},
"Content-Disposition": {disp},
"Content-Transfer-Encoding": {"base64"},
"Content-ID": {cid},
}
}
return header
}