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

fe3c442

Author: blmayer (bleemayer@gmail.com)

Date: Fri Apr 21 19:56:07 2023 -0300

Parent: 2007e86

Organised files

- Added support for attachments

Diff

cmd/dovel/backend.go

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/cmd/dovel/backend.go b/cmd/dovel/backend.go
new file mode 100644
index 0000000..1a07252
--- /dev/null
+++ b/cmd/dovel/backend.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+	"crypto"
+	"fmt"
+	"io"
+	"strings"
+
+	"blmayer.dev/x/dovel/config"
+	"blmayer.dev/x/dovel/interfaces"
+	"github.com/OfimaticSRL/parsemail"
+	"github.com/emersion/go-smtp"
+)
+
+// A Session is returned after EHLO.
+type Session struct {
+	User     string
+	handlers map[string]config.Handler
+}
+
+func (s Session) AuthPlain(username, password string) error {
+	println("connection sent", username, password)
+	return nil
+}
+
+func (s Session) Mail(from string, opts *smtp.MailOptions) error {
+	println("Mail from:", from)
+	return nil
+}
+
+func (s Session) Rcpt(to string) error {
+	println("Rcpt to:", to)
+	return nil
+}
+
+func (s Session) Data(r io.Reader) error {
+	content, err := io.ReadAll(r)
+	if err != nil {
+		println("read content", err.Error())
+		return err
+	}
+
+	email, err := parsemail.Parse(strings.NewReader(string(content)))
+	if err != nil {
+		println("parse email", err.Error())
+		return err
+	}
+
+	// get user from to field
+	mail := interfaces.ToEmail(email)
+	mail.Raw = content
+	for _, to := range mail.To {
+		userDomain := strings.Split(to, "@")
+		handler, ok := s.handlers[userDomain[1]]
+		if !ok {
+			println("no handler for domain", userDomain[1])
+			return fmt.Errorf("no handler for domain %s", userDomain)
+		}
+		mail.To = []string{to}
+		if err := handler(mail); err != nil {
+			println("handler error", err.Error())
+			return fmt.Errorf("handler error %s", err.Error())
+		}
+	}
+
+	return nil
+}
+
+func (s Session) Reset() {}
+
+func (s Session) Logout() error {
+	println("logged out")
+	return nil
+}
+
+type backend struct {
+	Handlers   map[string]config.Handler
+	PrivateKey crypto.Signer
+}
+
+func (b backend) NewSession(_ *smtp.Conn) (smtp.Session, error) {
+	return Session{handlers: b.Handlers}, nil
+}
+

cmd/dovel/functions.go

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/cmd/dovel/functions.go b/cmd/dovel/functions.go
deleted file mode 100644
index 3fcf87e..0000000
--- a/cmd/dovel/functions.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package main
-
-import "strings"
-
-func heading(text string, l int) string {
-	first, _, _ := strings.Cut(text, "\n")
-	first = strings.TrimSpace(first)
-	if len(first) < l {
-		return first
-	}
-	return first[:l]
-}

cmd/dovel/main.go

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/cmd/dovel/main.go b/cmd/dovel/main.go
index 4d435d8..9c4edc1 100644
--- a/cmd/dovel/main.go
+++ b/cmd/dovel/main.go
@@ -9,7 +9,6 @@ import (
 	"time"
 
 	"blmayer.dev/x/dovel/config"
-	"blmayer.dev/x/dovel/interfaces/backend"
 	"blmayer.dev/x/dovel/interfaces/file"
 	"blmayer.dev/x/dovel/interfaces/gwi"
 
@@ -35,7 +34,7 @@ var (
 		},
 	}
 	cfg config.Config
