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}}
+
+ | Attachments: |
+ {{range $i, $e := .}}
+
+ | {{$e.FileName}} |
+ ({{$e.ContentType}}) |
+ View |
+ Download |
+
+ {{end}}
+
+{{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