list

server

This is the main dovel repository, it has the Go code to run dovel SMTP server.

curl https://dovel.email/server.tar tar

8d0b023

Author: bmayer3 (bmayer@sibros.tech)

Date: Sun Feb 12 20:17:52 2023 -0300

Parent: 881b4b4

Added dkim support

Diff

cmd/dovel/main.go

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/cmd/dovel/main.go b/cmd/dovel/main.go
index f7076b4..32aafda 100644
--- a/cmd/dovel/main.go
+++ b/cmd/dovel/main.go
@@ -1,7 +1,11 @@
 package main
 
 import (
+	"crypto"
+	"crypto/x509"
 	"encoding/json"
+	"encoding/pem"
+	"io/ioutil"
 	"log"
 	"net/http"
 	"os"
@@ -21,8 +25,9 @@ var (
 	defaultConfig = config.Config{
 		WebPort: &defaultPort,
 		Server: config.ServerConfig{
-			Domain:  "m.blmayer.dev",
-			Address: ":2525",
+			DKIMKeyPath: "dkim.priv",
+			Domain:      "mail.blmayer.dev",
+			Address:     ":2525",
 			Inboxes: []config.InboxConfig{
 				{
 					Domain:    "localhost",
@@ -33,11 +38,11 @@ var (
 			},
 		},
 	}
-	b = backend.Backend{Handlers: map[string]config.Handler{}}
+	cfg config.Config
+	b   = backend.Backend{Handlers: map[string]config.Handler{}}
 )
 
 func main() {
-	var cfg config.Config
 	configPath := ".dovel-config.json"
 	configFile, err := os.Open(configPath)
 	if err != nil {
@@ -47,6 +52,25 @@ func main() {
 		json.NewDecoder(configFile).Decode(&cfg)
 	}
 
+	// load kdim key
+	if cfg.Server.DKIMKeyPath != "" {
+		key, err := ioutil.ReadFile(cfg.Server.DKIMKeyPath)
+		if err != nil {
+			panic(err)
+		}
+
+		block, _ := pem.Decode(key)
+		if block == nil {
+			panic("no PEM data found")
+		}
+		privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+		if err != nil {
+			panic(err)
+		}
+		b.PrivateKey = privKey.(crypto.Signer)
+		println("loaded dkim private key")
+	}
+
 	for _, hand := range cfg.Server.Inboxes {
 		switch hand.Handler {
 		case "gwi":

config/config.go

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/config/config.go b/config/config.go
index 6ec40d7..48fa959 100644
--- a/config/config.go
+++ b/config/config.go
@@ -36,6 +36,5 @@ type ServerConfig struct {
 	Address     string
 	Domain      string
 	Inboxes     []InboxConfig
-	DKIMPrivKey string
-	DKIMPubKey  string
+	DKIMKeyPath string
 }

go.mod

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/go.mod b/go.mod
index 9e87fd6..f159123 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,8 @@ module blmayer.dev/x/dovel
 go 1.19
 
 require (
+	github.com/OfimaticSRL/parsemail v0.0.0-20230123231945-3a41f46bbfa4
+	github.com/emersion/go-msgauth v0.6.6
 	github.com/emersion/go-smtp v0.15.0
 	github.com/go-git/go-git/v5 v5.4.2
 	github.com/marcospgmelo/parsemail v1.3.1-0.20201020162348-38663e9311e7
@@ -10,7 +12,6 @@ require (
 
 require (
 	github.com/Microsoft/go-winio v0.6.0 // indirect
-	github.com/OfimaticSRL/parsemail v0.0.0-20230123231945-3a41f46bbfa4 // indirect
 	github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/cloudflare/circl v1.3.0 // indirect

go.sum

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/go.sum b/go.sum
index 84f9db6..a59b1a1 100644
--- a/go.sum
+++ b/go.sum
@@ -20,10 +20,17 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emersion/go-message v0.11.2/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
+github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
+github.com/emersion/go-milter v0.3.3/go.mod h1:ablHK0pbLB83kMFBznp/Rj8aV+Kc3jw8cxzzmCNLIOY=
+github.com/emersion/go-msgauth v0.6.6 h1:buv5lL8v/3v4RpHnQFS2IPhE3nxSRX+AxnrEJbDbHhA=
+github.com/emersion/go-msgauth v0.6.6/go.mod h1:A+/zaz9bzukLM6tRWRgJ3BdrBi+TFKTvQ3fGMFOI9SM=
 github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
 github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
 github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
 github.com/emersion/go-smtp v0.15.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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
@@ -59,6 +66,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/marcospgmelo/parsemail v1.3.1-0.20201020162348-38663e9311e7 h1:KlXrFiVXXvxkEmHWmHErAxzBL7ynvJvzdLr8ZZRgYbc=
 github.com/marcospgmelo/parsemail v1.3.1-0.20201020162348-38663e9311e7/go.mod h1:bzTPHUEfHfbAxgj0nNHBAEH50bbKSkSuNVx8HEpO+5A=
+github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
 github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
 github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -77,6 +85,7 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -87,6 +96,7 @@ golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
@@ -113,6 +123,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=

interfaces/backend/backend.go

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/interfaces/backend/backend.go b/interfaces/backend/backend.go
index 323fee8..c6b8d50 100644
--- a/interfaces/backend/backend.go
+++ b/interfaces/backend/backend.go
@@ -5,6 +5,7 @@ package backend
 
 import (
 	"bytes"
+	"crypto"
 	"fmt"
 	"io"
 	"mime/multipart"
@@ -17,6 +18,7 @@ import (
 	"blmayer.dev/x/dovel/config"
 	"blmayer.dev/x/dovel/interfaces"
 	"github.com/OfimaticSRL/parsemail"
+	"github.com/emersion/go-msgauth/dkim"
 	"github.com/emersion/go-smtp"
 )
 
@@ -81,7 +83,8 @@ func (s Session) Logout() error {
 }
 
 type Backend struct {
-	Handlers map[string]config.Handler
+	Handlers   map[string]config.Handler
+	PrivateKey crypto.Signer
 }
 
 func (b Backend) NewSession(_ *smtp.Conn) (smtp.Session, error) {
@@ -98,6 +101,7 @@ func (b Backend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, erro
 
 func (b Backend) SendHandler(saveFunction func(e interfaces.Email, b []byte) error) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
+		println("backend sending email")
 		if err := r.ParseMultipartForm(20 * 1024 * 1024); err != nil {
 			println("form error: " + err.Error())
 			http.Error(w, "form error"+err.Error(), http.StatusNotAcceptable)
@@ -172,6 +176,17 @@ func (b Backend) SendHandler(saveFunction func(e interfaces.Email, b []byte) err
 		}
 		form.Close()
 
+		// dkim
+		payload := bytes.Buffer{}
+		options := &dkim.SignOptions{
+			Domain:   "mail.blmayer.dev",
+			Selector: "dkim",
+			Signer:   b.PrivateKey,
+		}
+		if err := dkim.Sign(&payload, &body, options); err != nil {
+			println("failed to sign body:", err.Error())
+		}
+
 		// dns mx for email
 		for _, to := range email.To {
 			addr := strings.Split(to, "@")
@@ -193,7 +208,7 @@ func (b Backend) SendHandler(saveFunction func(e interfaces.Email, b []byte) err
 				nil,
 				email.From,
 				[]string{to},
-				&body,
+				&payload,
 			)
 			if err != nil {
 				println("sendMail error: " + err.Error())

interfaces/file/file.go

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/interfaces/file/file.go b/interfaces/file/file.go
index 5123671..9ffc14a 100644
--- a/interfaces/file/file.go
+++ b/interfaces/file/file.go
@@ -79,6 +79,7 @@ func (f FileHandler) IndexHandler() http.HandlerFunc {
 
 func (f FileHandler) SendHandler(b backend.Backend) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
+		println("file handling send email")
 		user, pass, _ := r.BasicAuth()
 		if user != "x" || pass != f.password {
 			w.Header().Add("WWW-Authenticate", "Basic")
@@ -86,7 +87,7 @@ func (f FileHandler) SendHandler(b backend.Backend) http.HandlerFunc {
 			return
 		}
 
-		b.SendHandler(f.SaveSent)
+		b.SendHandler(f.SaveSent)(w, r)
 	}
 }
 

www/compose.html

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/www/compose.html b/www/compose.html
index 18621a6..fb6523e 100644
--- a/www/compose.html
+++ b/www/compose.html
@@ -1,15 +1,20 @@
 {{template "head.html"}}
 
-<p>// <b>composing</b></p>
+<p><b>composing</b></p>
 
 <form action=/out method=post enctype=multipart/form-data>
-	// From: <input name=from placeholder="___________" {{if .inbox}}value="{{index .inbox 0}}"{{end}}><br>
-	// To: <input name=to placeholder="________"><br>
-	// CC: <input name=cc placeholder="________"><br>
-	// CCo: <input name=cco placeholder="________"><br>
-	// Subject: <input name=subject placeholder="_________" {{if .subj}}value="{{index .subj 0}}"{{end}}><br>
-	// Body:<br><br>
-	<textarea name=body rows="7" cols="50" placeholder="________"></textarea><br>
+	<label for="from">From: </label>
+	<input name=from {{if .inbox}}value="{{index .inbox 0}}"{{end}}><br>
+	<label>To: </label>
+	<input name=to><br>
+	<label>CC: </label>
+	<input name=cc><br>
+	<label>CCo: </label>
+	<input name=cco><br>
+	<label>Subject: </label>
+	<input name=subject {{if .subj}}value="{{index .subj 0}}"{{end}}><br>
+	Body:<br><br>
+	<field><textarea name=body rows="7" cols="50"></textarea><br></field>
 	<br>
-	// <input type=submit value=send>
+	<input type=submit value=send>
 </form>

www/style-min.html

commit 8d0b023b0457c8bfcc9c2fa4ca077ea5b74a410e
Author: bmayer3 <bmayer@sibros.tech>
Date:   Sun Feb 12 20:17:52 2023 -0300

    Added dkim support

diff --git a/www/style-min.html b/www/style-min.html
index df86487..8f21fdd 100644
--- a/www/style-min.html
+++ b/www/style-min.html
@@ -10,8 +10,10 @@
 		color:#f0f
 	}
 	input, textarea {
+		flex-grow: 1;
 		background: #000;
 		border: none;
+		border-bottom: 1px solid #0f0;
 		color: #0f0;
 		font-family: monospace;
 	}
@@ -20,5 +22,10 @@
 		cursor: pointer;
 		color: #f0f;
 	}
+	label {
+		display: inline-block;
+		min-width: 80;
+	}
+
 </style>