From 2c7419c661f2642381be29baadf7c6a2177d186d Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 21 Oct 2012 13:42:34 -0700 Subject: [PATCH] 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 --- README.md | 10 +- conf/app.conf | 28 --- conf/inbucket.conf | 4 +- conf/routes | 21 --- datastore.go | 4 +- themes/integral/public/main.css | 2 +- themes/integral/templates/errors.html | 10 - themes/integral/templates/errors/404.html | 20 -- themes/integral/templates/errors/500.html | 16 -- .../mailbox/{html.html => _html.html} | 0 .../mailbox/{show.html => _show.html} | 0 themes/integral/templates/menu.html | 4 - web/mailbox_controller.go | 173 ++++++++++-------- web/server.go | 18 +- 14 files changed, 110 insertions(+), 200 deletions(-) delete mode 100644 conf/app.conf delete mode 100644 conf/routes delete mode 100644 themes/integral/templates/errors.html delete mode 100644 themes/integral/templates/errors/404.html delete mode 100644 themes/integral/templates/errors/500.html rename themes/integral/templates/mailbox/{html.html => _html.html} (100%) rename themes/integral/templates/mailbox/{show.html => _show.html} (100%) delete mode 100644 themes/integral/templates/menu.html diff --git a/README.md b/README.md index b666dab..b890dae 100644 --- a/README.md +++ b/README.md @@ -39,20 +39,16 @@ Installation You will need a functioning [Go installation][1] for this to work. # From the base of your GOPATH... - go get github.com/robfig/revel go get github.com/jhillyerd/inbucket - go build -o bin/revel github.com/robfig/revel/cmd - bin/revel run github.com/jhillyerd/inbucket + go build 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 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 ----- -Inbucket is written in [Google Go][1], and utilizes the [Revel framework][2] -for its web interface. +Inbucket is written in [Google Go][1]. Inbucket is open source software released under the MIT License. The latest version can be found at https://github.com/jhillyerd/inbucket diff --git a/conf/app.conf b/conf/app.conf deleted file mode 100644 index 9e44eb3..0000000 --- a/conf/app.conf +++ /dev/null @@ -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 diff --git a/conf/inbucket.conf b/conf/inbucket.conf index 5c8494e..e97ae3a 100644 --- a/conf/inbucket.conf +++ b/conf/inbucket.conf @@ -17,7 +17,7 @@ ip4.address=0.0.0.0 ip4.port=2500 # used in SMTP greeting -domain=skynet +domain=inbucket.local ############################################################################# [web] @@ -35,7 +35,7 @@ theme=integral template.dir=%(install.dir)s/themes/%(theme)s/templates # 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 public.dir=%(install.dir)s/themes/%(theme)s/public diff --git a/conf/routes b/conf/routes deleted file mode 100644 index b7633cd..0000000 --- a/conf/routes +++ /dev/null @@ -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} diff --git a/datastore.go b/datastore.go index 1c7ac67..ef7e4a2 100644 --- a/datastore.go +++ b/datastore.go @@ -160,8 +160,6 @@ func (m *Message) gobPath() string { } func (m *Message) rawPath() string { - Trace(m.mailbox.path) - Trace(m.Id) 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 func (m *Message) Delete() error { + Trace("Deleting %v", m.gobPath()) err := os.Remove(m.gobPath()) if err != nil { return err } + Trace("Deleting %v", m.rawPath()) return os.Remove(m.rawPath()) } diff --git a/themes/integral/public/main.css b/themes/integral/public/main.css index 81d9925..3921b25 100644 --- a/themes/integral/public/main.css +++ b/themes/integral/public/main.css @@ -285,7 +285,7 @@ a:hover { } #emailBody { - color: #000000; + color: #555; margin-top: 15px; } diff --git a/themes/integral/templates/errors.html b/themes/integral/templates/errors.html deleted file mode 100644 index eae587d..0000000 --- a/themes/integral/templates/errors.html +++ /dev/null @@ -1,10 +0,0 @@ -{{if .errors}} -
-

Please fix the following errors and resubmit:

-

-
-{{end}} diff --git a/themes/integral/templates/errors/404.html b/themes/integral/templates/errors/404.html deleted file mode 100644 index ebdfe10..0000000 --- a/themes/integral/templates/errors/404.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Not found - - -{{if eq .RunMode "dev"}} -{{template "errors/404-dev.html" .}} -{{else}} - {{with .Error}} -

