From e7a86bd8f8ed62595433ba0ed6b4b5260b621654 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Tue, 20 Mar 2018 17:55:43 -0700 Subject: [PATCH] Hide envelope, use Part.Content for #85 --- pkg/message/manager.go | 2 +- pkg/message/message.go | 36 ++++++++++++++++++++++++++++++- pkg/rest/apiv1_controller.go | 28 ++++++++++++------------ pkg/rest/apiv1_controller_test.go | 8 +++---- pkg/webui/mailbox_controller.go | 27 ++++++++++------------- 5 files changed, 65 insertions(+), 36 deletions(-) diff --git a/pkg/message/manager.go b/pkg/message/manager.go index 9ff4d66..5e428e6 100644 --- a/pkg/message/manager.go +++ b/pkg/message/manager.go @@ -121,7 +121,7 @@ func (s *StoreManager) GetMessage(mailbox, id string) (*Message, error) { } _ = r.Close() header := makeMetadata(sm) - return &Message{Metadata: *header, Envelope: env}, nil + return &Message{Metadata: *header, env: env}, nil } // PurgeMessages removes all messages from the specified mailbox. diff --git a/pkg/message/message.go b/pkg/message/message.go index 7f8bec0..8bdd460 100644 --- a/pkg/message/message.go +++ b/pkg/message/message.go @@ -5,6 +5,7 @@ import ( "io" "io/ioutil" "net/mail" + "net/textproto" "time" "github.com/jhillyerd/enmime" @@ -25,7 +26,40 @@ type Metadata struct { // Message holds both the metadata and content of a message. type Message struct { Metadata - Envelope *enmime.Envelope + env *enmime.Envelope +} + +// New constructs a new Message +func New(m Metadata, e *enmime.Envelope) *Message { + return &Message{ + Metadata: m, + env: e, + } +} + +// Attachments returns the MIME attachments for the message. +func (m *Message) Attachments() []*enmime.Part { + return m.env.Attachments +} + +// Header returns the header map for this message. +func (m *Message) Header() textproto.MIMEHeader { + return m.env.Root.Header +} + +// HTML returns the HTML body of the message. +func (m *Message) HTML() string { + return m.env.HTML +} + +// MIMEErrors returns MIME parsing errors and warnings. +func (m *Message) MIMEErrors() []*enmime.Error { + return m.env.Errors +} + +// Text returns the plain text body of the message. +func (m *Message) Text() string { + return m.env.Text } // Delivery is used to add a message to storage. diff --git a/pkg/rest/apiv1_controller.go b/pkg/rest/apiv1_controller.go index c463ecf..34dea29 100644 --- a/pkg/rest/apiv1_controller.go +++ b/pkg/rest/apiv1_controller.go @@ -7,7 +7,6 @@ import ( "crypto/md5" "encoding/hex" - "io/ioutil" "strconv" "github.com/jhillyerd/inbucket/pkg/log" @@ -63,19 +62,20 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) ( // This doesn't indicate empty, likely an IO error return fmt.Errorf("GetMessage(%q) failed: %v", id, err) } - mime := msg.Envelope - attachments := make([]*model.JSONMessageAttachmentV1, len(mime.Attachments)) - for i, att := range mime.Attachments { - var content []byte - content, err = ioutil.ReadAll(att) + attachParts := msg.Attachments() + attachments := make([]*model.JSONMessageAttachmentV1, len(attachParts)) + for i, part := range attachParts { + content := part.Content var checksum = md5.Sum(content) attachments[i] = &model.JSONMessageAttachmentV1{ - ContentType: att.ContentType, - FileName: att.FileName, - DownloadLink: "http://" + req.Host + "/mailbox/dattach/" + name + "/" + id + "/" + strconv.Itoa(i) + "/" + att.FileName, - ViewLink: "http://" + req.Host + "/mailbox/vattach/" + name + "/" + id + "/" + strconv.Itoa(i) + "/" + att.FileName, - MD5: hex.EncodeToString(checksum[:]), + ContentType: part.ContentType, + FileName: part.FileName, + DownloadLink: "http://" + req.Host + "/mailbox/dattach/" + name + "/" + id + "/" + + strconv.Itoa(i) + "/" + part.FileName, + ViewLink: "http://" + req.Host + "/mailbox/vattach/" + name + "/" + id + "/" + + strconv.Itoa(i) + "/" + part.FileName, + MD5: hex.EncodeToString(checksum[:]), } } @@ -88,10 +88,10 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) ( Subject: msg.Subject, Date: msg.Date, Size: msg.Size, - Header: mime.Root.Header, + Header: msg.Header(), Body: &model.JSONMessageBodyV1{ - Text: mime.Text, - HTML: mime.HTML, + Text: msg.Text(), + HTML: msg.HTML(), }, Attachments: attachments, }) diff --git a/pkg/rest/apiv1_controller_test.go b/pkg/rest/apiv1_controller_test.go index 4906d21..56fa403 100644 --- a/pkg/rest/apiv1_controller_test.go +++ b/pkg/rest/apiv1_controller_test.go @@ -172,8 +172,8 @@ func TestRestMessage(t *testing.T) { } // Test JSON message headers - msg1 := &message.Message{ - Metadata: message.Metadata{ + msg1 := message.New( + message.Metadata{ Mailbox: "good", ID: "0001", From: &mail.Address{Name: "", Address: "from1@host"}, @@ -181,7 +181,7 @@ func TestRestMessage(t *testing.T) { Subject: "subject 1", Date: time.Date(2012, 2, 1, 10, 11, 12, 253, time.FixedZone("PST", -800)), }, - Envelope: &enmime.Envelope{ + &enmime.Envelope{ Text: "This is some text", HTML: "This is some HTML", Root: &enmime.Part{ @@ -191,7 +191,7 @@ func TestRestMessage(t *testing.T) { }, }, }, - } + ) mm.AddMessage("good", msg1) // Check return code diff --git a/pkg/webui/mailbox_controller.go b/pkg/webui/mailbox_controller.go index d876b3a..e835d78 100644 --- a/pkg/webui/mailbox_controller.go +++ b/pkg/webui/mailbox_controller.go @@ -102,12 +102,11 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *web.Context) (er // This doesn't indicate empty, likely an IO error return fmt.Errorf("GetMessage(%q) failed: %v", id, err) } - mime := msg.Envelope - body := template.HTML(web.TextToHTML(mime.Text)) - htmlAvailable := mime.HTML != "" + body := template.HTML(web.TextToHTML(msg.Text())) + htmlAvailable := msg.HTML() != "" var htmlBody template.HTML if htmlAvailable { - if str, err := sanitize.HTML(mime.HTML); err == nil { + if str, err := sanitize.HTML(msg.HTML()); err == nil { htmlBody = template.HTML(str) } else { log.Warnf("HTML sanitizer failed: %s", err) @@ -121,8 +120,8 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *web.Context) (er "body": body, "htmlAvailable": htmlAvailable, "htmlBody": htmlBody, - "mimeErrors": mime.Errors, - "attachments": mime.Attachments, + "mimeErrors": msg.MIMEErrors(), + "attachments": msg.Attachments(), }) } @@ -143,14 +142,13 @@ func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *web.Context) (er // This doesn't indicate empty, likely an IO error return fmt.Errorf("GetMessage(%q) failed: %v", id, err) } - mime := msg.Envelope // Render partial template w.Header().Set("Content-Type", "text/html; charset=UTF-8") return web.RenderPartial("mailbox/_html.html", w, map[string]interface{}{ "ctx": ctx, "name": name, "message": msg, - "body": template.HTML(mime.HTML), + "body": template.HTML(msg.HTML()), }) } @@ -206,18 +204,16 @@ func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *web.Co // This doesn't indicate empty, likely an IO error return fmt.Errorf("GetMessage(%q) failed: %v", id, err) } - body := msg.Envelope - if int(num) >= len(body.Attachments) { + if int(num) >= len(msg.Attachments()) { ctx.Session.AddFlash("Attachment number too high", "errors") _ = ctx.Session.Save(req, w) http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther) return nil } - part := body.Attachments[num] // Output attachment w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", "attachment") - _, err = io.Copy(w, part) + _, err = w.Write(msg.Attachments()[num].Content) return err } @@ -249,16 +245,15 @@ func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *web.Contex // This doesn't indicate empty, likely an IO error return fmt.Errorf("GetMessage(%q) failed: %v", id, err) } - body := msg.Envelope - if int(num) >= len(body.Attachments) { + if int(num) >= len(msg.Attachments()) { ctx.Session.AddFlash("Attachment number too high", "errors") _ = ctx.Session.Save(req, w) http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther) return nil } - part := body.Attachments[num] // Output attachment + part := msg.Attachments()[num] w.Header().Set("Content-Type", part.ContentType) - _, err = io.Copy(w, part) + _, err = w.Write(part.Content) return err }