This is the main dovel repository, it has the Go code to run dovel SMTP server.
Author: blmayer (bleemayer@gmail.com)
Date: Fri Apr 21 19:56:07 2023 -0300
Parent: 2007e86
Organised files - Added support for attachments
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
+}
+
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]
-}
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
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)
+ }
+}
+
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)
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>