1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-18 18:17:03 +00:00

Gorilla is fully operational

- All mailbox actions working: index, list, show, html, source and delete
 - Cleaned up extra files from Revel
 - Took a guess at install process for README.md

This closes #4
This commit is contained in:
James Hillyerd
2012-10-21 13:42:34 -07:00
parent 71bb52a64a
commit 2c7419c661
14 changed files with 110 additions and 200 deletions

View File

@@ -39,20 +39,16 @@ Installation
You will need a functioning [Go installation][1] for this to work. You will need a functioning [Go installation][1] for this to work.
# From the base of your GOPATH... # From the base of your GOPATH...
go get github.com/robfig/revel
go get github.com/jhillyerd/inbucket go get github.com/jhillyerd/inbucket
go build -o bin/revel github.com/robfig/revel/cmd go build github.com/jhillyerd/inbucket
bin/revel run github.com/jhillyerd/inbucket bin/inbucketd src/github.com/jhillyerd/inbucket/conf/inbucket.conf
By default the SMTP server will be listening on localhost port 2500 and By default the SMTP server will be listening on localhost port 2500 and
the web interface will be available at [localhost:9000](http://localhost:9000/). the web interface will be available at [localhost:9000](http://localhost:9000/).
Inbucket's configuration can be found in the `inbucket/conf/app.conf` file.
About About
----- -----
Inbucket is written in [Google Go][1], and utilizes the [Revel framework][2] Inbucket is written in [Google Go][1].
for its web interface.
Inbucket is open source software released under the MIT License. The latest Inbucket is open source software released under the MIT License. The latest
version can be found at https://github.com/jhillyerd/inbucket version can be found at https://github.com/jhillyerd/inbucket

View File

@@ -1,28 +0,0 @@
app.name=inbucket
app.secret=bPlNFGdSC2wd8f2QnFhk5A84JJjKWZdKH9H2FHFuvUs9Jz8UvBHv3Vc5awx39ivu
http.addr=
http.port=9000
[dev]
results.pretty=true
server.watcher=true
smtpd.domain=skynet
smtpd.port=2500
datastore.path=/tmp/inbucket
log.trace.output = stderr
log.info.output = stderr
log.warn.output = stderr
log.error.output = stderr
[prod]
results.pretty=false
server.watcher=false
smtpd.domain=skynet
smtpd.port=2500
datastore.path=/tmp/inbucket
log.trace.output = off
log.info.output = off
log.warn.output = %(app.name)s.log
log.error.output = %(app.name)s.log

View File

@@ -17,7 +17,7 @@ ip4.address=0.0.0.0
ip4.port=2500 ip4.port=2500
# used in SMTP greeting # used in SMTP greeting
domain=skynet domain=inbucket.local
############################################################################# #############################################################################
[web] [web]
@@ -35,7 +35,7 @@ theme=integral
template.dir=%(install.dir)s/themes/%(theme)s/templates template.dir=%(install.dir)s/themes/%(theme)s/templates
# Should we cache parsed templates (set to false during theme dev) # Should we cache parsed templates (set to false during theme dev)
template.cache=false template.cache=true
# Path to the selected themes public (static) files # Path to the selected themes public (static) files
public.dir=%(install.dir)s/themes/%(theme)s/public public.dir=%(install.dir)s/themes/%(theme)s/public

View File

@@ -1,21 +0,0 @@
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
GET / Application.Index
GET /mailbox/{name} Mailbox.Index
GET /mailbox Mailbox.Index
GET /mailbox/list/{name} Mailbox.List
GET /mailbox/show/{name}/{id} Mailbox.Show
GET /mailbox/source/{name}/{id} Mailbox.Source
GET /mailbox/html/{name}/{id} Mailbox.Html
POST /mailbox/delete/{name}/{id} Mailbox.Delete
# Ignore favicon requests
GET /favicon.ico 404
# Map static resources from the /app/public folder to the /public path
GET /public/ staticDir:public
# Catch all
* /{controller}/{action} {controller}.{action}

View File

@@ -160,8 +160,6 @@ func (m *Message) gobPath() string {
} }
func (m *Message) rawPath() string { func (m *Message) rawPath() string {
Trace(m.mailbox.path)
Trace(m.Id)
return filepath.Join(m.mailbox.path, m.Id+".raw") return filepath.Join(m.mailbox.path, m.Id+".raw")
} }
@@ -261,10 +259,12 @@ func (m *Message) Close() error {
// Delete this Message from disk by removing both the gob and raw files // Delete this Message from disk by removing both the gob and raw files
func (m *Message) Delete() error { func (m *Message) Delete() error {
Trace("Deleting %v", m.gobPath())
err := os.Remove(m.gobPath()) err := os.Remove(m.gobPath())
if err != nil { if err != nil {
return err return err
} }
Trace("Deleting %v", m.rawPath())
return os.Remove(m.rawPath()) return os.Remove(m.rawPath())
} }

View File

@@ -285,7 +285,7 @@ a:hover {
} }
#emailBody { #emailBody {
color: #000000; color: #555;
margin-top: 15px; margin-top: 15px;
} }

