mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
Attachment display & downloading!
This commit is contained in:
@@ -34,7 +34,7 @@ max.recipients=100
|
|||||||
max.idle.seconds=30
|
max.idle.seconds=30
|
||||||
|
|
||||||
# Maximum allowable size of message body in bytes (including attachments)
|
# 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
|
# Should we place messages into the datastore, or just throw them away
|
||||||
# (for load testing): true or false
|
# (for load testing): true or false
|
||||||
|
|||||||
@@ -327,3 +327,28 @@ table.metrics {
|
|||||||
.metrics td.sparkline {
|
.metrics td.sparkline {
|
||||||
width: 170px;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
{{$name := .name}}
|
||||||
|
{{$id := .message.Id}}
|
||||||
<div id="emailActions">
|
<div id="emailActions">
|
||||||
<a href="javascript:deleteMessage('{{.message.Id}}');">Delete</a>
|
<a href="javascript:deleteMessage('{{.message.Id}}');">Delete</a>
|
||||||
<a href="javascript:messageSource('{{.message.Id}}');">Source</a>
|
<a href="javascript:messageSource('{{.message.Id}}');">Source</a>
|
||||||
@@ -15,6 +17,21 @@
|
|||||||
<td>{{.message.Date}}</td>
|
<td>{{.message.Date}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<table>
|
<table>
|
||||||
|
|
||||||
|
{{with .attachments}}
|
||||||
|
<table id="emailAttachments">
|
||||||
|
<tr><th colspan="4">Attachments:</th></tr>
|
||||||
|
{{range $i, $e := .}}
|
||||||
|
<tr>
|
||||||
|
<td class="fileName">{{$e.FileName}}</td>
|
||||||
|
<td>({{$e.ContentType}})</td>
|
||||||
|
<td><a href="/mailbox/vattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}" target="_blank">View</a></td>
|
||||||
|
<td><a href="/mailbox/dattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}">Download</a></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div id="emailSubject"><h3>{{.message.Subject}}</h3></div>
|
<div id="emailSubject"><h3>{{.message.Subject}}</h3></div>
|
||||||
|
|
||||||
<div id="emailBody">{{.body}}</div>
|
<div id="emailBody">{{.body}}</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
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) {
|
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"]
|
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)
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
if err != nil {
|
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) {
|
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"]
|
name := ctx.Vars["name"]
|
||||||
id := ctx.Vars["id"]
|
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)
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -82,22 +70,14 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err er
|
|||||||
"message": message,
|
"message": message,
|
||||||
"body": body,
|
"body": body,
|
||||||
"htmlAvailable": htmlAvailable,
|
"htmlAvailable": htmlAvailable,
|
||||||
|
"attachments": mime.Attachments,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
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"]
|
name := ctx.Vars["name"]
|
||||||
id := ctx.Vars["id"]
|
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)
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
if err != nil {
|
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) {
|
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"]
|
name := ctx.Vars["name"]
|
||||||
id := ctx.Vars["id"]
|
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)
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,19 +124,83 @@ func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err
|
|||||||
return nil
|
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"]
|
name := ctx.Vars["name"]
|
||||||
id := ctx.Vars["id"]
|
id := ctx.Vars["id"]
|
||||||
if len(name) == 0 {
|
numStr := ctx.Vars["num"]
|
||||||
ctx.Session.AddFlash("Account name is required", "errors")
|
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)
|
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
|
||||||
return nil
|
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)
|
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
|
||||||
return nil
|
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)
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -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/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/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/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
|
// Register w/ HTTP
|
||||||
Router = r
|
Router = r
|
||||||
|
|||||||
Reference in New Issue
Block a user