Forked the emersion/go-imap v1 project.
This commit is contained in:
120
imapserver/append.go
Normal file
120
imapserver/append.go
Normal file
@@ -0,0 +1,120 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user