diff --git a/conf/inbucket.conf b/conf/inbucket.conf index 0aed6ac..ff64374 100644 --- a/conf/inbucket.conf +++ b/conf/inbucket.conf @@ -32,10 +32,10 @@ ip4.port=9000 theme=integral # Path to the selected themes template files -templates.dir=%(install.dir)s/themes/%(theme)/templates +templates.dir=%(install.dir)s/themes/%(theme)s/templates # Path to the selected themes public (static) files -public.dir=%(install.dir)s/themes/%(theme)/public +public.dir=%(install.dir)s/themes/%(theme)s/public ############################################################################# [datastore] diff --git a/config.go b/config.go index 535acab..beee858 100644 --- a/config.go +++ b/config.go @@ -16,15 +16,29 @@ type SmtpConfig struct { Domain string } +type WebConfig struct { + Ip4address net.IP + Ip4port int + TemplatesDir string + PublicDir string +} + var smtpConfig *SmtpConfig +var webConfig *WebConfig + var Config *config.Config -// GetSmtpConfig returns a copy of the SmtpConfig +// GetSmtpConfig returns a copy of the SmtpConfig object func GetSmtpConfig() SmtpConfig { return *smtpConfig } +// GetWebConfig returns a copy of the WebConfig object +func GetWebConfig() WebConfig { + return *webConfig +} + // LoadConfig loads the specified configuration file into inbucket.Config // and performs validations on it. func LoadConfig(filename string) error { @@ -66,6 +80,11 @@ func LoadConfig(filename string) error { } err = parseSmtpConfig() + if err != nil { + return nil + } + + err = parseWebConfig() return err } @@ -106,6 +125,49 @@ func parseSmtpConfig() error { return nil } +// parseWebConfig trying to catch config errors early +func parseWebConfig() error { + webConfig = new(WebConfig) + + // Parse IP4 address only, error on IP6. + option := "[web]ip4.address" + str, err := Config.String("web", "ip4.address") + if err != nil { + return fmt.Errorf("Failed to parse %v: %v", option, err) + } + addr := net.ParseIP(str) + if addr == nil { + return fmt.Errorf("Failed to parse %v '%v'", option, str) + } + addr = addr.To4() + if addr == nil { + return fmt.Errorf("Failed to parse %v '%v' not IPv4!", option, str) + } + webConfig.Ip4address = addr + + option = "[web]ip4.port" + webConfig.Ip4port, err = Config.Int("web", "ip4.port") + if err != nil { + return fmt.Errorf("Failed to parse %v: %v", option, err) + } + + option = "[web]templates.dir" + str, err = Config.String("web", "templates.dir") + if err != nil { + return fmt.Errorf("Failed to parse %v: %v", option, err) + } + webConfig.TemplatesDir = str + + option = "[web]public.dir" + str, err = Config.String("web", "public.dir") + if err != nil { + return fmt.Errorf("Failed to parse %v: %v", option, err) + } + webConfig.PublicDir = str + + return nil +} + // requireSection checks that a [section] is defined in the configuration file, // appending a message if not. func requireSection(messages *list.List, section string) { diff --git a/inbucketd/inbucketd.go b/inbucketd/inbucketd.go index 9efcf07..e44d165 100644 --- a/inbucketd/inbucketd.go +++ b/inbucketd/inbucketd.go @@ -8,7 +8,7 @@ import ( "fmt" "github.com/jhillyerd/inbucket" "github.com/jhillyerd/inbucket/smtpd" - "log" + "github.com/jhillyerd/inbucket/web" "os" ) @@ -32,15 +32,11 @@ func main() { os.Exit(1) } - log.Println("Logger test") - inbucket.Trace("trace test") - inbucket.Info("info test") - inbucket.Warn("warn test") - inbucket.Error("error test") - // Startup SMTP server server := smtpd.New() - server.Start() + go server.Start() + + web.Start() } func init() { diff --git a/smtpd/listener.go b/smtpd/listener.go index d58d3d1..57fdc3b 100644 --- a/smtpd/listener.go +++ b/smtpd/listener.go @@ -34,7 +34,7 @@ func (s *Server) Start() { panic(err) } - inbucket.Info("Listening on TCP4 %v", addr) + inbucket.Info("SMTP listening on TCP4 %v", addr) ln, err := net.ListenTCP("tcp4", addr) if err != nil { inbucket.Error("Failed to start tcp4 listener: %v", err) diff --git a/web/app.go b/web/app.go index 185632f..17499a5 100644 --- a/web/app.go +++ b/web/app.go @@ -1,4 +1,4 @@ -package controllers +package web import ( "github.com/jhillyerd/inbucket/app/smtpd" diff --git a/web/helpers.go b/web/helpers.go index 3186d3a..ed18d2f 100644 --- a/web/helpers.go +++ b/web/helpers.go @@ -1,19 +1,35 @@ -package controllers +package web import ( - "github.com/robfig/revel" + "fmt" + "github.com/jhillyerd/inbucket" "html/template" "time" ) -func init() { - rev.TRACE.Println("Registering helpers") - rev.Funcs["friendlyTime"] = func(t time.Time) template.HTML { +var TemplateFuncs = template.FuncMap{ + // Reversable routing function + "reverse": func(name string, things ...interface{}) string { + // Convert the things to strings + strs := make([]string, len(things)) + for i, th := range things { + strs[i] = fmt.Sprint(th) + } + // Grab the route + u, err := Router.Get(name).URL(strs...) + if err != nil { + inbucket.Error("Failed to reverse route: %v", err) + return "/ROUTE-ERROR" + } + return u.Path + }, + // Friendly date & time rendering + "friendlyTime": func(t time.Time) template.HTML { ty, tm, td := t.Date() ny, nm, nd := time.Now().Date() if (ty == ny) && (tm == nm) && (td == nd) { return template.HTML(t.Format("03:04:05 PM")) } return template.HTML(t.Format("Mon Jan 2, 2006")) - } + }, } diff --git a/web/mailbox.go b/web/mailbox.go index 45d1777..cc6550e 100644 --- a/web/mailbox.go +++ b/web/mailbox.go @@ -1,4 +1,4 @@ -package controllers +package web import ( "github.com/jhillyerd/inbucket/app/inbucket" diff --git a/web/server.go b/web/server.go new file mode 100644 index 0000000..082d621 --- /dev/null +++ b/web/server.go @@ -0,0 +1,56 @@ +/* + The web package contains all the code to provide Inbucket's web GUI +*/ +package web + +import ( + "fmt" + "github.com/gorilla/mux" + "github.com/jhillyerd/inbucket" + "net/http" + "time" +) + +/* +type WebServer struct { + thing string +} + +// NewServer() returns a new web.Server instance +func NewWebServer() *Server { + return &WebServer{} +} +*/ + +var Router *mux.Router + +func setupRoutes(cfg inbucket.WebConfig) { + r := mux.NewRouter() + Router = r + inbucket.Info("Theme templates mapped to '%v'", cfg.TemplatesDir) + inbucket.Info("Theme static content mapped to '%v'", cfg.PublicDir) + + // Static content + r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", + http.FileServer(http.Dir(cfg.PublicDir)))) +} + +// Start() the web server +func Start() { + cfg := inbucket.GetWebConfig() + setupRoutes(cfg) + addr := fmt.Sprintf("%v:%v", cfg.Ip4address, cfg.Ip4port) + inbucket.Info("HTTP listening on TCP4 %v", addr) + + s := &http.Server{ + Addr: addr, + Handler: Router, + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + } + + err := s.ListenAndServe() + if err != nil { + inbucket.Error("HTTP server failed: %v", err) + } +}