This is the main dovel repository, it has the Go code to run dovel SMTP server.
Author: brian (git@myr.sh)
Date: Sun Dec 31 15:58:26 2023 -0300
Parent: 1a87fa1
Updated vault package * Updated smtp server package
diff --git a/TODO.txt b/TODO.txt index 11e2cd8..9bf7f49 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,6 +5,6 @@ ✓ Support PGP ✓ Use XDG desktop for config path ✓ Support SMTP for sending email +✓ Change PGP to external package ☐ Add denylist filtering ☐ Improve documentation -☐ Change PGP to external package
diff --git a/backend.go b/backend.go
index 73741d2..b9a80c1 100644
--- a/backend.go
+++ b/backend.go
@@ -18,14 +18,14 @@ import (
"github.com/emersion/go-mbox"
"github.com/emersion/go-msgauth/dkim"
- "github.com/emersion/go-openpgp-wkd"
+ wkd "github.com/emersion/go-openpgp-wkd"
"github.com/emersion/go-smtp"
"golang.org/x/crypto/openpgp"
)
// A Session is returned after EHLO.
type Session struct {
- user string
+ user User
from string
tos []string
}
@@ -37,7 +37,7 @@ func (s *Session) AuthPlain(username, password string) error {
return fmt.Errorf("user not authorized")
}
slog.Debug("authorized", "user", username)
- s.user = username
+ s.user = v.GetUser(username)
return nil
}
@@ -47,7 +47,7 @@ func (s *Session) Mail(from string, opts *smtp.MailOptions) error {
return nil
}
-func (s *Session) Rcpt(to string) error {
+func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error {
slog.Debug("received rcpt to", "to", to)
s.tos = append(s.tos, to)
return nil
@@ -83,7 +83,7 @@ func (s *Session) Data(raw io.Reader) error {
dom := strings.Split(s.from, "@")[1]
if dom == cfg.Domain {
// check auth
- if s.user == "" {
+ if s.user.Name == "" {
return smtp.ErrAuthRequired
}
err = s.Send(s.from, tos, strings.NewReader(string(cont)))
@@ -180,7 +180,6 @@ func (s *Session) Send(from string, tos []*mail.Address, raw io.Reader) error {
return err
}
- // TODO: use external package
slog.Debug("checking wkd key")
key, _ := wkd.Discover(to.Address)
if key != nil {
@@ -210,7 +209,7 @@ func (s *Session) Send(from string, tos []*mail.Address, raw io.Reader) error {
// dkim
slog.Debug("dkim check")
res := bytes.Buffer{}
- if keyPath := v.GetUser(s.user).PrivateKey; keyPath != "" {
+ if keyPath := s.user.PrivateKey; keyPath != "" {
slog.Debug("user has dkim key")
keyData, err := os.ReadFile(keyPath)
diff --git a/go.mod b/go.mod index c31be00..41811e6 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,10 @@ module dovel.email/server go 1.19 require ( - git.derelict.garden/bryon/vault v0.3.0 github.com/emersion/go-mbox v1.0.3 github.com/emersion/go-msgauth v0.6.6 github.com/emersion/go-openpgp-wkd v0.0.0-20200304100729-bbe7fac61be2 - github.com/emersion/go-smtp v0.16.0 + github.com/emersion/go-smtp v0.20.0 golang.org/x/crypto v0.7.0 )
diff --git a/go.sum b/go.sum index da7ec0f..b32889c 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8b github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8= github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.20.0 h1:eTD2iPzYKRrFF+NDHPwKgsBt5ZmB2jO6P2rP2LlVYWI= +github.com/emersion/go-smtp v0.20.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
diff --git a/main.go b/main.go
index 7490266..e57d5a4 100644
--- a/main.go
+++ b/main.go
@@ -8,14 +8,13 @@ import (
"path"
"time"
- "git.derelict.garden/bryon/vault"
"github.com/emersion/go-smtp"
)
var (
cfg = Config{}
configPath string
- v vault.Vault[User]
+ v store
)
func main() {
@@ -42,15 +41,6 @@ func main() {
json.NewDecoder(configFile).Decode(&cfg)
slog.Debug("config loaded", "config", cfg)
- if cfg.VaultFile != "" {
- slog.Debug("creating vault", "path", cfg.VaultFile)
- v, err = vault.NewJSONPlainTextVault[User](cfg.VaultFile)
- if err != nil {
- panic(err)
- }
- slog.Debug("vault created", "vault", v)
- }
-
tlsCfg := &tls.Config{}
if cfg.Certificate != "" {
slog.Debug("loading certs", "cert", cfg.Certificate, "key", cfg.PrivateKey)
@@ -72,10 +62,10 @@ func main() {
if len(tlsCfg.Certificates) > 0 {
s.TLSConfig = tlsCfg
- slog.Debug("starting server", "tls", tlsCfg)
+ slog.Info("server started", "tls", tlsCfg, "port", cfg.Port)
err = s.ListenAndServeTLS()
} else {
- slog.Debug("starting server")
+ slog.Info("server started", "port", cfg.Port)
err = s.ListenAndServe()
}
if err != nil {
diff --git a/model.go b/model.go
index 56c314b..6bb2bc3 100644
--- a/model.go
+++ b/model.go
@@ -11,11 +11,16 @@ package main
type Config struct {
Port string
Domain string
- VaultFile string
Certificate string
PrivateKey string
}
+// Vault interface gives validation and user fetching.
+type Vault interface {
+ Validate(n, p string) bool
+ GetUser(n string) User
+}
+
// User represents a user that should be able to send emails. This struct is
// found in the users json file, that is on the path pointed by VaultFile field
// in [Config]. PrivateKey is the path to a private key for the DKIM signature.
@@ -26,10 +31,20 @@ type User struct {
PrivateKey string
}
-func (w User) Login() string {
- return w.Name
+type store struct {
+ users []User
+}
+
+func (s store) Validate(n, p string) bool {
+ u := s.GetUser(n)
+ return u.Password == p
}
-func (w User) Pass() string {
- return w.Password
+func (s store) GetUser(n string) User {
+ for _, u := range s.users {
+ if u.Name == n {
+ return u
+ }
+ }
+ return User{}
}