-	b   = backend.Backend{Handlers: map[string]config.Handler{}}
+	b   = backend{Handlers: map[string]config.Handler{}}
 )
 
 func main() {
@@ -68,8 +67,7 @@ func main() {
 				panic(err)
 			}
 
-			funcs := map[string]any{"heading": heading}
-			mail, err := file.NewFileHandler(hand, v, funcs)
+			mail, err := file.NewFileHandler(hand, v, nil)
 			if err != nil {
 				panic(err)
 			}
@@ -77,7 +75,7 @@ func main() {
 			if hand.Templates != "" {
 				http.HandleFunc(hand.Domain+"/", mail.IndexHandler())
 				http.HandleFunc(hand.Domain+"/assets/", mail.AssetsHandler())
-				http.HandleFunc(hand.Domain+"/out", mail.SendHandler(b))
+				http.HandleFunc(hand.Domain+"/out", SendHandler(mail, v))
 			}
 
 			b.Handlers[hand.Domain] = mail.Save

cmd/dovel/web.go

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/cmd/dovel/web.go b/cmd/dovel/web.go
new file mode 100644
index 0000000..cdc925f
--- /dev/null
+++ b/cmd/dovel/web.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+	"io"
+	"net/http"
+	"time"
+
+	"blmayer.dev/x/dovel/interfaces"
+)
+
+func SendHandler(mailer interfaces.Mailer, vault interfaces.Vault) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		println("file handling send email")
+		user, pass, _ := r.BasicAuth()
+		if user == "" || !vault.Validate(user, pass) {
+			w.Header().Add("WWW-Authenticate", "Basic")
+			http.Error(w, "wrong auth", http.StatusUnauthorized)
+			return
+		}
+
+		if err := r.ParseMultipartForm(20 * 1024 * 1024); err != nil {
+			println("form error: " + err.Error())
+			http.Error(w, "form error"+err.Error(), http.StatusNotAcceptable)
+			return
+		}
+		fo := r.MultipartForm
+
+		email := interfaces.Email{
+			From:        fo.Value["from"][0],
+			To:          fo.Value["to"],
+			Cc:          fo.Value["cc"],
+			Subject:     fo.Value["subject"][0],
+			Date:        time.Now(),
+			Body:        fo.Value["body"][0],
+			Attachments: map[string]interfaces.Attachment{},
+		}
+
+		for name, fi := range fo.File {
+			attach, err := fi[0].Open()
+			if err != nil {
+				println("error getting attachment: " + err.Error())
+				continue
+			}
+			defer attach.Close()
+
+			content, err := io.ReadAll(attach)
+			if err != nil {
+				println("error getting attachment: " + err.Error())
+				continue
+			}
+			email.Attachments[name] = interfaces.Attachment{
+				Name: name,
+				Data: content,
+				// TODO: Add content-type
+			}
+		}
+		
+		err := mailer.Send(email)
+		if err != nil {
+			println("send error: " + err.Error())
+			http.Error(w, "send error"+err.Error(), http.StatusInternalServerError)
+			return
+		}
+		http.Redirect(w, r, "/", http.StatusFound)
+	}
+}
+

interfaces/file/file.go

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/interfaces/file/file.go b/interfaces/file/file.go
index 31e5be2..a987c0f 100644
--- a/interfaces/file/file.go
+++ b/interfaces/file/file.go
@@ -25,7 +25,6 @@ import (
 
 	"blmayer.dev/x/dovel/config"
 	"blmayer.dev/x/dovel/interfaces"
-	"blmayer.dev/x/dovel/interfaces/backend"
 
 	"github.com/OfimaticSRL/parsemail"
 	"github.com/emersion/go-msgauth/dkim"
@@ -111,63 +110,6 @@ func (f FileHandler) AssetsHandler() 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 == "" || !f.vault.Validate(user, pass) {
-			w.Header().Add("WWW-Authenticate", "Basic")
-			http.Error(w, "wrong auth", http.StatusUnauthorized)
-			return
-		}
-
-		if err := r.ParseMultipartForm(20 * 1024 * 1024); err != nil {
-			println("form error: " + err.Error())
-			http.Error(w, "form error"+err.Error(), http.StatusNotAcceptable)
-			return
-		}
-		fo := r.MultipartForm
-
-		email := interfaces.Email{
-			From:        fo.Value["from"][0],
-			To:          fo.Value["to"],
-			Cc:          fo.Value["cc"],
-			Subject:     fo.Value["subject"][0],
-			Date:        time.Now(),
-			Body:        fo.Value["body"][0],
-			Attachments: map[string]interfaces.Attachment{},
-		}
-
-		for name, fi := range fo.File {
-			attach, err := fi[0].Open()
-			if err != nil {
-				println("error getting attachment: " + err.Error())
-				continue
-			}
-			defer attach.Close()
-
-			content, err := io.ReadAll(attach)
-			if err != nil {
-				println("error getting attachment: " + err.Error())
-				continue
-			}
-			email.Attachments[name] = interfaces.Attachment{
-				Name: name,
-				Data: content,
-				// TODO: Add content-type
-			}
-		}
-		
-		err := f.Send(email)
-		if err != nil {
-			println("send error: " + err.Error())
-			http.Error(w, "send error"+err.Error(), http.StatusInternalServerError)
-			return
-		}
-		http.Redirect(w, r, "/", http.StatusFound)
-	}
-}
-
 func (f FileHandler) Save(email interfaces.Email) error {
 	mailDir := path.Join(f.root, email.To[0], email.Subject)
 	os.MkdirAll(mailDir, os.ModeDir|0o700)

www/compose.html

commit fe3c44266e4deadabe77afb9158b88760e5cff69
Author: blmayer <bleemayer@gmail.com>
Date:   Fri Apr 21 19:56:07 2023 -0300

    Organised files
    
    - Added support for attachments

diff --git a/www/compose.html b/www/compose.html
index 3e2ad77..ff0b41a 100644
--- a/www/compose.html
+++ b/www/compose.html
@@ -26,5 +26,6 @@
 	</table>
 	<field><textarea style="width:100%" name=body rows="7" cols="50" placeholder="Body:"></textarea><br></field>
 	<br>
+	<label>Attachments: </label><input type=file name=files multiple><br>
 	<input type=submit value=send>
 </form>