mirror of
https://github.com/jhillyerd/inbucket.git
synced 2026-02-07 19:06:13 +00:00
Merge branch 'feature/pkg' into develop, closes #79
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -26,8 +26,12 @@ _testmain.go
|
||||
*.swo
|
||||
|
||||
# our binaries
|
||||
/client
|
||||
/client.exe
|
||||
/inbucket
|
||||
/inbucket.exe
|
||||
/dist/**
|
||||
/cmd/client/client
|
||||
/cmd/client/client.exe
|
||||
/cmd/inbucket/inbucket
|
||||
/cmd/inbucket/inbucket.exe
|
||||
|
||||
@@ -2,13 +2,12 @@ language: go
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
- DEPLOY_WITH_MAJOR="1.9"
|
||||
- DEPLOY_WITH_MAJOR="1.10"
|
||||
|
||||
before_script:
|
||||
- go get github.com/golang/lint/golint
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- "1.10"
|
||||
|
||||
deploy:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Docker build file for Inbucket, see https://www.docker.io/
|
||||
# Inbucket website: http://www.inbucket.org/
|
||||
|
||||
FROM golang:1.9-alpine
|
||||
FROM golang:1.10-alpine
|
||||
MAINTAINER James Hillyerd, @jameshillyerd
|
||||
|
||||
# Configuration (WORKDIR doesn't support env vars)
|
||||
|
||||
29
Makefile
29
Makefile
@@ -1,26 +1,27 @@
|
||||
PKG := inbucket
|
||||
SHELL := /bin/sh
|
||||
SHELL = /bin/sh
|
||||
|
||||
SRC := $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
||||
PKGS := $$(go list ./... | grep -v /vendor/)
|
||||
PKGS := $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
.PHONY: all build clean fmt install lint simplify test
|
||||
.PHONY: all build clean fmt lint simplify test
|
||||
|
||||
all: test lint build
|
||||
commands = client inbucket
|
||||
|
||||
all: clean test lint build
|
||||
|
||||
$(commands): %: cmd/%
|
||||
go build ./$<
|
||||
|
||||
clean:
|
||||
go clean
|
||||
go clean $(PKGS)
|
||||
rm -f $(commands)
|
||||
|
||||
deps:
|
||||
go get -t ./...
|
||||
|
||||
build: clean deps
|
||||
go build
|
||||
build: deps $(commands)
|
||||
|
||||
install: build
|
||||
go install
|
||||
|
||||
test: clean deps
|
||||
test: deps
|
||||
go test -race ./...
|
||||
|
||||
fmt:
|
||||
@@ -31,5 +32,5 @@ simplify:
|
||||
|
||||
lint:
|
||||
@test -z "$(shell gofmt -l . | tee /dev/stderr)" || echo "[WARN] Fix formatting issues with 'make fmt'"
|
||||
@golint -set_exit_status $${PKGS}
|
||||
@go vet $${PKGS}
|
||||
@golint -set_exit_status $(PKGS)
|
||||
@go vet $(PKGS)
|
||||
|
||||
@@ -21,6 +21,9 @@ to contribute code to the project check out [CONTRIBUTING.md].
|
||||
|
||||
## Homebrew Tap
|
||||
|
||||
(currently broken, being tracked in [issue
|
||||
#68](https://github.com/jhillyerd/inbucket/issues/68))
|
||||
|
||||
Inbucket has an OS X [Homebrew] tap available as [jhillyerd/inbucket][Homebrew Tap],
|
||||
see the `README.md` there for installation instructions.
|
||||
|
||||
@@ -31,7 +34,7 @@ You will need a functioning [Go installation][Google Go] for this to work.
|
||||
|
||||
Grab the Inbucket source code and compile the daemon:
|
||||
|
||||
go get -v github.com/jhillyerd/inbucket
|
||||
go get -v github.com/jhillyerd/inbucket/cmd/inbucket
|
||||
|
||||
Edit etc/inbucket.conf and tailor to your environment. It should work on most
|
||||
Unix and OS X machines as is. Launch the daemon:
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"github.com/jhillyerd/inbucket/rest/client"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/client"
|
||||
)
|
||||
|
||||
type listCmd struct {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"github.com/jhillyerd/inbucket/rest/client"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/client"
|
||||
)
|
||||
|
||||
type matchCmd struct {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"github.com/jhillyerd/inbucket/rest/client"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/client"
|
||||
)
|
||||
|
||||
type mboxCmd struct {
|
||||
|
||||
@@ -12,15 +12,15 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/filestore"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pop3d"
|
||||
"github.com/jhillyerd/inbucket/rest"
|
||||
"github.com/jhillyerd/inbucket/smtpd"
|
||||
"github.com/jhillyerd/inbucket/webui"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/pop3"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/smtp"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage/file"
|
||||
"github.com/jhillyerd/inbucket/pkg/webui"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -39,8 +39,8 @@ var (
|
||||
shutdownChan = make(chan bool)
|
||||
|
||||
// Server instances
|
||||
smtpServer *smtpd.Server
|
||||
pop3Server *pop3d.Server
|
||||
smtpServer *smtp.Server
|
||||
pop3Server *pop3.Server
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -119,17 +119,17 @@ func main() {
|
||||
ds := filestore.DefaultFileDataStore()
|
||||
|
||||
// Start HTTP server
|
||||
httpd.Initialize(config.GetWebConfig(), shutdownChan, ds, msgHub)
|
||||
webui.SetupRoutes(httpd.Router)
|
||||
rest.SetupRoutes(httpd.Router)
|
||||
go httpd.Start(rootCtx)
|
||||
web.Initialize(config.GetWebConfig(), shutdownChan, ds, msgHub)
|
||||
webui.SetupRoutes(web.Router)
|
||||
rest.SetupRoutes(web.Router)
|
||||
go web.Start(rootCtx)
|
||||
|
||||
// Start POP3 server
|
||||
pop3Server = pop3d.New(config.GetPOP3Config(), shutdownChan, ds)
|
||||
pop3Server = pop3.New(config.GetPOP3Config(), shutdownChan, ds)
|
||||
go pop3Server.Start(rootCtx)
|
||||
|
||||
// Startup SMTP server
|
||||
smtpServer = smtpd.NewServer(config.GetSMTPConfig(), shutdownChan, ds, msgHub)
|
||||
smtpServer = smtp.NewServer(config.GetSMTPConfig(), shutdownChan, ds, msgHub)
|
||||
go smtpServer.Start(rootCtx)
|
||||
|
||||
// Loop forever waiting for signals or shutdown channel
|
||||
@@ -1,19 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type HashLock [4096]sync.RWMutex
|
||||
|
||||
func (h *HashLock) Get(hash string) *sync.RWMutex {
|
||||
if len(hash) < 3 {
|
||||
return nil
|
||||
}
|
||||
i, err := strconv.ParseInt(hash[0:3], 16, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &h[i]
|
||||
}
|
||||
@@ -16,8 +16,6 @@ apk add --no-cache --virtual .build-deps git
|
||||
# Setup
|
||||
export GOBIN="$bindir"
|
||||
cd "$srcdir"
|
||||
# Fetch tags for describe
|
||||
git fetch -t
|
||||
builddate="$(date -Iseconds)"
|
||||
buildver="$(git describe --tags --always)"
|
||||
|
||||
@@ -30,7 +28,7 @@ echo "### Testing Inbucket"
|
||||
go test ./...
|
||||
|
||||
echo "### Building Inbucket"
|
||||
go build -o inbucket -ldflags "-X 'main.version=$buildver' -X 'main.date=$builddate'" -v .
|
||||
go build -o inbucket -ldflags "-X 'main.version=$buildver' -X 'main.date=$builddate'" -v ./cmd/inbucket
|
||||
|
||||
echo "### Installing Inbucket"
|
||||
set -x
|
||||
|
||||
@@ -128,7 +128,7 @@ func openLogFile() error {
|
||||
var err error
|
||||
logf, err = os.OpenFile(logfname, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create %v: %v\n", logfname, err)
|
||||
return fmt.Errorf("failed to create %v: %v", logfname, err)
|
||||
}
|
||||
golog.SetOutput(logf)
|
||||
Tracef("Opened new logfile")
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/rest/model"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/model"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
)
|
||||
|
||||
// MailboxListV1 renders a list of messages in a mailbox
|
||||
func MailboxListV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxListV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
@@ -48,11 +48,11 @@ func MailboxListV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
Size: msg.Size(),
|
||||
}
|
||||
}
|
||||
return httpd.RenderJSON(w, jmessages)
|
||||
return web.RenderJSON(w, jmessages)
|
||||
}
|
||||
|
||||
// MailboxShowV1 renders a particular message from a mailbox
|
||||
func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -96,7 +96,7 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
}
|
||||
}
|
||||
|
||||
return httpd.RenderJSON(w,
|
||||
return web.RenderJSON(w,
|
||||
&model.JSONMessageV1{
|
||||
Mailbox: name,
|
||||
ID: msg.ID(),
|
||||
@@ -115,7 +115,7 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
}
|
||||
|
||||
// MailboxPurgeV1 deletes all messages from a mailbox
|
||||
func MailboxPurgeV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxPurgeV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
@@ -133,11 +133,11 @@ func MailboxPurgeV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context
|
||||
}
|
||||
log.Tracef("HTTP purged mailbox for %q", name)
|
||||
|
||||
return httpd.RenderJSON(w, "OK")
|
||||
return web.RenderJSON(w, "OK")
|
||||
}
|
||||
|
||||
// MailboxSourceV1 displays the raw source of a message, including headers. Renders text/plain
|
||||
func MailboxSourceV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxSourceV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -171,7 +171,7 @@ func MailboxSourceV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Contex
|
||||
}
|
||||
|
||||
// MailboxDeleteV1 removes a particular message from a mailbox
|
||||
func MailboxDeleteV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxDeleteV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -197,5 +197,5 @@ func MailboxDeleteV1(w http.ResponseWriter, req *http.Request, ctx *httpd.Contex
|
||||
return fmt.Errorf("Delete(%q) failed: %v", id, err)
|
||||
}
|
||||
|
||||
return httpd.RenderJSON(w, "OK")
|
||||
return web.RenderJSON(w, "OK")
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/rest/model"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/model"
|
||||
)
|
||||
|
||||
// Client accesses the Inbucket REST API v1
|
||||
@@ -30,6 +30,7 @@ type JSONMessageV1 struct {
|
||||
Attachments []*JSONMessageAttachmentV1 `json:"attachments"`
|
||||
}
|
||||
|
||||
// JSONMessageAttachmentV1 contains information about a MIME attachment
|
||||
type JSONMessageAttachmentV1 struct {
|
||||
FileName string `json:"filename"`
|
||||
ContentType string `json:"content-type"`
|
||||
23
pkg/rest/routes.go
Normal file
23
pkg/rest/routes.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package rest
|
||||
|
||||
import "github.com/gorilla/mux"
|
||||
import "github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
|
||||
// SetupRoutes populates the routes for the REST interface
|
||||
func SetupRoutes(r *mux.Router) {
|
||||
// API v1
|
||||
r.Path("/api/v1/mailbox/{name}").Handler(
|
||||
web.Handler(MailboxListV1)).Name("MailboxListV1").Methods("GET")
|
||||
r.Path("/api/v1/mailbox/{name}").Handler(
|
||||
web.Handler(MailboxPurgeV1)).Name("MailboxPurgeV1").Methods("DELETE")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}").Handler(
|
||||
web.Handler(MailboxShowV1)).Name("MailboxShowV1").Methods("GET")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}").Handler(
|
||||
web.Handler(MailboxDeleteV1)).Name("MailboxDeleteV1").Methods("DELETE")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}/source").Handler(
|
||||
web.Handler(MailboxSourceV1)).Name("MailboxSourceV1").Methods("GET")
|
||||
r.Path("/api/v1/monitor/messages").Handler(
|
||||
web.Handler(MonitorAllMessagesV1)).Name("MonitorAllMessagesV1").Methods("GET")
|
||||
r.Path("/api/v1/monitor/messages/{name}").Handler(
|
||||
web.Handler(MonitorMailboxMessagesV1)).Name("MonitorMailboxMessagesV1").Methods("GET")
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/rest/model"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/rest/model"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -144,17 +144,19 @@ func (ml *msgListener) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
// MonitorAllMessagesV1 is a web handler which upgrades the connection to a websocket and notifies
|
||||
// the client of all messages received.
|
||||
func MonitorAllMessagesV1(
|
||||
w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Upgrade to Websocket
|
||||
conn, err := upgrader.Upgrade(w, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpd.ExpWebSocketConnectsCurrent.Add(1)
|
||||
web.ExpWebSocketConnectsCurrent.Add(1)
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
httpd.ExpWebSocketConnectsCurrent.Add(-1)
|
||||
web.ExpWebSocketConnectsCurrent.Add(-1)
|
||||
}()
|
||||
|
||||
log.Tracef("HTTP[%v] Upgraded to websocket", req.RemoteAddr)
|
||||
@@ -167,8 +169,10 @@ func MonitorAllMessagesV1(
|
||||
return nil
|
||||
}
|
||||
|
||||
// MonitorMailboxMessagesV1 is a web handler which upgrades the connection to a websocket and
|
||||
// notifies the client of messages received by a particular mailbox.
|
||||
func MonitorMailboxMessagesV1(
|
||||
w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -178,10 +182,10 @@ func MonitorMailboxMessagesV1(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpd.ExpWebSocketConnectsCurrent.Add(1)
|
||||
web.ExpWebSocketConnectsCurrent.Add(1)
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
httpd.ExpWebSocketConnectsCurrent.Add(-1)
|
||||
web.ExpWebSocketConnectsCurrent.Add(-1)
|
||||
}()
|
||||
|
||||
log.Tracef("HTTP[%v] Upgraded to websocket", req.RemoteAddr)
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
type InputMessageData struct {
|
||||
@@ -184,7 +184,7 @@ func testRestGet(url string) (*httptest.ResponseRecorder, error) {
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
httpd.Router.ServeHTTP(w, req)
|
||||
web.Router.ServeHTTP(w, req)
|
||||
return w, nil
|
||||
}
|
||||
|
||||
@@ -200,8 +200,8 @@ func setupWebServer(ds datastore.DataStore) *bytes.Buffer {
|
||||
PublicDir: "../themes/bootstrap/public",
|
||||
}
|
||||
shutdownChan := make(chan bool)
|
||||
httpd.Initialize(cfg, shutdownChan, ds, &msghub.Hub{})
|
||||
SetupRoutes(httpd.Router)
|
||||
web.Initialize(cfg, shutdownChan, ds, &msghub.Hub{})
|
||||
SetupRoutes(web.Router)
|
||||
|
||||
return buf
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package pop3d
|
||||
package pop3
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
// State tracks the current mode of our POP3 state machine
|
||||
@@ -1,4 +1,4 @@
|
||||
package pop3d
|
||||
package pop3
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
// Server defines an instance of our POP3 server
|
||||
@@ -1,4 +1,4 @@
|
||||
package smtpd
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
)
|
||||
|
||||
// State tracks the current mode of our SMTP state machine
|
||||
@@ -1,4 +1,4 @@
|
||||
package smtpd
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
type scriptStep struct {
|
||||
@@ -1,4 +1,4 @@
|
||||
package smtpd
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -1,4 +1,4 @@
|
||||
package httpd
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
// Context is passed into every request handler function
|
||||
@@ -1,4 +1,4 @@
|
||||
package httpd
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
)
|
||||
|
||||
// TemplateFuncs declares functions made available to all templates (including partials)
|
||||
@@ -1,4 +1,4 @@
|
||||
package httpd
|
||||
package web
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
@@ -1,4 +1,4 @@
|
||||
package httpd
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package httpd provides the plumbing for Inbucket's web GUI and RESTful API
|
||||
package httpd
|
||||
// Package web provides the plumbing for Inbucket's web GUI and RESTful API
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/msghub"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
// Handler is a function type that handles an HTTP request in Inbucket
|
||||
@@ -1,4 +1,4 @@
|
||||
package httpd
|
||||
package web
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
)
|
||||
|
||||
var cachedMutex sync.Mutex
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
// FileMessage implements Message and contains a little bit of data about a
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
)
|
||||
|
||||
// Name of index file in each mailbox
|
||||
@@ -140,6 +140,7 @@ func (ds *FileDataStore) AllMailboxes() ([]datastore.Mailbox, error) {
|
||||
return mailboxes, nil
|
||||
}
|
||||
|
||||
// LockFor returns the RWMutex for this mailbox, or an error.
|
||||
func (ds *FileDataStore) LockFor(emailAddress string) (*sync.RWMutex, error) {
|
||||
name, err := stringutil.ParseMailboxName(emailAddress)
|
||||
if err != nil {
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
23
pkg/storage/lock.go
Normal file
23
pkg/storage/lock.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HashLock holds a fixed length array of mutexes. This approach allows concurrent mailbox
|
||||
// access in most cases without requiring an infinite number of mutexes.
|
||||
type HashLock [4096]sync.RWMutex
|
||||
|
||||
// Get returns a RWMutex based on the first 12 bits of the mailbox hash. Hash must be a hexidecimal
|
||||
// string of three or more characters.
|
||||
func (h *HashLock) Get(hash string) *sync.RWMutex {
|
||||
if len(hash) < 3 {
|
||||
return nil
|
||||
}
|
||||
i, err := strconv.ParseInt(hash[0:3], 16, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &h[i]
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package datastore_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
)
|
||||
|
||||
func TestHashLock(t *testing.T) {
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -27,6 +27,7 @@ func (m *MockDataStore) AllMailboxes() ([]Mailbox, error) {
|
||||
return args.Get(0).([]Mailbox), args.Error(1)
|
||||
}
|
||||
|
||||
// LockFor mock function returns a new RWMutex, never errors.
|
||||
func (m *MockDataStore) LockFor(name string) (*sync.RWMutex, error) {
|
||||
return &sync.RWMutex{}, nil
|
||||
}
|
||||
@@ -7,29 +7,29 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/jhillyerd/inbucket/datastore"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/log"
|
||||
"github.com/jhillyerd/inbucket/sanitize"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/log"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/storage"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/webui/sanitize"
|
||||
)
|
||||
|
||||
// MailboxIndex renders the index page for a particular mailbox
|
||||
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Form values must be validated manually
|
||||
name := req.FormValue("name")
|
||||
selected := req.FormValue("id")
|
||||
if len(name) == 0 {
|
||||
ctx.Session.AddFlash("Account name is required", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
name, err = stringutil.ParseMailboxName(name)
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash(err.Error(), "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
// Remember this mailbox was visited
|
||||
@@ -40,7 +40,7 @@ func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
return err
|
||||
}
|
||||
// Render template
|
||||
return httpd.RenderTemplate("mailbox/index.html", w, map[string]interface{}{
|
||||
return web.RenderTemplate("mailbox/index.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"errorFlash": errorFlash,
|
||||
"name": name,
|
||||
@@ -49,24 +49,24 @@ func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
}
|
||||
|
||||
// MailboxLink handles pretty links to a particular message. Renders a redirect
|
||||
func MailboxLink(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxLink(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash(err.Error(), "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
// Build redirect
|
||||
uri := fmt.Sprintf("%s?name=%s&id=%s", httpd.Reverse("MailboxIndex"), name, id)
|
||||
uri := fmt.Sprintf("%s?name=%s&id=%s", web.Reverse("MailboxIndex"), name, id)
|
||||
http.Redirect(w, req, uri, http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MailboxList renders a list of messages in a mailbox. Renders a partial
|
||||
func MailboxList(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxList(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
@@ -84,7 +84,7 @@ func MailboxList(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
log.Tracef("Got %v messsages", len(messages))
|
||||
// Render partial template
|
||||
return httpd.RenderPartial("mailbox/_list.html", w, map[string]interface{}{
|
||||
return web.RenderPartial("mailbox/_list.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"name": name,
|
||||
"messages": messages,
|
||||
@@ -92,7 +92,7 @@ func MailboxList(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
|
||||
// MailboxShow renders a particular message from a mailbox. Renders an HTML partial
|
||||
func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -117,7 +117,7 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadBody(%q) failed: %v", id, err)
|
||||
}
|
||||
body := template.HTML(httpd.TextToHTML(mime.Text))
|
||||
body := template.HTML(web.TextToHTML(mime.Text))
|
||||
htmlAvailable := mime.HTML != ""
|
||||
var htmlBody template.HTML
|
||||
if htmlAvailable {
|
||||
@@ -128,7 +128,7 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
}
|
||||
// Render partial template
|
||||
return httpd.RenderPartial("mailbox/_show.html", w, map[string]interface{}{
|
||||
return web.RenderPartial("mailbox/_show.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"name": name,
|
||||
"message": msg,
|
||||
@@ -141,7 +141,7 @@ func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
|
||||
// MailboxHTML displays the HTML content of a message. Renders a partial
|
||||
func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -168,7 +168,7 @@ func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
// Render partial template
|
||||
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
|
||||
return httpd.RenderPartial("mailbox/_html.html", w, map[string]interface{}{
|
||||
return web.RenderPartial("mailbox/_html.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"name": name,
|
||||
"message": message,
|
||||
@@ -178,7 +178,7 @@ func MailboxHTML(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
}
|
||||
|
||||
// MailboxSource displays the raw source of a message, including headers. Renders text/plain
|
||||
func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
@@ -213,14 +213,14 @@ func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *httpd.Context)
|
||||
|
||||
// MailboxDownloadAttach sends the attachment to the client; disposition:
|
||||
// attachment, type: application/octet-stream
|
||||
func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
id := ctx.Vars["id"]
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash(err.Error(), "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
numStr := ctx.Vars["num"]
|
||||
@@ -228,7 +228,7 @@ func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
mb, err := ctx.DataStore.MailboxFor(name)
|
||||
@@ -252,7 +252,7 @@ func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.
|
||||
if int(num) >= len(body.Attachments) {
|
||||
ctx.Session.AddFlash("Attachment number too high", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
part := body.Attachments[num]
|
||||
@@ -266,13 +266,13 @@ func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.
|
||||
}
|
||||
|
||||
// MailboxViewAttach sends the attachment to the client for online viewing
|
||||
func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
// Don't have to validate these aren't empty, Gorilla returns 404
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash(err.Error(), "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
id := ctx.Vars["id"]
|
||||
@@ -281,7 +281,7 @@ func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.Cont
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
mb, err := ctx.DataStore.MailboxFor(name)
|
||||
@@ -305,7 +305,7 @@ func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *httpd.Cont
|
||||
if int(num) >= len(body.Attachments) {
|
||||
ctx.Session.AddFlash("Attachment number too high", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
part := body.Attachments[num]
|
||||
@@ -1,7 +1,7 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
)
|
||||
|
||||
// RememberMailbox manages the list of recently accessed mailboxes stored in the session
|
||||
func RememberMailbox(ctx *httpd.Context, mailbox string) {
|
||||
func RememberMailbox(ctx *web.Context, mailbox string) {
|
||||
recent := RecentMailboxes(ctx)
|
||||
newRecent := make([]string, 1, maxRemembered)
|
||||
newRecent[0] = mailbox
|
||||
@@ -28,7 +28,7 @@ func RememberMailbox(ctx *httpd.Context, mailbox string) {
|
||||
}
|
||||
|
||||
// RecentMailboxes returns a slice of the most recently accessed mailboxes
|
||||
func RecentMailboxes(ctx *httpd.Context) []string {
|
||||
func RecentMailboxes(ctx *web.Context) []string {
|
||||
val := ctx.Session.Values[mailboxKey]
|
||||
recent, _ := val.([]string)
|
||||
return recent
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/jhillyerd/inbucket/config"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
"github.com/jhillyerd/inbucket/stringutil"
|
||||
"github.com/jhillyerd/inbucket/pkg/config"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||
)
|
||||
|
||||
// RootIndex serves the Inbucket landing page
|
||||
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
greeting, err := ioutil.ReadFile(config.GetWebConfig().GreetingFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to load greeting: %v", err)
|
||||
@@ -23,7 +23,7 @@ func RootIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (er
|
||||
return err
|
||||
}
|
||||
// Render template
|
||||
return httpd.RenderTemplate("root/index.html", w, map[string]interface{}{
|
||||
return web.RenderTemplate("root/index.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"errorFlash": errorFlash,
|
||||
"greeting": template.HTML(string(greeting)),
|
||||
@@ -31,11 +31,11 @@ func RootIndex(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (er
|
||||
}
|
||||
|
||||
// RootMonitor serves the Inbucket monitor page
|
||||
func RootMonitor(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func RootMonitor(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
if !config.GetWebConfig().MonitorVisible {
|
||||
ctx.Session.AddFlash("Monitor is disabled in configuration", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
// Get flash messages, save session
|
||||
@@ -44,25 +44,25 @@ func RootMonitor(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (
|
||||
return err
|
||||
}
|
||||
// Render template
|
||||
return httpd.RenderTemplate("root/monitor.html", w, map[string]interface{}{
|
||||
return web.RenderTemplate("root/monitor.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"errorFlash": errorFlash,
|
||||
})
|
||||
}
|
||||
|
||||
// RootMonitorMailbox serves the Inbucket monitor page for a particular mailbox
|
||||
func RootMonitorMailbox(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func RootMonitorMailbox(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
if !config.GetWebConfig().MonitorVisible {
|
||||
ctx.Session.AddFlash("Monitor is disabled in configuration", "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
name, err := stringutil.ParseMailboxName(ctx.Vars["name"])
|
||||
if err != nil {
|
||||
ctx.Session.AddFlash(err.Error(), "errors")
|
||||
_ = ctx.Session.Save(req, w)
|
||||
http.Redirect(w, req, httpd.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
http.Redirect(w, req, web.Reverse("RootIndex"), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
// Get flash messages, save session
|
||||
@@ -71,7 +71,7 @@ func RootMonitorMailbox(w http.ResponseWriter, req *http.Request, ctx *httpd.Con
|
||||
return err
|
||||
}
|
||||
// Render template
|
||||
return httpd.RenderTemplate("root/monitor.html", w, map[string]interface{}{
|
||||
return web.RenderTemplate("root/monitor.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"errorFlash": errorFlash,
|
||||
"name": name,
|
||||
@@ -79,7 +79,7 @@ func RootMonitorMailbox(w http.ResponseWriter, req *http.Request, ctx *httpd.Con
|
||||
}
|
||||
|
||||
// RootStatus serves the Inbucket status page
|
||||
func RootStatus(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (err error) {
|
||||
func RootStatus(w http.ResponseWriter, req *http.Request, ctx *web.Context) (err error) {
|
||||
smtpListener := fmt.Sprintf("%s:%d", config.GetSMTPConfig().IP4address.String(),
|
||||
config.GetSMTPConfig().IP4port)
|
||||
pop3Listener := fmt.Sprintf("%s:%d", config.GetPOP3Config().IP4address.String(),
|
||||
@@ -92,7 +92,7 @@ func RootStatus(w http.ResponseWriter, req *http.Request, ctx *httpd.Context) (e
|
||||
return err
|
||||
}
|
||||
// Render template
|
||||
return httpd.RenderTemplate("root/status.html", w, map[string]interface{}{
|
||||
return web.RenderTemplate("root/status.html", w, map[string]interface{}{
|
||||
"ctx": ctx,
|
||||
"errorFlash": errorFlash,
|
||||
"version": config.Version,
|
||||
35
pkg/webui/routes.go
Normal file
35
pkg/webui/routes.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Package webui powers Inbucket's web GUI
|
||||
package webui
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jhillyerd/inbucket/pkg/server/web"
|
||||
)
|
||||
|
||||
// SetupRoutes populates routes for the webui into the provided Router
|
||||
func SetupRoutes(r *mux.Router) {
|
||||
r.Path("/").Handler(
|
||||
web.Handler(RootIndex)).Name("RootIndex").Methods("GET")
|
||||
r.Path("/monitor").Handler(
|
||||
web.Handler(RootMonitor)).Name("RootMonitor").Methods("GET")
|
||||
r.Path("/monitor/{name}").Handler(
|
||||
web.Handler(RootMonitorMailbox)).Name("RootMonitorMailbox").Methods("GET")
|
||||
r.Path("/status").Handler(
|
||||
web.Handler(RootStatus)).Name("RootStatus").Methods("GET")
|
||||
r.Path("/link/{name}/{id}").Handler(
|
||||
web.Handler(MailboxLink)).Name("MailboxLink").Methods("GET")
|
||||
r.Path("/mailbox").Handler(
|
||||
web.Handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
|
||||
r.Path("/mailbox/{name}").Handler(
|
||||
web.Handler(MailboxList)).Name("MailboxList").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}").Handler(
|
||||
web.Handler(MailboxShow)).Name("MailboxShow").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/html").Handler(
|
||||
web.Handler(MailboxHTML)).Name("MailboxHtml").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/source").Handler(
|
||||
web.Handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
||||
r.Path("/mailbox/dattach/{name}/{id}/{num}/{file}").Handler(
|
||||
web.Handler(MailboxDownloadAttach)).Name("MailboxDownloadAttach").Methods("GET")
|
||||
r.Path("/mailbox/vattach/{name}/{id}/{num}/{file}").Handler(
|
||||
web.Handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET")
|
||||
}
|
||||
@@ -18,6 +18,7 @@ var (
|
||||
AllowAttrs("style").Matching(cssSafe).Globally()
|
||||
)
|
||||
|
||||
// HTML sanitizes the provided html, while attempting to preserve inline CSS styling.
|
||||
func HTML(html string) (output string, err error) {
|
||||
output, err = sanitizeStyleTags(html)
|
||||
if err != nil {
|
||||
@@ -3,7 +3,7 @@ package sanitize_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jhillyerd/inbucket/sanitize"
|
||||
"github.com/jhillyerd/inbucket/pkg/webui/sanitize"
|
||||
)
|
||||
|
||||
// TestHTMLPlainStrings test plain text passthrough
|
||||
@@ -1,23 +0,0 @@
|
||||
package rest
|
||||
|
||||
import "github.com/gorilla/mux"
|
||||
import "github.com/jhillyerd/inbucket/httpd"
|
||||
|
||||
// SetupRoutes populates the routes for the REST interface
|
||||
func SetupRoutes(r *mux.Router) {
|
||||
// API v1
|
||||
r.Path("/api/v1/mailbox/{name}").Handler(
|
||||
httpd.Handler(MailboxListV1)).Name("MailboxListV1").Methods("GET")
|
||||
r.Path("/api/v1/mailbox/{name}").Handler(
|
||||
httpd.Handler(MailboxPurgeV1)).Name("MailboxPurgeV1").Methods("DELETE")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}").Handler(
|
||||
httpd.Handler(MailboxShowV1)).Name("MailboxShowV1").Methods("GET")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}").Handler(
|
||||
httpd.Handler(MailboxDeleteV1)).Name("MailboxDeleteV1").Methods("DELETE")
|
||||
r.Path("/api/v1/mailbox/{name}/{id}/source").Handler(
|
||||
httpd.Handler(MailboxSourceV1)).Name("MailboxSourceV1").Methods("GET")
|
||||
r.Path("/api/v1/monitor/messages").Handler(
|
||||
httpd.Handler(MonitorAllMessagesV1)).Name("MonitorAllMessagesV1").Methods("GET")
|
||||
r.Path("/api/v1/monitor/messages/{name}").Handler(
|
||||
httpd.Handler(MonitorMailboxMessagesV1)).Name("MonitorMailboxMessagesV1").Methods("GET")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Package webui powers Inbucket's web GUI
|
||||
package webui
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jhillyerd/inbucket/httpd"
|
||||
)
|
||||
|
||||
// SetupRoutes populates routes for the webui into the provided Router
|
||||
func SetupRoutes(r *mux.Router) {
|
||||
r.Path("/").Handler(
|
||||
httpd.Handler(RootIndex)).Name("RootIndex").Methods("GET")
|
||||
r.Path("/monitor").Handler(
|
||||
httpd.Handler(RootMonitor)).Name("RootMonitor").Methods("GET")
|
||||
r.Path("/monitor/{name}").Handler(
|
||||
httpd.Handler(RootMonitorMailbox)).Name("RootMonitorMailbox").Methods("GET")
|
||||
r.Path("/status").Handler(
|
||||
httpd.Handler(RootStatus)).Name("RootStatus").Methods("GET")
|
||||
r.Path("/link/{name}/{id}").Handler(
|
||||
httpd.Handler(MailboxLink)).Name("MailboxLink").Methods("GET")
|
||||
r.Path("/mailbox").Handler(
|
||||
httpd.Handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
|
||||
r.Path("/mailbox/{name}").Handler(
|
||||
httpd.Handler(MailboxList)).Name("MailboxList").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}").Handler(
|
||||
httpd.Handler(MailboxShow)).Name("MailboxShow").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/html").Handler(
|
||||
httpd.Handler(MailboxHTML)).Name("MailboxHtml").Methods("GET")
|
||||
r.Path("/mailbox/{name}/{id}/source").Handler(
|
||||
httpd.Handler(MailboxSource)).Name("MailboxSource").Methods("GET")
|
||||
r.Path("/mailbox/dattach/{name}/{id}/{num}/{file}").Handler(
|
||||
httpd.Handler(MailboxDownloadAttach)).Name("MailboxDownloadAttach").Methods("GET")
|
||||
r.Path("/mailbox/vattach/{name}/{id}/{num}/{file}").Handler(
|
||||
httpd.Handler(MailboxViewAttach)).Name("MailboxViewAttach").Methods("GET")
|
||||
}
|
||||
Reference in New Issue
Block a user