summaryrefslogtreecommitdiff
path: root/lib/attachment.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/attachment.go')
-rw-r--r--lib/attachment.go136
1 files changed, 136 insertions, 0 deletions
diff --git a/lib/attachment.go b/lib/attachment.go
new file mode 100644
index 0000000..d7cbf3f
--- /dev/null
+++ b/lib/attachment.go
@@ -0,0 +1,136 @@
+package lib
+
+import (
+ "bufio"
+ "io"
+ "mime"
+ "net/http"
+ "os"
+ "path/filepath"
+
+ "github.com/emersion/go-message/mail"
+ "github.com/pkg/errors"
+)
+
+type Part struct {
+ MimeType string
+ Params map[string]string
+ Body io.Reader
+}
+
+func NewPart(mimetype string, params map[string]string, body io.Reader) *Part {
+ return &Part{
+ MimeType: mimetype,
+ Params: params,
+ Body: body,
+ }
+}
+
+type Attachment interface {
+ Name() string
+ WriteTo(w *mail.Writer) error
+}
+
+type FileAttachment struct {
+ path string
+}
+
+func NewFileAttachment(path string) *FileAttachment {
+ return &FileAttachment{
+ path,
+ }
+}
+
+func (fa *FileAttachment) Name() string {
+ return fa.path
+}
+
+func (fa *FileAttachment) WriteTo(w *mail.Writer) error {
+ f, err := os.Open(fa.path)
+ if err != nil {
+ return errors.Wrap(err, "os.Open")
+ }
+ defer f.Close()
+
+ reader := bufio.NewReader(f)
+
+ // if we have an extension, prefer that instead of trying to sniff the header.
+ // That's generally more accurate than sniffing as lots of things are zip files
+ // under the hood, e.g. most office file types
+ ext := filepath.Ext(fa.path)
+ var mimeString string
+ if mimeString = mime.TypeByExtension(ext); mimeString != "" {
+ // found it in the DB
+ } else {
+ // Sniff the mime type instead
+ // http.DetectContentType only cares about the first 512 bytes
+ head, err := reader.Peek(512)
+ if err != nil && err != io.EOF {
+ return errors.Wrap(err, "Peek")
+ }
+ mimeString = http.DetectContentType(head)
+ }
+
+ // mimeString can contain type and params (like text encoding),
+ // so we need to break them apart before passing them to the headers
+ mimeType, params, err := mime.ParseMediaType(mimeString)
+ if err != nil {
+ return errors.Wrap(err, "ParseMediaType")
+ }
+ filename := filepath.Base(fa.path)
+ params["name"] = filename
+
+ // set header fields
+ ah := mail.AttachmentHeader{}
+ ah.SetContentType(mimeType, params)
+ // setting the filename auto sets the content disposition
+ ah.SetFilename(filename)
+
+ aw, err := w.CreateAttachment(ah)
+ if err != nil {
+ return errors.Wrap(err, "CreateAttachment")
+ }
+ defer aw.Close()
+
+ if _, err := reader.WriteTo(aw); err != nil {
+ return errors.Wrap(err, "reader.WriteTo")
+ }
+
+ return nil
+}
+
+type PartAttachment struct {
+ part *Part
+ name string
+}
+
+func NewPartAttachment(part *Part, name string) *PartAttachment {
+ return &PartAttachment{
+ part,
+ name,
+ }
+}
+
+func (pa *PartAttachment) Name() string {
+ return pa.name
+}
+
+func (pa *PartAttachment) WriteTo(w *mail.Writer) error {
+ // set header fields
+ ah := mail.AttachmentHeader{}
+ ah.SetContentType(pa.part.MimeType, pa.part.Params)
+
+ // setting the filename auto sets the content disposition
+ ah.SetFilename(pa.Name())
+
+ aw, err := w.CreateAttachment(ah)
+ if err != nil {
+ return errors.Wrap(err, "CreateAttachment")
+ }
+ defer aw.Close()
+
+ if _, err := io.Copy(aw, pa.part.Body); err != nil {
+ return errors.Wrap(err, "io.Copy")
+ }
+ return nil
+}