View File

@@ -1,10 +0,0 @@
{{if .errors}}
<div class="errors">
<p>Please fix the following errors and resubmit:<p>
<ul>
{{range .errors}}
<li>{{.Message}}</li>
{{end}}
</ul>
</div>
{{end}}

View File

@@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
{{if eq .RunMode "dev"}}
{{template "errors/404-dev.html" .}}
{{else}}
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
{{end}}
</body>
</html>

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Application error</title>
</head>
<body>
{{if eq .RunMode "dev"}}
{{template "errors/500-dev.html" .}}
{{else}}
<h1>Oops, an error occured</h1>
<p>
This exception has been logged.
</p>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
<div id="logo">
<h1><a href="/">inbucket</a></h1>
<h2>email testing service</h2>
</div>

View File

@@ -2,6 +2,8 @@ package web
import ( import (
"github.com/jhillyerd/inbucket" "github.com/jhillyerd/inbucket"
"html/template"
"io"
"net/http" "net/http"
) )
@@ -44,124 +46,139 @@ func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err er
}) })
} }
/*
func (c Mailbox) Show(name string, id string) rev.Result {
func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
c.Validation.Required(name).Message("Account name is required") name := ctx.Vars["name"]
c.Validation.Required(id).Message("Message ID is required") id := ctx.Vars["id"]
if len(name) == 0 {
if c.Validation.HasErrors() { ctx.Session.AddFlash("Account name is required", "errors")
c.Validation.Keep() http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
c.FlashParams() return nil
return c.Redirect(Application.Index) }
if len(id) == 0 {
ctx.Session.AddFlash("Message ID is required", "errors")
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
return nil
} }
ds := inbucket.NewDataStore() mb, err := ctx.DataStore.MailboxFor(name)
mb, err := ds.MailboxFor(name)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
message, err := mb.GetMessage(id) message, err := mb.GetMessage(id)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
_, mime, err := message.ReadBody() _, mime, err := message.ReadBody()
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
body := template.HTML(inbucket.TextToHtml(mime.Text)) body := template.HTML(inbucket.TextToHtml(mime.Text))
htmlAvailable := mime.Html != "" htmlAvailable := mime.Html != ""
c.Response.Out.Header().Set("Expires", "-1") return RenderPartial("mailbox/_show.html", w, map[string]interface{}{
return c.Render(name, message, body, htmlAvailable) "ctx": ctx,
"name": name,
"message": message,
"body": body,
"htmlAvailable": htmlAvailable,
})
} }
func (c Mailbox) Delete(name string, id string) rev.Result {
func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
c.Validation.Required(name).Message("Account name is required")
c.Validation.Required(id).Message("Message ID is required")
if c.Validation.HasErrors() {
c.Validation.Keep()
c.FlashParams()
return c.Redirect(Application.Index)
}
ds := inbucket.NewDataStore()
mb, err := ds.MailboxFor(name)
if err != nil {
return c.RenderError(err)
}
message, err := mb.GetMessage(id)
if err != nil {
return c.RenderError(err)
}
err = message.Delete()
if err != nil {
return c.RenderError(err)
}
c.Response.Out.Header().Set("Expires", "-1")
return c.RenderText("OK")
}
func (c Mailbox) Html(name string, id string) rev.Result {
func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
c.Validation.Required(name).Message("Account name is required") name := ctx.Vars["name"]
c.Validation.Required(id).Message("Message ID is required") id := ctx.Vars["id"]
if len(name) == 0 {
if c.Validation.HasErrors() { ctx.Session.AddFlash("Account name is required", "errors")
c.Validation.Keep() http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
c.FlashParams() return nil
return c.Redirect(Application.Index) }
if len(id) == 0 {
ctx.Session.AddFlash("Message ID is required", "errors")
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
return nil
} }
ds := inbucket.NewDataStore() mb, err := ctx.DataStore.MailboxFor(name)
mb, err := ds.MailboxFor(name)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
message, err := mb.GetMessage(id) message, err := mb.GetMessage(id)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
_, mime, err := message.ReadBody() _, mime, err := message.ReadBody()
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
// Mark as safe to render HTML
return RenderPartial("mailbox/_html.html", w, map[string]interface{}{
"ctx": ctx,
"name": name,
"message": message,
// TODO: It is not really safe to render, need to sanitize. // TODO: It is not really safe to render, need to sanitize.
body := template.HTML(mime.Html) "body": template.HTML(mime.Html),
})
c.Response.Out.Header().Set("Expires", "-1")
return c.Render(name, message, body)
} }
func (c Mailbox) Source(name string, id string) rev.Result {
func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
c.Validation.Required(name).Message("Account name is required") name := ctx.Vars["name"]
c.Validation.Required(id).Message("Message ID is required") id := ctx.Vars["id"]
if len(name) == 0 {
if c.Validation.HasErrors() { ctx.Session.AddFlash("Account name is required", "errors")
c.Validation.Keep() http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
c.FlashParams() return nil
return c.Redirect(Application.Index) }
if len(id) == 0 {
ctx.Session.AddFlash("Message ID is required", "errors")
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
return nil
} }
ds := inbucket.NewDataStore() mb, err := ctx.DataStore.MailboxFor(name)
mb, err := ds.MailboxFor(name)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
message, err := mb.GetMessage(id) message, err := mb.GetMessage(id)
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
raw, err := message.ReadRaw() raw, err := message.ReadRaw()
if err != nil { if err != nil {
return c.RenderError(err) return err
} }
c.Response.Out.Header().Set("Expires", "-1") w.Header().Set("Content-Type", "text/plain")
return c.RenderText(*raw) io.WriteString(w, *raw)
return nil
}
func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
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 {
return err
}
message, err := mb.GetMessage(id)
if err != nil {
return err
}
err = message.Delete()
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "OK")
return nil
} }
*/

