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>