mirror of https://git.sr.ht/~rjarry/aerc
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
3.4 KiB
Go
156 lines
3.4 KiB
Go
package jmap
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"git.sr.ht/~rjarry/aerc/lib/log"
|
|
"git.sr.ht/~rjarry/aerc/worker/types"
|
|
"git.sr.ht/~rockorager/go-jmap"
|
|
"git.sr.ht/~rockorager/go-jmap/mail/email"
|
|
"git.sr.ht/~rockorager/go-jmap/mail/emailsubmission"
|
|
"git.sr.ht/~rockorager/go-jmap/mail/mailbox"
|
|
"github.com/emersion/go-message/mail"
|
|
)
|
|
|
|
func (w *JMAPWorker) handleStartSend(msg *types.StartSendingMessage) error {
|
|
reader, writer := io.Pipe()
|
|
send := &jmapSendWriter{writer: writer, done: make(chan error)}
|
|
|
|
w.w.PostMessage(&types.MessageWriter{
|
|
Message: types.RespondTo(msg),
|
|
Writer: send,
|
|
}, nil)
|
|
|
|
go func() {
|
|
defer log.PanicHandler()
|
|
defer close(send.done)
|
|
|
|
identity, err := w.getSenderIdentity(msg.From)
|
|
if err != nil {
|
|
send.done <- err
|
|
return
|
|
}
|
|
|
|
blob, err := w.Upload(reader)
|
|
if err != nil {
|
|
send.done <- err
|
|
return
|
|
}
|
|
|
|
var req jmap.Request
|
|
|
|
// Import the blob into drafts
|
|
req.Invoke(&email.Import{
|
|
Account: w.AccountId(),
|
|
Emails: map[string]*email.EmailImport{
|
|
"aerc": {
|
|
BlobID: blob.ID,
|
|
MailboxIDs: map[jmap.ID]bool{
|
|
w.roles[mailbox.RoleDrafts]: true,
|
|
},
|
|
Keywords: map[string]bool{
|
|
"$draft": true,
|
|
"$seen": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
from := &emailsubmission.Address{Email: msg.From.Address}
|
|
var rcpts []*emailsubmission.Address
|
|
for _, address := range msg.Rcpts {
|
|
rcpts = append(rcpts, &emailsubmission.Address{
|
|
Email: address.Address,
|
|
})
|
|
}
|
|
envelope := &emailsubmission.Envelope{MailFrom: from, RcptTo: rcpts}
|
|
onSuccess := jmap.Patch{
|
|
"keywords/$draft": nil,
|
|
w.rolePatch(mailbox.RoleSent): true,
|
|
w.rolePatch(mailbox.RoleDrafts): nil,
|
|
}
|
|
if copyTo := w.dir2mbox[msg.CopyTo]; copyTo != "" {
|
|
onSuccess[w.mboxPatch(copyTo)] = true
|
|
}
|
|
// Create the submission
|
|
req.Invoke(&emailsubmission.Set{
|
|
Account: w.AccountId(),
|
|
Create: map[jmap.ID]*emailsubmission.EmailSubmission{
|
|
"sub": {
|
|
IdentityID: identity,
|
|
EmailID: "#aerc",
|
|
Envelope: envelope,
|
|
},
|
|
},
|
|
OnSuccessUpdateEmail: map[jmap.ID]jmap.Patch{
|
|
"#sub": onSuccess,
|
|
},
|
|
})
|
|
|
|
resp, err := w.Do(&req)
|
|
if err != nil {
|
|
send.done <- err
|
|
return
|
|
}
|
|
|
|
for _, inv := range resp.Responses {
|
|
switch r := inv.Args.(type) {
|
|
case *email.ImportResponse:
|
|
if err, ok := r.NotCreated["aerc"]; ok {
|
|
send.done <- wrapSetError(err)
|
|
return
|
|
}
|
|
case *emailsubmission.SetResponse:
|
|
if err, ok := r.NotCreated["sub"]; ok {
|
|
send.done <- wrapSetError(err)
|
|
return
|
|
}
|
|
case *jmap.MethodError:
|
|
send.done <- wrapMethodError(r)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
type jmapSendWriter struct {
|
|
writer *io.PipeWriter
|
|
done chan error
|
|
}
|
|
|
|
func (w *jmapSendWriter) Write(data []byte) (int, error) {
|
|
return w.writer.Write(data)
|
|
}
|
|
|
|
func (w *jmapSendWriter) Close() error {
|
|
writeErr := w.writer.Close()
|
|
sendErr := <-w.done
|
|
if writeErr != nil {
|
|
return writeErr
|
|
}
|
|
return sendErr
|
|
}
|
|
|
|
func (w *JMAPWorker) getSenderIdentity(from *mail.Address) (jmap.ID, error) {
|
|
if len(w.identities) == 0 {
|
|
if err := w.GetIdentities(); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
name, domain, _ := strings.Cut(from.Address, "@")
|
|
for _, ident := range w.identities {
|
|
n, d, _ := strings.Cut(ident.Email, "@")
|
|
switch {
|
|
case n == name && d == domain:
|
|
fallthrough
|
|
case n == "*" && d == domain:
|
|
return ident.ID, nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("no identity found for address: %s@%s", name, domain)
|
|
}
|