- {{.Title}} -

-

- {{.Description}} -

- {{end}} -{{end}} - - diff --git a/themes/integral/templates/errors/500.html b/themes/integral/templates/errors/500.html deleted file mode 100644 index 0cef4de..0000000 --- a/themes/integral/templates/errors/500.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - Application error - - - {{if eq .RunMode "dev"}} - {{template "errors/500-dev.html" .}} - {{else}} -

Oops, an error occured

-

- This exception has been logged. -

- {{end}} - - diff --git a/themes/integral/templates/mailbox/html.html b/themes/integral/templates/mailbox/_html.html similarity index 100% rename from themes/integral/templates/mailbox/html.html rename to themes/integral/templates/mailbox/_html.html diff --git a/themes/integral/templates/mailbox/show.html b/themes/integral/templates/mailbox/_show.html similarity index 100% rename from themes/integral/templates/mailbox/show.html rename to themes/integral/templates/mailbox/_show.html diff --git a/themes/integral/templates/menu.html b/themes/integral/templates/menu.html deleted file mode 100644 index 906b49f..0000000 --- a/themes/integral/templates/menu.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/web/mailbox_controller.go b/web/mailbox_controller.go index 043fd54..00fc7c7 100644 --- a/web/mailbox_controller.go +++ b/web/mailbox_controller.go @@ -2,6 +2,8 @@ package web import ( "github.com/jhillyerd/inbucket" + "html/template" + "io" "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) { - 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) + 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 } - ds := inbucket.NewDataStore() - mb, err := ds.MailboxFor(name) + mb, err := ctx.DataStore.MailboxFor(name) if err != nil { - return c.RenderError(err) + return err } message, err := mb.GetMessage(id) if err != nil { - return c.RenderError(err) + return err } _, mime, err := message.ReadBody() if err != nil { - return c.RenderError(err) + return err } body := template.HTML(inbucket.TextToHtml(mime.Text)) htmlAvailable := mime.Html != "" - c.Response.Out.Header().Set("Expires", "-1") - return c.Render(name, message, body, htmlAvailable) + return RenderPartial("mailbox/_show.html", w, map[string]interface{}{ + "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) { - 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) + 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 } - ds := inbucket.NewDataStore() - mb, err := ds.MailboxFor(name) + mb, err := ctx.DataStore.MailboxFor(name) if err != nil { - return c.RenderError(err) + return err } message, err := mb.GetMessage(id) if err != nil { - return c.RenderError(err) + return err } _, mime, err := message.ReadBody() if err != nil { - return c.RenderError(err) + return err } - // Mark as safe to render HTML - // TODO: It is not really safe to render, need to sanitize. - body := template.HTML(mime.Html) - c.Response.Out.Header().Set("Expires", "-1") - return c.Render(name, message, body) + 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. + "body": template.HTML(mime.Html), + }) } -func (c Mailbox) Source(name string, id string) rev.Result { func MailboxSource(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) + 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 } - ds := inbucket.NewDataStore() - mb, err := ds.MailboxFor(name) + mb, err := ctx.DataStore.MailboxFor(name) if err != nil { - return c.RenderError(err) + return err } message, err := mb.GetMessage(id) if err != nil { - return c.RenderError(err) + return err } raw, err := message.ReadRaw() if err != nil { - return c.RenderError(err) + return err } - c.Response.Out.Header().Set("Expires", "-1") - return c.RenderText(*raw) + w.Header().Set("Content-Type", "text/plain") + 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 } -*/ diff --git a/web/server.go b/web/server.go index 7effd92..f832641 100644 --- a/web/server.go +++ b/web/server.go @@ -15,17 +15,6 @@ import ( 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 sessionStore sessions.Store @@ -44,6 +33,10 @@ func setupRoutes(cfg inbucket.WebConfig) { r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").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/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 @@ -73,6 +66,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Create the context ctx, err := NewContext(req) if err != nil { + inbucket.Error("Failed to create context: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -80,6 +74,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Run the handler, grab the error, and report it buf := new(httpbuf.Buffer) + inbucket.Trace("Web: %v %v %v %v", req.RemoteAddr, req.Proto, req.Method, req.RequestURI) err = h(buf, req, ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -88,6 +83,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Save the session if err = ctx.Session.Save(req, buf); err != nil { + inbucket.Error("Failed to save session: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return }