plugins/base: save message as draft

This commit is contained in:
Simon Ser 2020-01-24 17:49:50 +01:00
parent 267999b6e5
commit bfc617b702
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
4 changed files with 98 additions and 45 deletions

View file

@ -2,12 +2,15 @@ package koushinbase
import (
"bufio"
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/emersion/go-imap"
imapspecialuse "github.com/emersion/go-imap-specialuse"
imapclient "github.com/emersion/go-imap/client"
"github.com/emersion/go-message"
"github.com/emersion/go-message/textproto"
@ -375,3 +378,45 @@ func markMessageAnswered(conn *imapclient.Client, mboxName string, uid uint32) e
flags := []interface{}{imap.AnsweredFlag}
return conn.UidStore(seqSet, item, flags, nil)
}
type mailboxType int
const (
mailboxSent mailboxType = iota
mailboxDrafts
)
func appendMessage(c *imapclient.Client, msg *OutgoingMessage, mboxType mailboxType) (saved bool, err error) {
var mboxAttr string
switch mboxType {
case mailboxSent:
mboxAttr = imapspecialuse.Sent
case mailboxDrafts:
mboxAttr = imapspecialuse.Drafts
}
mbox, err := getMailboxByAttribute(c, mboxAttr)
if err != nil {
return false, err
}
if mbox == nil {
return false, 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 false, err
}
flags := []string{imap.SeenFlag}
if mboxType == mailboxDrafts {
flags = append(flags, imap.DraftFlag)
}
if err := c.Append(mbox.Name, flags, time.Now(), &buf); err != nil {
return false, err
}
return true, nil
}

View file

@ -26,6 +26,7 @@
<label for="attachments">Attachments:</label>
<input type="file" name="attachments" id="attachments" multiple>
<br><br>
<input type="submit" name="save_as_draft" value="Save as draft">
<input type="submit" value="Send">
</form>

View file

@ -1,7 +1,6 @@
package koushinbase
import (
"bytes"
"fmt"
"io/ioutil"
"mime"
@ -9,12 +8,10 @@ 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"
@ -281,6 +278,39 @@ type ComposeRenderData struct {
Message *OutgoingMessage
}
// Send message, append it to the Sent mailbox, mark the original message as
// answered
func submitCompose(ctx *koushin.Context, msg *OutgoingMessage, inReplyToMboxName string, inReplyToUid uint32) error {
err := ctx.Session.DoSMTP(func(c *smtp.Client) error {
return sendMessage(c, msg)
})
if err != nil {
if _, ok := err.(koushin.AuthError); ok {
return echo.NewHTTPError(http.StatusForbidden, 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 {
_, err := appendMessage(c, msg, mailboxSent)
return err
})
if err != nil {
return fmt.Errorf("failed to save message to Sent mailbox: %v", err)
}
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
}
func handleCompose(ctx *koushin.Context) error {
var msg OutgoingMessage
if strings.ContainsRune(ctx.Session.Username(), '@') {
@ -355,6 +385,12 @@ func handleCompose(ctx *koushin.Context) error {
}
if ctx.Request().Method == http.MethodPost {
formParams, err := ctx.FormParams()
if err != nil {
return fmt.Errorf("failed to parse form: %v", err)
}
_, saveAsDraft := formParams["save_as_draft"]
msg.From = ctx.FormValue("from")
msg.To = parseAddressList(ctx.FormValue("to"))
msg.Subject = ctx.FormValue("subject")
@ -367,52 +403,23 @@ func handleCompose(ctx *koushin.Context) error {
}
msg.Attachments = form.File["attachments"]
err = ctx.Session.DoSMTP(func(c *smtp.Client) error {
return sendMessage(c, &msg)
})
if err != nil {
if _, ok := err.(koushin.AuthError); ok {
return echo.NewHTTPError(http.StatusForbidden, err)
}
return fmt.Errorf("failed to send message: %v", err)
}
if inReplyToUid != 0 {
if saveAsDraft {
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
return markMessageAnswered(c, inReplyToMboxName, inReplyToUid)
copied, err := appendMessage(c, &msg, mailboxDrafts)
if err != nil {
return err
}
if !copied {
return fmt.Errorf("no Draft mailbox found")
}
return nil
})
if err != nil {
return fmt.Errorf("failed to mark original message as answered: %v", err)
return fmt.Errorf("failed to save message to Draft mailbox: %v", err)
}
} else {
return submitCompose(ctx, &msg, inReplyToMboxName, inReplyToUid)
}
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
// TODO: add \Answered flag to original IMAP message
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
}
return ctx.Render(http.StatusOK, "compose.html", &ComposeRenderData{

View file

@ -5,10 +5,10 @@ import (
"html/template"
"path/filepath"
"git.sr.ht/~emersion/koushin"
"github.com/labstack/echo/v4"
"github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
"git.sr.ht/~emersion/koushin"
)
type luaRoute struct {