move email composing to utils
This commit is contained in:
79
bot/email.go
79
bot/email.go
@@ -2,15 +2,10 @@ package bot
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/emersion/go-msgauth/dkim"
|
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/format"
|
"maunium.net/go/mautrix/format"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
@@ -206,75 +201,11 @@ func (b *Bot) Send2Email(ctx context.Context, to, subject, body string) error {
|
|||||||
body = b.getBody(content)
|
body = b.getBody(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg strings.Builder
|
ID := evt.ID.String()[1:] + "@" + b.domain
|
||||||
msg.WriteString("From: ")
|
data := utils.
|
||||||
msg.WriteString(from)
|
NewEmail(ID, inReplyTo, subject, from, to, body, "", nil).
|
||||||
msg.WriteString("\r\n")
|
Compose(b.getBotSettings().DKIMPrivateKey())
|
||||||
|
return b.mta.Send(from, to, data)
|
||||||
msg.WriteString("To: ")
|
|
||||||
msg.WriteString(to)
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
msg.WriteString("Message-Id: ")
|
|
||||||
msg.WriteString(evt.ID.String()[1:] + "@" + b.domain)
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
msg.WriteString("Date: ")
|
|
||||||
msg.WriteString(time.Now().UTC().Format(time.RFC1123Z))
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
if inReplyTo != "" {
|
|
||||||
msg.WriteString("In-Reply-To: ")
|
|
||||||
msg.WriteString(inReplyTo)
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.WriteString("Subject: ")
|
|
||||||
msg.WriteString(subject)
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
msg.WriteString(body)
|
|
||||||
msg.WriteString("\r\n")
|
|
||||||
|
|
||||||
msg = b.signDKIM(msg)
|
|
||||||
|
|
||||||
return b.mta.Send(from, to, msg.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) signDKIM(body strings.Builder) strings.Builder {
|
|
||||||
privkey := b.getBotSettings().DKIMPrivateKey()
|
|
||||||
if privkey == "" {
|
|
||||||
b.log.Warn("DKIM private key not found, email will be sent unsigned")
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
pemblock, _ := pem.Decode([]byte(privkey))
|
|
||||||
if pemblock == nil {
|
|
||||||
b.log.Error("cannot decode DKIM private key")
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
parsedkey, err := x509.ParsePKCS8PrivateKey(pemblock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
b.log.Error("cannot parse PKCS8 private key: %v", err)
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
signer := parsedkey.(crypto.Signer)
|
|
||||||
|
|
||||||
options := &dkim.SignOptions{
|
|
||||||
Domain: b.domain,
|
|
||||||
Selector: "postmoogle",
|
|
||||||
Signer: signer,
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg strings.Builder
|
|
||||||
err = dkim.Sign(&msg, strings.NewReader(body.String()), options)
|
|
||||||
if err != nil {
|
|
||||||
b.log.Error("cannot sign email: %v", err)
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) sendFiles(ctx context.Context, roomID id.RoomID, files []*utils.File, noThreads bool, parentID id.EventID) {
|
func (b *Bot) sendFiles(ctx context.Context, roomID id.RoomID, files []*utils.File, noThreads bool, parentID id.EventID) {
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/emersion/go-msgauth/dkim"
|
||||||
|
)
|
||||||
|
|
||||||
// MTA is mail transfer agent
|
// MTA is mail transfer agent
|
||||||
type MTA interface {
|
type MTA interface {
|
||||||
Send(from, to, data string) error
|
Send(from, to, data string) error
|
||||||
@@ -7,6 +17,9 @@ type MTA interface {
|
|||||||
|
|
||||||
// Email object
|
// Email object
|
||||||
type Email struct {
|
type Email struct {
|
||||||
|
data strings.Builder
|
||||||
|
|
||||||
|
Date string
|
||||||
MessageID string
|
MessageID string
|
||||||
InReplyTo string
|
InReplyTo string
|
||||||
From string
|
From string
|
||||||
@@ -20,6 +33,7 @@ type Email struct {
|
|||||||
// NewEmail constructs Email object
|
// NewEmail constructs Email object
|
||||||
func NewEmail(messageID, inReplyTo, subject, from, to, text, html string, files []*File) *Email {
|
func NewEmail(messageID, inReplyTo, subject, from, to, text, html string, files []*File) *Email {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
|
Date: time.Now().UTC().Format(time.RFC1123Z),
|
||||||
MessageID: messageID,
|
MessageID: messageID,
|
||||||
InReplyTo: inReplyTo,
|
InReplyTo: inReplyTo,
|
||||||
From: from,
|
From: from,
|
||||||
@@ -40,3 +54,71 @@ func NewEmail(messageID, inReplyTo, subject, from, to, text, html string, files
|
|||||||
|
|
||||||
return email
|
return email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compose converts email object to string and (optionally) signs it
|
||||||
|
func (e *Email) Compose(privkey string) string {
|
||||||
|
domain := strings.SplitN(e.From, "@", 2)[0]
|
||||||
|
|
||||||
|
e.data.WriteString("From: ")
|
||||||
|
e.data.WriteString(e.From)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.data.WriteString("To: ")
|
||||||
|
e.data.WriteString(e.To)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.data.WriteString("Message-Id: ")
|
||||||
|
e.data.WriteString(e.MessageID)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.data.WriteString("Date: ")
|
||||||
|
e.data.WriteString(e.Date)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
if e.InReplyTo != "" {
|
||||||
|
e.data.WriteString("In-Reply-To: ")
|
||||||
|
e.data.WriteString(e.InReplyTo)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.data.WriteString("Subject: ")
|
||||||
|
e.data.WriteString(e.Subject)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.data.WriteString(e.Text)
|
||||||
|
e.data.WriteString("\r\n")
|
||||||
|
|
||||||
|
e.sign(domain, privkey)
|
||||||
|
return e.data.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Email) sign(domain, privkey string) {
|
||||||
|
if privkey == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pemblock, _ := pem.Decode([]byte(privkey))
|
||||||
|
if pemblock == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedkey, err := x509.ParsePKCS8PrivateKey(pemblock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
signer := parsedkey.(crypto.Signer)
|
||||||
|
|
||||||
|
options := &dkim.SignOptions{
|
||||||
|
Domain: domain,
|
||||||
|
Selector: "postmoogle",
|
||||||
|
Signer: signer,
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg strings.Builder
|
||||||
|
err = dkim.Sign(&msg, strings.NewReader(e.data.String()), options)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.data = msg
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user