This is the main dovel repository, it has the Go code to run dovel SMTP server.
Author: blmayer (bleemayer@gmail.com)
Date: Fri Sep 8 19:50:01 2023 -0300
Parent: d4e60c7
Organized subpackages
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/cmd/dovel/backend.go b/cmd/dovel/backend.go
index be441ac..c5f9949 100644
--- a/cmd/dovel/backend.go
+++ b/cmd/dovel/backend.go
@@ -15,7 +15,7 @@ import (
"strings"
"time"
- "git.derelict.garden/dovel/email/util/wkd"
+ "git.derelict.garden/dovel/email/wkd"
"github.com/ProtonMail/gopenpgp/v2/helper"
"github.com/emersion/go-mbox"
"github.com/emersion/go-msgauth/dkim"
@@ -188,7 +188,6 @@ func (s *Session) Send(from string, tos []string, raw io.Reader) error {
if err != nil {
return err
}
-
}
return nil
}
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/cmd/dovel/main.go b/cmd/dovel/main.go
index 6a95bf6..76c9df4 100644
--- a/cmd/dovel/main.go
+++ b/cmd/dovel/main.go
@@ -14,7 +14,7 @@ import (
var (
domain string
configPath string
- v vault.Vault[email.WebUser]
+ v vault.Vault[email.User]
)
func main() {
@@ -33,7 +33,7 @@ func main() {
cfg := email.Config{}
json.NewDecoder(configFile).Decode(&cfg)
- v, err = vault.NewJSONPlainTextVault[email.WebUser](cfg.VaultFile)
+ v, err = vault.NewJSONPlainTextVault[email.User](cfg.VaultFile)
if err != nil {
panic(err)
}
@@ -44,8 +44,7 @@ func main() {
s.ReadTimeout = 10 * time.Second
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
- s.MaxRecipients = 2
- s.AllowInsecureAuth = true
+ s.MaxRecipients = 5
if err := s.ListenAndServe(); err != nil {
panic(err)
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/doc.go b/doc.go
index c71b848..da7e951 100644
--- a/doc.go
+++ b/doc.go
@@ -1,9 +1,8 @@
-// email is a SMTP server and web interface that is simple to setup and
-// use.
+// email is a SMTP server that is simple to setup and use.
//
// This package has two parts:
// 1. The dovel binary
-// 2. The interfaces library
+// 2. The library
//
// # The binary
//
@@ -18,9 +17,8 @@
//
// # The library
//
-// This package also delivers a subpackage called interfaces, it contains
-// structs that are used through this project. Those are also used in templates
-// and you can use them in other projects.
+// This package also delivers a subpackage called email, it contains
+// structs that are used through this project.
//
// # Setting up your email server
//
@@ -39,19 +37,10 @@
// [git.derelict.garden/dovel/email/config.Config].
//
// The server itself only receives email, for this feature only the MX record
-// is enough. There are 2 handlers included in this project:
-//
-// - gwi: to be used with the gwi project, saves email by subject, ish.
-// - file: closer to the standard, saves email using to and subject fields.
-//
-// The "file" handler gives you multiple inboxes, so you can separate by giving
-// different addresses, for example, email1@my.domain and email2@my.domain,
-// will have different inboxes. Inside each inbox emails are separated by
-// subject.
-//
-// The "gwi" handler is intended for mailing lists, or issues on git
-// repositories, email is added to the open folder, and then separated by
-// subject. See the gwi project for more information.
+// is enough. Everytime dovel receives an email it searches for an executable
+// file in $XDG_CONFIG_DIR/dovel/ named receive-DOMAIN, for the DOMAIN the is
+// receiving the email and executes it passing the email in mbox format as
+// standard input.
//
// # Configuring DNS records
//
@@ -59,4 +48,9 @@
// sending email is more complicated, some receiving servers only need the SPF
// and PTR records, some also need DKIM and DMARK, adjust according to your
// needs.
+//
+// # Sending email
+//
+// Dovel also sends email, for that it needs a valid VaultFile path on the
+// config. That means you need at least one user descrived on the json file.
package email
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/model.go b/model.go
index dd0b2d7..94d528a 100644
--- a/model.go
+++ b/model.go
@@ -1,132 +1,36 @@
-// package dovel contains interfaces that are passed to the common
+// package email contains interfaces that are passed to the common
// functions on the server and web projects. You can import this to use
// on templates and handlers.
package email
import (
"crypto"
- "encoding/base64"
- "fmt"
- "io"
- "time"
-
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
-
- "github.com/OfimaticSRL/parsemail"
)
// Config is used to configure your email server.
// Port is used to specify which port the server should listen
// to connections, a typicall value is 2525.
// Domain is what the server should respond in a HELO or EHLO request.
-// Handlers is a list of domains that are allowed to send/receive emails.
+// VaultFile is the path to a json file containing the list of users
+// that can send email.
type Config struct {
Port string
Domain string
VaultFile string
}
-type tz struct {
- Name string
- Offset int
-}
-
-type WebUser struct {
+type User struct {
Name string
Email string
Password string
- TimeZone tz
PrivateKey crypto.Signer
}
-func (w WebUser) Login() string {
+func (w User) Login() string {
return w.Name
}
-func (w WebUser) Pass() string {
+func (w User) Pass() string {
return w.Password
}
-type File struct {
- *object.File
- Size int64
-}
-
-type Info struct {
- User string
- Repo string
- Ref plumbing.Hash
- RefName string
- Args string
-}
-
-type Mailbox struct {
- Title string
- LastMod time.Time
- Length int
-}
-
-type Attachment struct {
- Name string
- ContentType string
- Data []byte
-}
-
-type Opt struct {
- Encrypt bool
- Key string
-}
-
-type Email struct {
- Headers map[string][]string
- ID string
- From string
- To []string
- Cc []string
- BCc []string
- Date time.Time
- Subject string
- Body string
- Attachments map[string]Attachment
- Raw []byte
-}
-
-func ToEmail(mail parsemail.Email) Email {
- m := Email{
- Headers: mail.Header,
- From: mail.From[0].Address,
- To: []string{},
- Cc: []string{},
- Subject: mail.Subject,
- ID: mail.MessageID,
- Date: mail.Date,
- Body: mail.TextBody,
- Attachments: map[string]Attachment{},
- }
- for _, to := range mail.To {
- m.To = append(m.To, to.Address)
- }
- for _, cc := range mail.Cc {
- m.Cc = append(m.Cc, cc.Address)
- }
- for _, a := range mail.Attachments {
- content, err := io.ReadAll(a.Data)
- if err != nil {
- fmt.Println("read attachment", err.Error())
- continue
- }
-
- encContent := base64.StdEncoding.EncodeToString(content)
- m.Attachments[a.Filename] = Attachment{
- Name: a.Filename,
- ContentType: a.ContentType,
- Data: []byte(encContent),
- }
- }
- return m
-}
-
-type Mailer interface {
- Save(from, to string, raw io.Reader) error
-}
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/util/decode/multipart.go b/util/decode/multipart.go
deleted file mode 100644
index 2df6798..0000000
--- a/util/decode/multipart.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package util
-
-import "strings"
-
-func Decode(header, content string) (string, map[string]string) {
- if strings.Contains(header, "multipart") {
- boundary := strings.Split(header, `"`)[1]
- }
-
-
-}
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/util/multipart.go b/util/multipart.go
deleted file mode 100644
index 698bb27..0000000
--- a/util/multipart.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package util
-
-import (
- "encoding/base64"
- "io"
- "mime"
- "mime/multipart"
- "strings"
-
- "git.derelict.garden/dovel/email/model"
-)
-
-func Decode(header, content string) (string, map[string]model.Attachment, error) {
- mediaType, params, err := mime.ParseMediaType(header)
- if err != nil {
- return content, nil, err
- }
- if !strings.HasPrefix(mediaType, "multipart/") {
- return content, nil, nil
- }
-
- var text string
- attach := map[string]model.Attachment{}
- r := multipart.NewReader(strings.NewReader(content), params["boundary"])
- for {
- p, err := r.NextPart()
- if err == io.EOF {
- break
- }
- if err != nil {
- return text, attach, err
- }
- cont, err := io.ReadAll(p)
- if err != nil {
- return text, attach, err
- }
-
- if p.Header.Get("Content-Transfer-Encoding") == "base64" {
- cont, err = base64.RawStdEncoding.DecodeString(
- string(cont),
- )
- if err != nil {
- println("base64", err.Error())
- }
- }
-
- cType := p.Header.Get("Content-Type")
- if fileName := p.FileName(); fileName != "" {
- attach[fileName] = model.Attachment{
- Name: fileName,
- Data: cont,
- ContentType: cType,
- }
- continue
- }
- if strings.HasPrefix(cType, "text/plain") {
- text = string(cont)
- }
- }
- return text, attach, nil
-}
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/util/multipart_test.go b/util/multipart_test.go
deleted file mode 100644
index 2ea92f0..0000000
--- a/util/multipart_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package util
-
-import (
- "testing"
-)
-
-var header = `multipart/alternative; boundary="000000000000b05aa10600f3345f"`
-var body = `--000000000000b05aa10600f3345f
-Content-Type: text/plain; charset="UTF-8"
-
-sdfv qergwergwergvd fvds fvv
-
---000000000000b05aa10600f3345f
-Content-Type: text/html; charset="UTF-8"
-
-<div dir="ltr"><div class="gmail_default" style="font-family:arial,sans-serif;font-size:small;color:#000000">sdfv qergwergwergvd fvds fvv<br></div></div>
-
---000000000000b05aa10600f3345f--
-`
-
-func TestDecode(t *testing.T) {
- text, attach, err := Decode(header, body)
- if err != nil {
- t.Error(err)
- }
- if text == "" {
- t.Error("text is empty")
- }
- t.Log(text)
- t.Log(attach)
-}
-
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/util/parse_test.go b/util/parse_test.go
deleted file mode 100644
index 59d917d..0000000
--- a/util/parse_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package util
-
-import (
- "testing"
-
- "git.derelict.garden/dovel/email/model"
- "git.derelict.garden/dovel/email/model/file"
-)
-
-func TestParseMail(t *testing.T) {
- h, err := file.NewFileHandler(model.CommonConfig{}, nil, nil)
- if err != nil {
- t.Error(err)
- return
- }
-
- mail, err := h.Mail(
- "mail/test/attachment/base64.txt",
- )
- if err != nil {
- t.Error(err)
- }
- if len(mail.Attachments) == 0 {
- t.Error("attachment not parsed")
- }
-
- t.Log(mail)
-}
commit abf55a1d39e734f0bbf834e7055096bf411436e2
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 8 19:50:01 2023 -0300
Organized subpackages
diff --git a/wkd/wkd.go b/wkd/wkd.go
new file mode 100644
index 0000000..1a86698
--- /dev/null
+++ b/wkd/wkd.go
@@ -0,0 +1,33 @@
+package wkd
+
+import (
+ "crypto/sha1"
+ "fmt"
+ "net/http"
+
+ pgp "github.com/ProtonMail/gopenpgp/v2/crypto"
+ "github.com/tv42/zbase32"
+)
+
+const suffix = "/.well-known/openpgpkey/hu/"
+
+func FetchPGPKey(local, domain string) (string, error) {
+ s := sha1.Sum([]byte(local))
+ slug := zbase32.EncodeToString(s[:])
+ res, err := http.Get("https://" + domain + suffix + slug)
+ if err != nil {
+ return "", err
+ }
+
+ if res.StatusCode > 399 {
+ return "", fmt.Errorf("key not found")
+ }
+ defer res.Body.Close()
+
+ key, err := pgp.NewKeyFromReader(res.Body)
+ if err != nil {
+ return "", err
+ }
+
+ return key.GetArmoredPublicKey()
+}