1package mail
2
3import (
4	"io"
5	"strings"
6
7	"github.com/emersion/go-message"
8)
9
10func initInlineContentTransferEncoding(h *message.Header) {
11	if !h.Has("Content-Transfer-Encoding") {
12		t, _, _ := h.ContentType()
13		if strings.HasPrefix(t, "text/") {
14			h.Set("Content-Transfer-Encoding", "quoted-printable")
15		} else {
16			h.Set("Content-Transfer-Encoding", "base64")
17		}
18	}
19}
20
21func initInlineHeader(h *InlineHeader) {
22	h.Set("Content-Disposition", "inline")
23	initInlineContentTransferEncoding(&h.Header)
24}
25
26func initAttachmentHeader(h *AttachmentHeader) {
27	disp, _, _ := h.ContentDisposition()
28	if disp != "attachment" {
29		h.Set("Content-Disposition", "attachment")
30	}
31	if !h.Has("Content-Transfer-Encoding") {
32		h.Set("Content-Transfer-Encoding", "base64")
33	}
34}
35
36// A Writer writes a mail message. A mail message contains one or more text
37// parts and zero or more attachments.
38type Writer struct {
39	mw *message.Writer
40}
41
42// CreateWriter writes a mail header to w and creates a new Writer.
43func CreateWriter(w io.Writer, header Header) (*Writer, error) {
44	header.Set("Content-Type", "multipart/mixed")
45
46	mw, err := message.CreateWriter(w, header.Header)
47	if err != nil {
48		return nil, err
49	}
50
51	return &Writer{mw}, nil
52}
53
54// CreateSingleInlineWriter writes a mail header to w. The mail will contain a
55// single inline part. The body of the part should be written to the returned
56// io.WriteCloser. Only one single inline part should be written, use
57// CreateWriter if you want multiple parts.
58func CreateSingleInlineWriter(w io.Writer, header Header) (io.WriteCloser, error) {
59	initInlineContentTransferEncoding(&header.Header)
60	return message.CreateWriter(w, header.Header)
61}
62
63// CreateInline creates a InlineWriter. One or more parts representing the same
64// text in different formats can be written to a InlineWriter.
65func (w *Writer) CreateInline() (*InlineWriter, error) {
66	var h message.Header
67	h.Set("Content-Type", "multipart/alternative")
68
69	mw, err := w.mw.CreatePart(h)
70	if err != nil {
71		return nil, err
72	}
73	return &InlineWriter{mw}, nil
74}
75
76// CreateSingleInline creates a new single text part with the provided header.
77// The body of the part should be written to the returned io.WriteCloser. Only
78// one single text part should be written, use CreateInline if you want multiple
79// text parts.
80func (w *Writer) CreateSingleInline(h InlineHeader) (io.WriteCloser, error) {
81	initInlineHeader(&h)
82	return w.mw.CreatePart(h.Header)
83}
84
85// CreateAttachment creates a new attachment with the provided header. The body
86// of the part should be written to the returned io.WriteCloser.
87func (w *Writer) CreateAttachment(h AttachmentHeader) (io.WriteCloser, error) {
88	initAttachmentHeader(&h)
89	return w.mw.CreatePart(h.Header)
90}
91
92// Close finishes the Writer.
93func (w *Writer) Close() error {
94	return w.mw.Close()
95}
96
97// InlineWriter writes a mail message's text.
98type InlineWriter struct {
99	mw *message.Writer
100}
101
102// CreatePart creates a new text part with the provided header. The body of the
103// part should be written to the returned io.WriteCloser.
104func (w *InlineWriter) CreatePart(h InlineHeader) (io.WriteCloser, error) {
105	initInlineHeader(&h)
106	return w.mw.CreatePart(h.Header)
107}
108
109// Close finishes the InlineWriter.
110func (w *InlineWriter) Close() error {
111	return w.mw.Close()
112}
113