View File

@@ -15,17 +15,6 @@ import (
type handler func(http.ResponseWriter, *http.Request, *Context) error type handler func(http.ResponseWriter, *http.Request, *Context) error
/*
type WebServer struct {
thing string
}
// NewServer() returns a new web.Server instance
func NewWebServer() *Server {
return &WebServer{}
}
*/
var Router *mux.Router var Router *mux.Router
var sessionStore sessions.Store var sessionStore sessions.Store
@@ -44,6 +33,10 @@ func setupRoutes(cfg inbucket.WebConfig) {
r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").Methods("GET") r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").Methods("GET")
r.Path("/mailbox").Handler(handler(MailboxIndex)).Name("MailboxIndex").Methods("GET") r.Path("/mailbox").Handler(handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
r.Path("/mailbox/list/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET") r.Path("/mailbox/list/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET")
r.Path("/mailbox/show/{name}/{id}").Handler(handler(MailboxShow)).Name("MailboxShow").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/delete/{name}/{id}").Handler(handler(MailboxDelete)).Name("MailboxDelete").Methods("POST")
} }
// Start() the web server // Start() the web server
@@ -73,6 +66,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Create the context // Create the context
ctx, err := NewContext(req) ctx, err := NewContext(req)
if err != nil { if err != nil {
inbucket.Error("Failed to create context: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@@ -80,6 +74,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Run the handler, grab the error, and report it // Run the handler, grab the error, and report it
buf := new(httpbuf.Buffer) buf := new(httpbuf.Buffer)
inbucket.Trace("Web: %v %v %v %v", req.RemoteAddr, req.Proto, req.Method, req.RequestURI)
err = h(buf, req, ctx) err = h(buf, req, ctx)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -88,6 +83,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Save the session // Save the session
if err = ctx.Session.Save(req, buf); err != nil { if err = ctx.Session.Save(req, buf); err != nil {
inbucket.Error("Failed to save session: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }