121 lines
2.6 KiB
Go
121 lines
2.6 KiB
Go
package imapserver
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/emersion/go-imap/v2"
|
|
"github.com/emersion/go-imap/v2/internal"
|
|
"github.com/emersion/go-imap/v2/internal/imapwire"
|
|
)
|
|
|
|
// appendLimit is the maximum size of an APPEND payload.
|
|
//
|
|
// TODO: make configurable
|
|
const appendLimit = 100 * 1024 * 1024 // 100MiB
|
|
|
|
func (c *Conn) handleAppend(tag string, dec *imapwire.Decoder) error {
|
|
var (
|
|
mailbox string
|
|
options imap.AppendOptions
|
|
)
|
|
if !dec.ExpectSP() || !dec.ExpectMailbox(&mailbox) || !dec.ExpectSP() {
|
|
return dec.Err()
|
|
}
|
|
|
|
hasFlagList, err := dec.List(func() error {
|
|
flag, err := internal.ExpectFlag(dec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options.Flags = append(options.Flags, flag)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if hasFlagList && !dec.ExpectSP() {
|
|
return dec.Err()
|
|
}
|
|
|
|
t, err := internal.DecodeDateTime(dec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !t.IsZero() && !dec.ExpectSP() {
|
|
return dec.Err()
|
|
}
|
|
options.Time = t
|
|
|
|
var dataExt string
|
|
if !dec.Special('~') && dec.Atom(&dataExt) { // ignore literal8 prefix if any for BINARY
|
|
switch strings.ToUpper(dataExt) {
|
|
case "UTF8":
|
|
// '~' is the literal8 prefix
|
|
if !dec.ExpectSP() || !dec.ExpectSpecial('(') || !dec.ExpectSpecial('~') {
|
|
return dec.Err()
|
|
}
|
|
default:
|
|
return newClientBugError("Unknown APPEND data extension")
|
|
}
|
|
}
|
|
|
|
lit, nonSync, err := dec.ExpectLiteralReader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if lit.Size() > appendLimit {
|
|
return &imap.Error{
|
|
Type: imap.StatusResponseTypeNo,
|
|
Code: imap.ResponseCodeTooBig,
|
|
Text: fmt.Sprintf("Literals are limited to %v bytes for this command", appendLimit),
|
|
}
|
|
}
|
|
if err := c.acceptLiteral(lit.Size(), nonSync); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.setReadTimeout(literalReadTimeout)
|
|
defer c.setReadTimeout(cmdReadTimeout)
|
|
|
|
if err := c.checkState(imap.ConnStateAuthenticated); err != nil {
|
|
io.Copy(io.Discard, lit)
|
|
dec.CRLF()
|
|
return err
|
|
}
|
|
|
|
data, appendErr := c.session.Append(mailbox, lit, &options)
|
|
if _, discardErr := io.Copy(io.Discard, lit); discardErr != nil {
|
|
return err
|
|
}
|
|
if dataExt != "" && !dec.ExpectSpecial(')') {
|
|
return dec.Err()
|
|
}
|
|
if !dec.ExpectCRLF() {
|
|
return err
|
|
}
|
|
if appendErr != nil {
|
|
return appendErr
|
|
}
|
|
if err := c.poll("APPEND"); err != nil {
|
|
return err
|
|
}
|
|
return c.writeAppendOK(tag, data)
|
|
}
|
|
|
|
func (c *Conn) writeAppendOK(tag string, data *imap.AppendData) error {
|
|
enc := newResponseEncoder(c)
|
|
defer enc.end()
|
|
|
|
enc.Atom(tag).SP().Atom("OK").SP()
|
|
if data != nil {
|
|
enc.Special('[')
|
|
enc.Atom("APPENDUID").SP().Number(data.UIDValidity).SP().UID(data.UID)
|
|
enc.Special(']').SP()
|
|
}
|
|
enc.Text("APPEND completed")
|
|
return enc.CRLF()
|
|
}
|