From 1da98a9f7ea9b0f3c960a0775bff62b98ba1b9a7 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Mon, 5 Nov 2012 21:35:34 -0800 Subject: [PATCH] Attachment display & downloading! --- etc/devel.conf | 2 +- themes/integral/public/main.css | 25 ++++ themes/integral/templates/mailbox/_show.html | 17 +++ web/mailbox_controller.go | 115 ++++++++++++------- web/server.go | 2 + 5 files changed, 120 insertions(+), 41 deletions(-) diff --git a/etc/devel.conf b/etc/devel.conf index 11d42e8..ecb9b14 100644 --- a/etc/devel.conf +++ b/etc/devel.conf @@ -34,7 +34,7 @@ max.recipients=100 max.idle.seconds=30 # Maximum allowable size of message body in bytes (including attachments) -max.message.bytes=2048000 +max.message.bytes=20480000 # Should we place messages into the datastore, or just throw them away # (for load testing): true or false diff --git a/themes/integral/public/main.css b/themes/integral/public/main.css index 0d2d3ae..4dd6f14 100644 --- a/themes/integral/public/main.css +++ b/themes/integral/public/main.css @@ -327,3 +327,28 @@ table.metrics { .metrics td.sparkline { width: 170px; } + +#emailAttachments { + border-collapse: collapse; +} + +#emailAttachments th, #emailAttachments td { + text-align: left; + padding: 0 3px 3px 0; +} + +#emailAttachments .fileName:before { + content: '\203A\00A0'; +} + +#emailAttachments a { + background: #8ac6dc; + color: #fff; + text-decoration: none; + padding: 0 5px; +} + +#emailAttachments a:hover { + background: #becf74; +} + diff --git a/themes/integral/templates/mailbox/_show.html b/themes/integral/templates/mailbox/_show.html index 116debd..bf83fcb 100644 --- a/themes/integral/templates/mailbox/_show.html +++ b/themes/integral/templates/mailbox/_show.html @@ -1,3 +1,5 @@ +{{$name := .name}} +{{$id := .message.Id}}
Delete Source @@ -15,6 +17,21 @@ {{.message.Date}} + +{{with .attachments}} +
+ + {{range $i, $e := .}} + + + + + + + {{end}} +
Attachments:
{{$e.FileName}}({{$e.ContentType}})ViewDownload
+{{end}} +

{{.message.Subject}}

{{.body}}
diff --git a/web/mailbox_controller.go b/web/mailbox_controller.go index 33176fa..3eec6dd 100644 --- a/web/mailbox_controller.go +++ b/web/mailbox_controller.go @@ -6,6 +6,7 @@ import ( "html/template" "io" "net/http" + "strconv" ) func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { @@ -23,12 +24,8 @@ func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err e } func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 name := ctx.Vars["name"] - if len(name) == 0 { - ctx.Session.AddFlash("Account name is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } mb, err := ctx.DataStore.MailboxFor(name) if err != nil { @@ -48,18 +45,9 @@ func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err er } func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 name := ctx.Vars["name"] id := ctx.Vars["id"] - if len(name) == 0 { - ctx.Session.AddFlash("Account name is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } - if len(id) == 0 { - ctx.Session.AddFlash("Message ID is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } mb, err := ctx.DataStore.MailboxFor(name) if err != nil { @@ -82,22 +70,14 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err er "message": message, "body": body, "htmlAvailable": htmlAvailable, + "attachments": mime.Attachments, }) } func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 name := ctx.Vars["name"] id := ctx.Vars["id"] - if len(name) == 0 { - ctx.Session.AddFlash("Account name is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } - if len(id) == 0 { - ctx.Session.AddFlash("Message ID is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } mb, err := ctx.DataStore.MailboxFor(name) if err != nil { @@ -122,18 +102,9 @@ func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err er } func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 name := ctx.Vars["name"] id := ctx.Vars["id"] - if len(name) == 0 { - ctx.Session.AddFlash("Account name is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } - if len(id) == 0 { - ctx.Session.AddFlash("Message ID is required", "errors") - http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) - return nil - } mb, err := ctx.DataStore.MailboxFor(name) if err != nil { @@ -153,19 +124,83 @@ func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err return nil } -func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { +func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 name := ctx.Vars["name"] id := ctx.Vars["id"] - if len(name) == 0 { - ctx.Session.AddFlash("Account name is required", "errors") + numStr := ctx.Vars["num"] + num, err := strconv.ParseUint(numStr, 10, 32) + if err != nil { + ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors") http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) return nil } - if len(id) == 0 { - ctx.Session.AddFlash("Message ID is required", "errors") + + mb, err := ctx.DataStore.MailboxFor(name) + if err != nil { + return err + } + message, err := mb.GetMessage(id) + if err != nil { + return err + } + _, body, err := message.ReadBody() + if err != nil { + return err + } + if int(num) >= len(body.Attachments) { + ctx.Session.AddFlash("Attachment number too high", "errors") http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) return nil } + part := body.Attachments[num] + + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment") + w.Write(part.Content()) + return nil +} + +func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 + name := ctx.Vars["name"] + id := ctx.Vars["id"] + numStr := ctx.Vars["num"] + num, err := strconv.ParseUint(numStr, 10, 32) + if err != nil { + ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors") + http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) + return nil + } + + mb, err := ctx.DataStore.MailboxFor(name) + if err != nil { + return err + } + message, err := mb.GetMessage(id) + if err != nil { + return err + } + _, body, err := message.ReadBody() + if err != nil { + return err + } + if int(num) >= len(body.Attachments) { + ctx.Session.AddFlash("Attachment number too high", "errors") + http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther) + return nil + } + part := body.Attachments[num] + + w.Header().Set("Content-Type", part.ContentType()) + w.Write(part.Content()) + return nil +} + +func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { + // Don't have to validate these aren't empty, Gorilla returns 404 + name := ctx.Vars["name"] + id := ctx.Vars["id"] mb, err := ctx.DataStore.MailboxFor(name) if err != nil { diff --git a/web/server.go b/web/server.go index 534b572..531e741 100644 --- a/web/server.go +++ b/web/server.go @@ -38,6 +38,8 @@ func setupRoutes(cfg config.WebConfig) { r.Path("/mailbox/html/{name}/{id}").Handler(handler(MailboxHtml)).Name("MailboxHtml").Methods("GET") r.Path("/mailbox/source/{name}/{id}").Handler(handler(MailboxSource)).Name("MailboxSource").Methods("GET") r.Path("/mailbox/delete/{name}/{id}").Handler(handler(MailboxDelete)).Name("MailboxDelete").Methods("POST") + r.Path("/mailbox/dattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxDownloadAttach)).Name("MailboxDownloadAttach").Methods("GET") + r.Path("/mailbox/vattach/{name}/{id}/{num}/{file}").Handler(handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET") // Register w/ HTTP Router = r