Browse Source

plugins/base: save message as draft

master
Simon Ser 4 years ago
parent
commit
bfc617b702
No known key found for this signature in database GPG Key ID: FDE7BE0E88F5E48
4 changed files with 98 additions and 45 deletions
  1. +45
    -0
      plugins/base/imap.go
  2. +1
    -0
      plugins/base/public/compose.html
  3. +51
    -44
      plugins/base/routes.go
  4. +1
    -1
      plugins/lua/lua.go

+ 45
- 0
plugins/base/imap.go 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
}

+ 1
- 0
plugins/base/public/compose.html 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>



+ 51
- 44
plugins/base/routes.go 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)
}
}

err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
mbox, err := getMailboxByAttribute(c, imapspecialuse.Sent)
if err != nil {
return err
return fmt.Errorf("failed to save message to Draft mailbox: %v", 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)
} else {
return submitCompose(ctx, &msg, inReplyToMboxName, inReplyToUid)
}

// 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{


+ 1
- 1
plugins/lua/lua.go 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 {


Loading…
Cancel
Save