plugins/base: append outgoing messages to Sent mailbox
And mark original message as answered. Closes: https://todo.sr.ht/~sircmpwn/koushin/15
This commit is contained in:
parent
3340fcd63d
commit
589b303f9f
4 changed files with 85 additions and 4 deletions
1
go.mod
1
go.mod
|
@ -7,6 +7,7 @@ require (
|
|||
github.com/chris-ramon/douceur v0.2.0
|
||||
github.com/emersion/go-imap v1.0.3
|
||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342
|
||||
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62
|
||||
github.com/emersion/go-message v0.11.1
|
||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b
|
||||
github.com/emersion/go-smtp v0.12.1
|
||||
|
|
2
go.sum
2
go.sum
|
@ -14,6 +14,8 @@ github.com/emersion/go-imap v1.0.3 h1:5eEee8/DTSIPfliiWqwfvjPGkU8bBtvOy/Wx+eeXzO
|
|||
github.com/emersion/go-imap v1.0.3/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 h1:5p1t3e1PomYgLWwEwhwEU5kVBwcyAcVrOpexv8AeZx0=
|
||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
|
||||
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 h1:4ZAfwfc8aDlj26kkEap1UDSwwDnJp9Ie8Uj1MSXAkPk=
|
||||
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4=
|
||||
github.com/emersion/go-message v0.11.1 h1:0C/S4JIXDTSfXB1vpqdimAYyK4+79fgEAMQ0dSL+Kac=
|
||||
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
|
||||
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q=
|
||||
|
|
|
@ -35,6 +35,30 @@ func listMailboxes(conn *imapclient.Client) ([]*imap.MailboxInfo, error) {
|
|||
return mailboxes, nil
|
||||
}
|
||||
|
||||
func getMailboxByAttribute(conn *imapclient.Client, attr string) (*imap.MailboxInfo, error) {
|
||||
ch := make(chan *imap.MailboxInfo, 10)
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- conn.List("", "%", ch)
|
||||
}()
|
||||
|
||||
var mailbox *imap.MailboxInfo
|
||||
for mbox := range ch {
|
||||
for _, a := range mbox.Attributes {
|
||||
if attr == a {
|
||||
mailbox = mbox
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := <-done; err != nil {
|
||||
return nil, fmt.Errorf("failed to get mailbox with attribute %q: %v", attr, err)
|
||||
}
|
||||
|
||||
return mailbox, nil
|
||||
}
|
||||
|
||||
func ensureMailboxSelected(conn *imapclient.Client, mboxName string) error {
|
||||
mbox := conn.Mailbox()
|
||||
if mbox == nil || mbox.Name != mboxName {
|
||||
|
@ -339,3 +363,15 @@ func getMessagePart(conn *imapclient.Client, mboxName string, uid uint32, partPa
|
|||
|
||||
return &IMAPMessage{msg}, part, nil
|
||||
}
|
||||
|
||||
func markMessageAnswered(conn *imapclient.Client, mboxName string, uid uint32) error {
|
||||
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seqSet := new(imap.SeqSet)
|
||||
seqSet.AddNum(uid)
|
||||
item := imap.FormatFlagsOp(imap.AddFlags, true)
|
||||
flags := []interface{}{imap.AnsweredFlag}
|
||||
return conn.UidStore(seqSet, item, flags, nil)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package koushinbase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
|
@ -8,10 +9,12 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~emersion/koushin"
|
||||
"github.com/emersion/go-imap"
|
||||
imapmove "github.com/emersion/go-imap-move"
|
||||
imapspecialuse "github.com/emersion/go-imap-specialuse"
|
||||
imapclient "github.com/emersion/go-imap/client"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-smtp"
|
||||
|
@ -289,12 +292,19 @@ func handleCompose(ctx *koushin.Context) error {
|
|||
msg.Text = ctx.QueryParam("body")
|
||||
msg.InReplyTo = ctx.QueryParam("in-reply-to")
|
||||
|
||||
if ctx.Request().Method == http.MethodGet && ctx.Param("uid") != "" {
|
||||
var inReplyToMboxName string
|
||||
var inReplyToUid uint32
|
||||
if ctx.Param("uid") != "" {
|
||||
// This is a reply
|
||||
mboxName, uid, err := parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
|
||||
var err error
|
||||
inReplyToMboxName, inReplyToUid, err = parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Request().Method == http.MethodGet && inReplyToUid != 0 {
|
||||
// Populate fields from original message
|
||||
partPath, err := parsePartPath(ctx.QueryParam("part"))
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||
|
@ -304,7 +314,7 @@ func handleCompose(ctx *koushin.Context) error {
|
|||
var part *message.Entity
|
||||
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
|
||||
var err error
|
||||
inReplyTo, part, err = getMessagePart(c, mboxName, uid, partPath)
|
||||
inReplyTo, part, err = getMessagePart(c, inReplyToMboxName, inReplyToUid, partPath)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -364,7 +374,39 @@ func handleCompose(ctx *koushin.Context) error {
|
|||
if _, ok := err.(koushin.AuthError); ok {
|
||||
return echo.NewHTTPError(http.StatusForbidden, err)
|
||||
}
|
||||
return err
|
||||
return fmt.Errorf("failed to send message: %v", err)
|
||||
}
|
||||
|
||||
if inReplyToUid != 0 {
|
||||
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
|
||||
return markMessageAnswered(c, inReplyToMboxName, inReplyToUid)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to mark original message as answered: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
|
||||
mbox, err := getMailboxByAttribute(c, imapspecialuse.Sent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mbox == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IMAP needs to know in advance the final size of the message, so
|
||||
// there's no way around storing it in a buffer here.
|
||||
var buf bytes.Buffer
|
||||
if err := msg.WriteTo(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flags := []string{imap.SeenFlag}
|
||||
return c.Append(mbox.Name, flags, time.Now(), &buf)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save message to Sent mailbox: %v", err)
|
||||
}
|
||||
|
||||
// TODO: append to IMAP Sent mailbox
|
||||
|
|
Loading…
Reference in a new issue