Update vendor/
This commit is contained in:
3
vendor/github.com/emersion/go-imap/.travis.yml
generated
vendored
3
vendor/github.com/emersion/go-imap/.travis.yml
generated
vendored
@@ -1,5 +1,6 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
script: bash <(curl -sL https://gist.github.com/emersion/49d4dda535497002639626bd9e16480c/raw/codecov-go.sh)
|
||||
after_script: bash <(curl -s https://codecov.io/bash)
|
||||
|
3
vendor/github.com/emersion/go-imap/README.md
generated
vendored
3
vendor/github.com/emersion/go-imap/README.md
generated
vendored
@@ -6,7 +6,6 @@
|
||||
[](https://goreportcard.com/report/github.com/emersion/go-imap)
|
||||
[](https://github.com/emersion/stability-badges#unstable)
|
||||
[](https://gitter.im/goimap/Lobby)
|
||||
|
||||
An [IMAP4rev1](https://tools.ietf.org/html/rfc3501) library written in Go. It
|
||||
can be used to build a client and/or a server.
|
||||
@@ -96,7 +95,7 @@ func main() {
|
||||
messages := make(chan *imap.Message, 10)
|
||||
done = make(chan error, 1)
|
||||
go func() {
|
||||
done <- c.Fetch(seqset, []string{imap.EnvelopeMsgAttr}, messages)
|
||||
done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)
|
||||
}()
|
||||
|
||||
log.Println("Last 4 messages:")
|
||||
|
2
vendor/github.com/emersion/go-imap/backend/backendutil/backendutil.go
generated
vendored
Normal file
2
vendor/github.com/emersion/go-imap/backend/backendutil/backendutil.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package backendutil provides utility functions to implement IMAP backends.
|
||||
package backendutil
|
68
vendor/github.com/emersion/go-imap/backend/backendutil/body.go
generated
vendored
Normal file
68
vendor/github.com/emersion/go-imap/backend/backendutil/body.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
var errNoSuchPart = errors.New("backendutil: no such message body part")
|
||||
|
||||
// FetchBodySection extracts a body section from a message.
|
||||
func FetchBodySection(e *message.Entity, section *imap.BodySectionName) (imap.Literal, error) {
|
||||
// First, find the requested part using the provided path
|
||||
for i := len(section.Path) - 1; i >= 0; i-- {
|
||||
n := section.Path[i]
|
||||
|
||||
mr := e.MultipartReader()
|
||||
if mr == nil {
|
||||
return nil, errNoSuchPart
|
||||
}
|
||||
|
||||
for j := 1; j <= n; j++ {
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
return nil, errNoSuchPart
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if j == n {
|
||||
e = p
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then, write the requested data to a buffer
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
// Write the header
|
||||
mw, err := message.CreateWriter(b, e.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer mw.Close()
|
||||
|
||||
// If the header hasn't been requested, discard it
|
||||
if section.Specifier == imap.TextSpecifier {
|
||||
b.Reset()
|
||||
}
|
||||
|
||||
// Write the body, if requested
|
||||
switch section.Specifier {
|
||||
case imap.EntireSpecifier, imap.TextSpecifier:
|
||||
if _, err := io.Copy(mw, e.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var l imap.Literal = b
|
||||
if section.Partial != nil {
|
||||
l = bytes.NewReader(section.ExtractPartial(b.Bytes()))
|
||||
}
|
||||
return l, nil
|
||||
}
|
60
vendor/github.com/emersion/go-imap/backend/backendutil/bodystructure.go
generated
vendored
Normal file
60
vendor/github.com/emersion/go-imap/backend/backendutil/bodystructure.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// FetchBodyStructure computes a message's body structure from its content.
|
||||
func FetchBodyStructure(e *message.Entity, extended bool) (*imap.BodyStructure, error) {
|
||||
bs := new(imap.BodyStructure)
|
||||
|
||||
mediaType, mediaParams, _ := e.Header.ContentType()
|
||||
typeParts := strings.SplitN(mediaType, "/", 2)
|
||||
bs.MIMEType = typeParts[0]
|
||||
if len(typeParts) == 2 {
|
||||
bs.MIMESubType = typeParts[1]
|
||||
}
|
||||
bs.Params = mediaParams
|
||||
|
||||
bs.Id = e.Header.Get("Content-Id")
|
||||
bs.Description = e.Header.Get("Content-Description")
|
||||
bs.Encoding = e.Header.Get("Content-Encoding")
|
||||
// TODO: bs.Size
|
||||
|
||||
if mr := e.MultipartReader(); mr != nil {
|
||||
var parts []*imap.BodyStructure
|
||||
for {
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbs, err := FetchBodyStructure(p, extended)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts = append(parts, pbs)
|
||||
}
|
||||
bs.Parts = parts
|
||||
}
|
||||
|
||||
// TODO: bs.Envelope, bs.BodyStructure
|
||||
// TODO: bs.Lines
|
||||
|
||||
if extended {
|
||||
bs.Extended = true
|
||||
|
||||
bs.Disposition, bs.DispositionParams, _ = e.Header.ContentDisposition()
|
||||
|
||||
// TODO: bs.Language, bs.Location
|
||||
// TODO: bs.MD5
|
||||
}
|
||||
|
||||
return bs, nil
|
||||
}
|
50
vendor/github.com/emersion/go-imap/backend/backendutil/envelope.go
generated
vendored
Normal file
50
vendor/github.com/emersion/go-imap/backend/backendutil/envelope.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func headerAddressList(h mail.Header, key string) ([]*imap.Address, error) {
|
||||
addrs, err := h.AddressList(key)
|
||||
|
||||
list := make([]*imap.Address, len(addrs))
|
||||
for i, a := range addrs {
|
||||
parts := strings.SplitN(a.Address, "@", 2)
|
||||
mailbox := parts[0]
|
||||
var hostname string
|
||||
if len(parts) == 2 {
|
||||
hostname = parts[1]
|
||||
}
|
||||
|
||||
list[i] = &imap.Address{
|
||||
PersonalName: a.Name,
|
||||
MailboxName: mailbox,
|
||||
HostName: hostname,
|
||||
}
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
// FetchEnvelope returns a message's envelope from its header.
|
||||
func FetchEnvelope(h message.Header) (*imap.Envelope, error) {
|
||||
mh := mail.Header{h}
|
||||
|
||||
env := new(imap.Envelope)
|
||||
env.Date, _ = mh.Date()
|
||||
env.Subject, _ = mh.Subject()
|
||||
env.From, _ = headerAddressList(mh, "From")
|
||||
env.Sender, _ = headerAddressList(mh, "Sender")
|
||||
env.ReplyTo, _ = headerAddressList(mh, "Reply-To")
|
||||
env.To, _ = headerAddressList(mh, "To")
|
||||
env.Cc, _ = headerAddressList(mh, "Cc")
|
||||
env.Bcc, _ = headerAddressList(mh, "Bcc")
|
||||
env.InReplyTo = mh.Get("In-Reply-To")
|
||||
env.MessageId = mh.Get("Message-Id")
|
||||
|
||||
return env, nil
|
||||
}
|
40
vendor/github.com/emersion/go-imap/backend/backendutil/flags.go
generated
vendored
Normal file
40
vendor/github.com/emersion/go-imap/backend/backendutil/flags.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// UpdateFlags executes a flag operation on the flag set current.
|
||||
func UpdateFlags(current []string, op imap.FlagsOp, flags []string) []string {
|
||||
switch op {
|
||||
case imap.SetFlags:
|
||||
// TODO: keep \Recent if it is present
|
||||
return flags
|
||||
case imap.AddFlags:
|
||||
// Check for duplicates
|
||||
for _, flag := range current {
|
||||
for i, addFlag := range flags {
|
||||
if addFlag == flag {
|
||||
flags = append(flags[:i], flags[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return append(current, flags...)
|
||||
case imap.RemoveFlags:
|
||||
// Iterate through flags from the last one to the first one, to be able to
|
||||
// delete some of them.
|
||||
for i := len(current) - 1; i >= 0; i-- {
|
||||
flag := current[i]
|
||||
|
||||
for _, removeFlag := range flags {
|
||||
if removeFlag == flag {
|
||||
current = append(current[:i], current[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
return current
|
||||
}
|
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func matchString(s, substr string) bool {
|
||||
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||||
}
|
||||
|
||||
func bufferBody(e *message.Entity) (*bytes.Buffer, error) {
|
||||
b := new(bytes.Buffer)
|
||||
if _, err := io.Copy(b, e.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Body = b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func matchBody(e *message.Entity, substr string) (bool, error) {
|
||||
if s, ok := e.Body.(fmt.Stringer); ok {
|
||||
return matchString(s.String(), substr), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return matchString(b.String(), substr), nil
|
||||
}
|
||||
|
||||
type lengther interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
func bodyLen(e *message.Entity) (int, error) {
|
||||
if l, ok := e.Body.(lengther); ok {
|
||||
return l.Len(), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.Len(), nil
|
||||
}
|
||||
|
||||
// Match returns true if a message matches the provided criteria. Sequence
|
||||
// number, UID, flag and internal date contrainsts are not checked.
|
||||
func Match(e *message.Entity, c *imap.SearchCriteria) (bool, error) {
|
||||
// TODO: support encoded header fields for Bcc, Cc, From, To
|
||||
// TODO: add header size for Larger and Smaller
|
||||
|
||||
h := mail.Header{e.Header}
|
||||
|
||||
if !c.SentBefore.IsZero() || !c.SentSince.IsZero() {
|
||||
t, err := h.Date()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
t = t.Round(24 * time.Hour)
|
||||
|
||||
if !c.SentBefore.IsZero() && !t.Before(c.SentBefore) {
|
||||
return false, nil
|
||||
}
|
||||
if !c.SentSince.IsZero() && !t.After(c.SentSince) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for key, wantValues := range c.Header {
|
||||
values, ok := e.Header[key]
|
||||
for _, wantValue := range wantValues {
|
||||
if wantValue == "" && !ok {
|
||||
return false, nil
|
||||
}
|
||||
if wantValue != "" {
|
||||
ok := false
|
||||
for _, v := range values {
|
||||
if matchString(v, wantValue) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, body := range c.Body {
|
||||
if ok, err := matchBody(e, body); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, text := range c.Text {
|
||||
// TODO: also match header fields
|
||||
if ok, err := matchBody(e, text); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Larger > 0 || c.Smaller > 0 {
|
||||
n, err := bodyLen(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if c.Larger > 0 && uint32(n) < c.Larger {
|
||||
return false, nil
|
||||
}
|
||||
if c.Smaller > 0 && uint32(n) > c.Smaller {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
ok, err := Match(e, not)
|
||||
if err != nil || ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
ok1, err := Match(e, or[0])
|
||||
if err != nil {
|
||||
return ok1, err
|
||||
}
|
||||
|
||||
ok2, err := Match(e, or[1])
|
||||
if err != nil || (!ok1 && !ok2) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func matchFlags(flags map[string]bool, c *imap.SearchCriteria) bool {
|
||||
for _, f := range c.WithFlags {
|
||||
if !flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, f := range c.WithoutFlags {
|
||||
if flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if matchFlags(flags, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !matchFlags(flags, or[0]) && !matchFlags(flags, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchFlags returns true if a flag list matches the provided criteria.
|
||||
func MatchFlags(flags []string, c *imap.SearchCriteria) bool {
|
||||
flagsMap := make(map[string]bool)
|
||||
for _, f := range flags {
|
||||
flagsMap[f] = true
|
||||
}
|
||||
|
||||
return matchFlags(flagsMap, c)
|
||||
}
|
||||
|
||||
// MatchSeqNumAndUid returns true if a sequence number and a UID matches the
|
||||
// provided criteria.
|
||||
func MatchSeqNumAndUid(seqNum uint32, uid uint32, c *imap.SearchCriteria) bool {
|
||||
if c.SeqNum != nil && !c.SeqNum.Contains(seqNum) {
|
||||
return false
|
||||
}
|
||||
if c.Uid != nil && !c.Uid.Contains(uid) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchSeqNumAndUid(seqNum, uid, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchSeqNumAndUid(seqNum, uid, or[0]) && !MatchSeqNumAndUid(seqNum, uid, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchDate returns true if a date matches the provided criteria.
|
||||
func MatchDate(date time.Time, c *imap.SearchCriteria) bool {
|
||||
date = date.Round(24 * time.Hour)
|
||||
if !c.Since.IsZero() && !date.After(c.Since) {
|
||||
return false
|
||||
}
|
||||
if !c.Before.IsZero() && !date.Before(c.Before) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchDate(date, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchDate(date, or[0]) && !MatchDate(date, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
12
vendor/github.com/emersion/go-imap/backend/mailbox.go
generated
vendored
12
vendor/github.com/emersion/go-imap/backend/mailbox.go
generated
vendored
@@ -15,11 +15,11 @@ type Mailbox interface {
|
||||
// Info returns this mailbox info.
|
||||
Info() (*imap.MailboxInfo, error)
|
||||
|
||||
// Status returns this mailbox status. The fields Name, Flags and
|
||||
// PermanentFlags in the returned MailboxStatus must be always populated. This
|
||||
// function does not affect the state of any messages in the mailbox. See RFC
|
||||
// 3501 section 6.3.10 for a list of items that can be requested.
|
||||
Status(items []string) (*imap.MailboxStatus, error)
|
||||
// Status returns this mailbox status. The fields Name, Flags, PermanentFlags
|
||||
// and UnseenSeqNum in the returned MailboxStatus must be always populated.
|
||||
// This function does not affect the state of any messages in the mailbox. See
|
||||
// RFC 3501 section 6.3.10 for a list of items that can be requested.
|
||||
Status(items []imap.StatusItem) (*imap.MailboxStatus, error)
|
||||
|
||||
// SetSubscribed adds or removes the mailbox to the server's set of "active"
|
||||
// or "subscribed" mailboxes.
|
||||
@@ -38,7 +38,7 @@ type Mailbox interface {
|
||||
// 3501 section 6.4.5 for a list of items that can be requested.
|
||||
//
|
||||
// Messages must be sent to ch. When the function returns, ch must be closed.
|
||||
ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch chan<- *imap.Message) error
|
||||
ListMessages(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error
|
||||
|
||||
// SearchMessages searches messages. The returned list must contain UIDs if
|
||||
// uid is set to true, or sequence numbers otherwise.
|
||||
|
95
vendor/github.com/emersion/go-imap/backend/updates.go
generated
vendored
95
vendor/github.com/emersion/go-imap/backend/updates.go
generated
vendored
@@ -6,35 +6,47 @@ import (
|
||||
|
||||
// Update contains user and mailbox information about an unilateral backend
|
||||
// update.
|
||||
type Update struct {
|
||||
type Update interface {
|
||||
// The user targeted by this update. If empty, all connected users will
|
||||
// be notified.
|
||||
Username string
|
||||
Username() string
|
||||
// The mailbox targeted by this update. If empty, the update targets all
|
||||
// mailboxes.
|
||||
Mailbox string
|
||||
Mailbox() string
|
||||
// Done returns a channel that is closed when the update has been broadcast to
|
||||
// all clients.
|
||||
Done() chan struct{}
|
||||
}
|
||||
|
||||
// A channel that will be closed once the update has been processed.
|
||||
// NewUpdate creates a new update.
|
||||
func NewUpdate(username, mailbox string) Update {
|
||||
return &update{
|
||||
username: username,
|
||||
mailbox: mailbox,
|
||||
}
|
||||
}
|
||||
|
||||
type update struct {
|
||||
username string
|
||||
mailbox string
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Done returns a channel that is closed when the update has been broadcast to
|
||||
// all clients.
|
||||
func (u *Update) Done() <-chan struct{} {
|
||||
func (u *update) Username() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u *update) Mailbox() string {
|
||||
return u.mailbox
|
||||
}
|
||||
|
||||
func (u *update) Done() chan struct{} {
|
||||
if u.done == nil {
|
||||
u.done = make(chan struct{})
|
||||
}
|
||||
return u.done
|
||||
}
|
||||
|
||||
// DoneUpdate marks an update as done.
|
||||
// TODO: remove this function
|
||||
func DoneUpdate(u *Update) {
|
||||
if u.done != nil {
|
||||
close(u.done)
|
||||
}
|
||||
}
|
||||
|
||||
// StatusUpdate is a status update. See RFC 3501 section 7.1 for a list of
|
||||
// status responses.
|
||||
type StatusUpdate struct {
|
||||
@@ -60,50 +72,21 @@ type ExpungeUpdate struct {
|
||||
SeqNum uint32
|
||||
}
|
||||
|
||||
// Updater is a Backend that implements Updater is able to send unilateral
|
||||
// backend updates. Backends not implementing this interface don't correctly
|
||||
// send unilateral updates, for instance if a user logs in from two connections
|
||||
// and deletes a message from one of them, the over is not aware that such a
|
||||
// mesage has been deleted. More importantly, backends implementing Updater can
|
||||
// notify the user for external updates such as new message notifications.
|
||||
type Updater interface {
|
||||
// BackendUpdater is a Backend that implements Updater is able to send
|
||||
// unilateral backend updates. Backends not implementing this interface don't
|
||||
// correctly send unilateral updates, for instance if a user logs in from two
|
||||
// connections and deletes a message from one of them, the over is not aware
|
||||
// that such a mesage has been deleted. More importantly, backends implementing
|
||||
// Updater can notify the user for external updates such as new message
|
||||
// notifications.
|
||||
type BackendUpdater interface {
|
||||
// Updates returns a set of channels where updates are sent to.
|
||||
Updates() <-chan interface{}
|
||||
Updates() <-chan Update
|
||||
}
|
||||
|
||||
// UpdaterMailbox is a Mailbox that implements UpdaterMailbox is able to poll
|
||||
// updates for new messages or message status updates during a period of
|
||||
// inactivity.
|
||||
type UpdaterMailbox interface {
|
||||
// MailboxPoller is a Mailbox that is able to poll updates for new messages or
|
||||
// message status updates during a period of inactivity.
|
||||
type MailboxPoller interface {
|
||||
// Poll requests mailbox updates.
|
||||
Poll() error
|
||||
}
|
||||
|
||||
// WaitUpdates returns a channel that's closed when all provided updates have
|
||||
// been dispatched to all clients. It panics if one of the provided value is
|
||||
// not an update.
|
||||
func WaitUpdates(updates ...interface{}) <-chan struct{} {
|
||||
done := make(chan struct{})
|
||||
|
||||
var chs []<-chan struct{}
|
||||
for _, u := range updates {
|
||||
uu, ok := u.(interface {
|
||||
Done() <-chan struct{}
|
||||
})
|
||||
if !ok {
|
||||
panic("imap: cannot wait for update: provided value is not a valid update")
|
||||
}
|
||||
|
||||
chs = append(chs, uu.Done())
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Wait for all updates to be sent
|
||||
for _, ch := range chs {
|
||||
<-ch
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
return done
|
||||
}
|
||||
|
32
vendor/github.com/emersion/go-imap/command.go
generated
vendored
32
vendor/github.com/emersion/go-imap/command.go
generated
vendored
@@ -5,38 +5,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IMAP4rev1 commands.
|
||||
const (
|
||||
Capability string = "CAPABILITY"
|
||||
Noop = "NOOP"
|
||||
Logout = "LOGOUT"
|
||||
StartTLS = "STARTTLS"
|
||||
|
||||
Authenticate = "AUTHENTICATE"
|
||||
Login = "LOGIN"
|
||||
|
||||
Select = "SELECT"
|
||||
Examine = "EXAMINE"
|
||||
Create = "CREATE"
|
||||
Delete = "DELETE"
|
||||
Rename = "RENAME"
|
||||
Subscribe = "SUBSCRIBE"
|
||||
Unsubscribe = "UNSUBSCRIBE"
|
||||
List = "LIST"
|
||||
Lsub = "LSUB"
|
||||
Status = "STATUS"
|
||||
Append = "APPEND"
|
||||
|
||||
Check = "CHECK"
|
||||
Close = "CLOSE"
|
||||
Expunge = "EXPUNGE"
|
||||
Search = "SEARCH"
|
||||
Fetch = "FETCH"
|
||||
Store = "STORE"
|
||||
Copy = "COPY"
|
||||
Uid = "UID"
|
||||
)
|
||||
|
||||
// A value that can be converted to a command.
|
||||
type Commander interface {
|
||||
Command() *Command
|
||||
|
18
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
18
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
@@ -19,13 +19,13 @@ type Append struct {
|
||||
func (cmd *Append) Command() *imap.Command {
|
||||
var args []interface{}
|
||||
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
args = append(args, mailbox)
|
||||
|
||||
if cmd.Flags != nil {
|
||||
flags := make([]interface{}, len(cmd.Flags))
|
||||
for i, flag := range cmd.Flags {
|
||||
flags[i] = flag
|
||||
flags[i] = imap.Atom(flag)
|
||||
}
|
||||
args = append(args, flags)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func (cmd *Append) Command() *imap.Command {
|
||||
args = append(args, cmd.Message)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Append,
|
||||
Name: "APPEND",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,9 @@ func (cmd *Append) Parse(fields []interface{}) (err error) {
|
||||
}
|
||||
|
||||
// Parse mailbox name
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
@@ -81,11 +81,9 @@ func (cmd *Append) Parse(fields []interface{}) (err error) {
|
||||
|
||||
// Parse date
|
||||
if len(fields) > 0 {
|
||||
date, ok := fields[0].(string)
|
||||
if !ok {
|
||||
if date, ok := fields[0].(string); !ok {
|
||||
return errors.New("Date must be a string")
|
||||
}
|
||||
if cmd.Date, err = time.Parse(imap.DateTimeLayout, date); err != nil {
|
||||
} else if cmd.Date, err = time.Parse(imap.DateTimeLayout, date); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
4
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
4
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
@@ -27,7 +27,7 @@ type Authenticate struct {
|
||||
|
||||
func (cmd *Authenticate) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Authenticate,
|
||||
Name: "AUTHENTICATE",
|
||||
Arguments: []interface{}{cmd.Mechanism},
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (cmd *Authenticate) Handle(mechanisms map[string]sasl.Server, conn Authenti
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(challenge)
|
||||
cont := &imap.ContinuationResp{Info: encoded}
|
||||
cont := &imap.ContinuationReq{Info: encoded}
|
||||
if err := conn.WriteResp(cont); err != nil {
|
||||
return err
|
||||
}
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
@@ -9,7 +9,7 @@ type Capability struct{}
|
||||
|
||||
func (c *Capability) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Capability,
|
||||
Name: "CAPABILITY",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
@@ -9,7 +9,7 @@ type Check struct{}
|
||||
|
||||
func (cmd *Check) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Check,
|
||||
Name: "CHECK",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
@@ -9,7 +9,7 @@ type Close struct{}
|
||||
|
||||
func (cmd *Close) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Close,
|
||||
Name: "CLOSE",
|
||||
}
|
||||
}
|
||||
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
@@ -14,10 +14,10 @@ type Copy struct {
|
||||
}
|
||||
|
||||
func (cmd *Copy) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Copy,
|
||||
Name: "COPY",
|
||||
Arguments: []interface{}{cmd.SeqSet, mailbox},
|
||||
}
|
||||
}
|
||||
@@ -29,15 +29,15 @@ func (cmd *Copy) Parse(fields []interface{}) error {
|
||||
|
||||
if seqSet, ok := fields[0].(string); !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
} else if seqSet, err := imap.NewSeqSet(seqSet); err != nil {
|
||||
} else if seqSet, err := imap.ParseSeqSet(seqSet); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.SeqSet = seqSet
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
10
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
10
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
@@ -13,10 +13,10 @@ type Create struct {
|
||||
}
|
||||
|
||||
func (cmd *Create) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Create,
|
||||
Name: "CREATE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ func (cmd *Create) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
10
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
10
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
@@ -13,10 +13,10 @@ type Delete struct {
|
||||
}
|
||||
|
||||
func (cmd *Delete) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Delete,
|
||||
Name: "DELETE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ func (cmd *Delete) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
@@ -8,7 +8,7 @@ import (
|
||||
type Expunge struct{}
|
||||
|
||||
func (cmd *Expunge) Command() *imap.Command {
|
||||
return &imap.Command{Name: imap.Expunge}
|
||||
return &imap.Command{Name: "EXPUNGE"}
|
||||
}
|
||||
|
||||
func (cmd *Expunge) Parse(fields []interface{}) error {
|
||||
|
37
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
37
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
@@ -10,21 +10,21 @@ import (
|
||||
// Fetch is a FETCH command, as defined in RFC 3501 section 6.4.5.
|
||||
type Fetch struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Items []string
|
||||
Items []imap.FetchItem
|
||||
}
|
||||
|
||||
func (cmd *Fetch) Command() *imap.Command {
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, item := range cmd.Items {
|
||||
if section, err := imap.NewBodySectionName(item); err == nil {
|
||||
if section, err := imap.ParseBodySectionName(item); err == nil {
|
||||
items[i] = section
|
||||
} else {
|
||||
items[i] = item
|
||||
items[i] = string(item)
|
||||
}
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Fetch,
|
||||
Name: "FETCH",
|
||||
Arguments: []interface{}{cmd.SeqSet, items},
|
||||
}
|
||||
}
|
||||
@@ -34,33 +34,22 @@ func (cmd *Fetch) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
seqset, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Sequence set must be a string")
|
||||
}
|
||||
|
||||
var err error
|
||||
if cmd.SeqSet, err = imap.NewSeqSet(seqset); err != nil {
|
||||
if seqset, ok := fields[0].(string); !ok {
|
||||
return errors.New("Sequence set must be an atom")
|
||||
} else if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch items := fields[1].(type) {
|
||||
case string: // A macro or a single item
|
||||
switch strings.ToUpper(items) {
|
||||
case "ALL":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE"}
|
||||
case "FAST":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE"}
|
||||
case "FULL":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE", "BODY"}
|
||||
default:
|
||||
cmd.Items = []string{strings.ToUpper(items)}
|
||||
}
|
||||
cmd.Items = imap.FetchItem(strings.ToUpper(items)).Expand()
|
||||
case []interface{}: // A list of items
|
||||
cmd.Items = make([]string, len(items))
|
||||
for i, v := range items {
|
||||
item, _ := v.(string)
|
||||
cmd.Items[i] = strings.ToUpper(item)
|
||||
cmd.Items = make([]imap.FetchItem, 0, len(items))
|
||||
for _, v := range items {
|
||||
itemStr, _ := v.(string)
|
||||
item := imap.FetchItem(strings.ToUpper(itemStr))
|
||||
cmd.Items = append(cmd.Items, item.Expand()...)
|
||||
}
|
||||
default:
|
||||
return errors.New("Items must be either a string or a list")
|
||||
|
23
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
23
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
@@ -17,13 +17,14 @@ type List struct {
|
||||
}
|
||||
|
||||
func (cmd *List) Command() *imap.Command {
|
||||
name := imap.List
|
||||
name := "LIST"
|
||||
if cmd.Subscribed {
|
||||
name = imap.Lsub
|
||||
name = "LSUB"
|
||||
}
|
||||
|
||||
ref, _ := utf7.Encoder.String(cmd.Reference)
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
ref, _ := enc.String(cmd.Reference)
|
||||
mailbox, _ := enc.String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
@@ -36,18 +37,20 @@ func (cmd *List) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Reference name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
dec := utf7.Encoding.NewDecoder()
|
||||
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := dec.String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
// TODO: canonical mailbox path
|
||||
cmd.Reference = imap.CanonicalMailboxName(mailbox)
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := dec.String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
@@ -14,7 +14,7 @@ type Login struct {
|
||||
|
||||
func (cmd *Login) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Login,
|
||||
Name: "LOGIN",
|
||||
Arguments: []interface{}{cmd.Username, cmd.Password},
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@ func (cmd *Login) Parse(fields []interface{}) error {
|
||||
return errors.New("Not enough arguments")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if cmd.Username, ok = fields[0].(string); !ok {
|
||||
return errors.New("Username is not a string")
|
||||
var err error
|
||||
if cmd.Username, err = imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Password, ok = fields[1].(string); !ok {
|
||||
return errors.New("Password is not a string")
|
||||
if cmd.Password, err = imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
@@ -9,7 +9,7 @@ type Logout struct{}
|
||||
|
||||
func (c *Logout) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Logout,
|
||||
Name: "LOGOUT",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
@@ -9,7 +9,7 @@ type Noop struct{}
|
||||
|
||||
func (c *Noop) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Noop,
|
||||
Name: "NOOP",
|
||||
}
|
||||
}
|
||||
|
||||
|
21
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
21
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
@@ -14,11 +14,12 @@ type Rename struct {
|
||||
}
|
||||
|
||||
func (cmd *Rename) Command() *imap.Command {
|
||||
existingName, _ := utf7.Encoder.String(cmd.Existing)
|
||||
newName, _ := utf7.Encoder.String(cmd.New)
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
existingName, _ := enc.String(cmd.Existing)
|
||||
newName, _ := enc.String(cmd.New)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Rename,
|
||||
Name: "RENAME",
|
||||
Arguments: []interface{}{existingName, newName},
|
||||
}
|
||||
}
|
||||
@@ -28,17 +29,19 @@ func (cmd *Rename) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if existingName, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if existingName, err := utf7.Decoder.String(existingName); err != nil {
|
||||
dec := utf7.Encoding.NewDecoder()
|
||||
|
||||
if existingName, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if existingName, err := dec.String(existingName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Existing = imap.CanonicalMailboxName(existingName)
|
||||
}
|
||||
|
||||
if newName, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if newName, err := utf7.Decoder.String(newName); err != nil {
|
||||
if newName, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if newName, err := dec.String(newName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.New = imap.CanonicalMailboxName(newName)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
@@ -22,7 +22,7 @@ func (cmd *Search) Command() *imap.Command {
|
||||
args = append(args, cmd.Criteria.Format()...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Search,
|
||||
Name: "SEARCH",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
@@ -15,12 +15,12 @@ type Select struct {
|
||||
}
|
||||
|
||||
func (cmd *Select) Command() *imap.Command {
|
||||
name := imap.Select
|
||||
name := "SELECT"
|
||||
if cmd.ReadOnly {
|
||||
name = imap.Examine
|
||||
name = "EXAMINE"
|
||||
}
|
||||
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
@@ -33,9 +33,9 @@ func (cmd *Select) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
@@ -9,7 +9,7 @@ type StartTLS struct{}
|
||||
|
||||
func (cmd *StartTLS) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.StartTLS,
|
||||
Name: "STARTTLS",
|
||||
}
|
||||
}
|
||||
|
||||
|
33
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
33
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
@@ -11,19 +11,19 @@ import (
|
||||
// Status is a STATUS command, as defined in RFC 3501 section 6.3.10.
|
||||
type Status struct {
|
||||
Mailbox string
|
||||
Items []string
|
||||
Items []imap.StatusItem
|
||||
}
|
||||
|
||||
func (cmd *Status) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, f := range cmd.Items {
|
||||
items[i] = f
|
||||
for i, item := range cmd.Items {
|
||||
items[i] = string(item)
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Status,
|
||||
Name: "STATUS",
|
||||
Arguments: []interface{}{mailbox, items},
|
||||
}
|
||||
}
|
||||
@@ -33,21 +33,24 @@ func (cmd *Status) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
}
|
||||
|
||||
if items, ok := fields[1].([]interface{}); !ok {
|
||||
return errors.New("Items must be a list")
|
||||
} else {
|
||||
cmd.Items = make([]string, len(items))
|
||||
for i, v := range items {
|
||||
item, _ := v.(string)
|
||||
cmd.Items[i] = strings.ToUpper(item)
|
||||
items, ok := fields[1].([]interface{})
|
||||
if !ok {
|
||||
return errors.New("STATUS command parameter is not a list")
|
||||
}
|
||||
cmd.Items = make([]imap.StatusItem, len(items))
|
||||
for i, f := range items {
|
||||
if s, ok := f.(string); !ok {
|
||||
return errors.New("Got a non-string field in a STATUS command parameter")
|
||||
} else {
|
||||
cmd.Items[i] = imap.StatusItem(strings.ToUpper(s))
|
||||
}
|
||||
}
|
||||
|
||||
|
20
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
20
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
@@ -10,18 +10,18 @@ import (
|
||||
// Store is a STORE command, as defined in RFC 3501 section 6.4.6.
|
||||
type Store struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Item string
|
||||
Item imap.StoreItem
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (cmd *Store) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Store,
|
||||
Arguments: []interface{}{cmd.SeqSet, cmd.Item, cmd.Value},
|
||||
Name: "STORE",
|
||||
Arguments: []interface{}{cmd.SeqSet, string(cmd.Item), cmd.Value},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Store) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Store) Parse(fields []interface{}) error {
|
||||
if len(fields) < 3 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
@@ -30,16 +30,18 @@ func (cmd *Store) Parse(fields []interface{}) (err error) {
|
||||
if !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
}
|
||||
if cmd.SeqSet, err = imap.NewSeqSet(seqset); err != nil {
|
||||
var err error
|
||||
if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.Item, ok = fields[1].(string); !ok {
|
||||
if item, ok := fields[1].(string); !ok {
|
||||
return errors.New("Item name must be a string")
|
||||
} else {
|
||||
cmd.Item = imap.StoreItem(strings.ToUpper(item))
|
||||
}
|
||||
cmd.Item = strings.ToUpper(cmd.Item)
|
||||
|
||||
// TODO: could be fields[2:] according to RFC 3501 page 91 "store-att-flags"
|
||||
cmd.Value = fields[2]
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
38
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
38
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
@@ -13,29 +13,25 @@ type Subscribe struct {
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Subscribe,
|
||||
Name: "SUBSCRIBE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Subscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enogh arguments")
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
mailbox, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
}
|
||||
|
||||
if cmd.Mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if cmd.Mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// An UNSUBSCRIBE command.
|
||||
@@ -45,27 +41,23 @@ type Unsubscribe struct {
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Unsubscribe,
|
||||
Name: "UNSUBSCRIBE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Unsubscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enogh arguments")
|
||||
}
|
||||
|
||||
mailbox, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
}
|
||||
|
||||
if cmd.Mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if cmd.Mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
@@ -20,7 +20,7 @@ func (cmd *Uid) Command() *imap.Command {
|
||||
args = append(args, inner.Arguments...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Uid,
|
||||
Name: "UID",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
5
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
5
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
@@ -154,10 +154,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
|
||||
// Flush writes any buffered data to the underlying connection.
|
||||
func (c *Conn) Flush() error {
|
||||
if err := c.Writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return c.Writer.Flush()
|
||||
}
|
||||
|
||||
// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
|
||||
|
121
vendor/github.com/emersion/go-imap/handle.go
generated
vendored
121
vendor/github.com/emersion/go-imap/handle.go
generated
vendored
@@ -1,121 +0,0 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A response that can be either accepted or rejected by a handler.
|
||||
type RespHandle struct {
|
||||
Resp interface{}
|
||||
Accepts chan bool
|
||||
}
|
||||
|
||||
// Accept this response. This means that the handler will process it.
|
||||
func (h *RespHandle) Accept() {
|
||||
h.Accepts <- true
|
||||
}
|
||||
|
||||
// Reject this response. The handler cannot process it.
|
||||
func (h *RespHandle) Reject() {
|
||||
h.Accepts <- false
|
||||
}
|
||||
|
||||
// Accept this response if it has the specified name. If not, reject it.
|
||||
func (h *RespHandle) AcceptNamedResp(name string) (fields []interface{}, accepted bool) {
|
||||
res, ok := h.Resp.(*Resp)
|
||||
if !ok || len(res.Fields) == 0 {
|
||||
h.Reject()
|
||||
return
|
||||
}
|
||||
|
||||
n, ok := res.Fields[0].(string)
|
||||
if !ok || n != name {
|
||||
h.Reject()
|
||||
return
|
||||
}
|
||||
|
||||
h.Accept()
|
||||
|
||||
fields = res.Fields[1:]
|
||||
accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
// Delivers responses to handlers.
|
||||
type RespHandler chan *RespHandle
|
||||
|
||||
// Handles responses from a handler.
|
||||
type RespHandlerFrom interface {
|
||||
HandleFrom(hdlr RespHandler) error
|
||||
}
|
||||
|
||||
// A RespHandlerFrom that forwards responses to multiple RespHandler.
|
||||
type MultiRespHandler struct {
|
||||
handlers []RespHandler
|
||||
locker sync.Locker
|
||||
}
|
||||
|
||||
func NewMultiRespHandler() *MultiRespHandler {
|
||||
return &MultiRespHandler{
|
||||
locker: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) HandleFrom(ch RespHandler) error {
|
||||
for rh := range ch {
|
||||
mh.locker.Lock()
|
||||
|
||||
accepted := false
|
||||
for i := len(mh.handlers) - 1; i >= 0; i-- {
|
||||
hdlr := mh.handlers[i]
|
||||
|
||||
rh := &RespHandle{
|
||||
Resp: rh.Resp,
|
||||
Accepts: make(chan bool),
|
||||
}
|
||||
|
||||
hdlr <- rh
|
||||
if accepted = <-rh.Accepts; accepted {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
mh.locker.Unlock()
|
||||
|
||||
if accepted {
|
||||
rh.Accept()
|
||||
} else {
|
||||
rh.Reject()
|
||||
}
|
||||
}
|
||||
|
||||
mh.locker.Lock()
|
||||
for _, hdlr := range mh.handlers {
|
||||
close(hdlr)
|
||||
}
|
||||
mh.handlers = nil
|
||||
mh.locker.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) Add(hdlr RespHandler) {
|
||||
if hdlr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
mh.locker.Lock()
|
||||
mh.handlers = append(mh.handlers, hdlr)
|
||||
mh.locker.Unlock()
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) Del(hdlr RespHandler) {
|
||||
mh.locker.Lock()
|
||||
for i, h := range mh.handlers {
|
||||
if h == hdlr {
|
||||
close(hdlr)
|
||||
mh.handlers = append(mh.handlers[:i], mh.handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
mh.locker.Unlock()
|
||||
}
|
82
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
82
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
@@ -2,9 +2,60 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A StatusItem is a mailbox status data item that can be retrieved with a
|
||||
// STATUS command. See RFC 3501 section 6.3.10.
|
||||
type StatusItem string
|
||||
|
||||
const (
|
||||
StatusMessages StatusItem = "MESSAGES"
|
||||
StatusRecent = "RECENT"
|
||||
StatusUidNext = "UIDNEXT"
|
||||
StatusUidValidity = "UIDVALIDITY"
|
||||
StatusUnseen = "UNSEEN"
|
||||
)
|
||||
|
||||
// A FetchItem is a message data item that can be fetched.
|
||||
type FetchItem string
|
||||
|
||||
// List of items that can be fetched.
|
||||
const (
|
||||
// Macros
|
||||
FetchAll FetchItem = "ALL"
|
||||
FetchFast = "FAST"
|
||||
FetchFull = "FULL"
|
||||
|
||||
// Items
|
||||
FetchBody = "BODY"
|
||||
FetchBodyStructure = "BODYSTRUCTURE"
|
||||
FetchEnvelope = "ENVELOPE"
|
||||
FetchFlags = "FLAGS"
|
||||
FetchInternalDate = "INTERNALDATE"
|
||||
FetchRFC822 = "RFC822"
|
||||
FetchRFC822Header = "RFC822.HEADER"
|
||||
FetchRFC822Size = "RFC822.SIZE"
|
||||
FetchRFC822Text = "RFC822.TEXT"
|
||||
FetchUid = "UID"
|
||||
)
|
||||
|
||||
// Expand expands the item if it's a macro.
|
||||
func (item FetchItem) Expand() []FetchItem {
|
||||
switch item {
|
||||
case FetchAll:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size, FetchEnvelope}
|
||||
case FetchFast:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size}
|
||||
case FetchFull:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size, FetchEnvelope, FetchBody}
|
||||
default:
|
||||
return []FetchItem{item}
|
||||
}
|
||||
}
|
||||
|
||||
// FlagsOp is an operation that will be applied on message flags.
|
||||
type FlagsOp string
|
||||
|
||||
@@ -17,9 +68,36 @@ const (
|
||||
RemoveFlags = "-FLAGS"
|
||||
)
|
||||
|
||||
// SilentOp can be appended to a FlagsOp to prevent the operation from
|
||||
// silentOp can be appended to a FlagsOp to prevent the operation from
|
||||
// triggering unilateral message updates.
|
||||
const SilentOp = ".SILENT"
|
||||
const silentOp = ".SILENT"
|
||||
|
||||
// A StoreItem is a message data item that can be updated.
|
||||
type StoreItem string
|
||||
|
||||
// FormatFlagsOp returns the StoreItem that executes the flags operation op.
|
||||
func FormatFlagsOp(op FlagsOp, silent bool) StoreItem {
|
||||
s := string(op)
|
||||
if silent {
|
||||
s += silentOp
|
||||
}
|
||||
return StoreItem(s)
|
||||
}
|
||||
|
||||
// ParseFlagsOp parses a flags operation from StoreItem.
|
||||
func ParseFlagsOp(item StoreItem) (op FlagsOp, silent bool, err error) {
|
||||
itemStr := string(item)
|
||||
silent = strings.HasSuffix(itemStr, silentOp)
|
||||
if silent {
|
||||
itemStr = strings.TrimSuffix(itemStr, silentOp)
|
||||
}
|
||||
op = FlagsOp(itemStr)
|
||||
|
||||
if op != SetFlags && op != AddFlags && op != RemoveFlags {
|
||||
err = errors.New("Unsupported STORE operation")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CharsetReader, if non-nil, defines a function to generate charset-conversion
|
||||
// readers, converting from the provided charset into UTF-8. Charsets are always
|
||||
|
84
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
84
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
@@ -2,6 +2,7 @@ package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -53,22 +54,36 @@ func (info *MailboxInfo) Parse(fields []interface{}) error {
|
||||
return errors.New("Mailbox info needs at least 3 fields")
|
||||
}
|
||||
|
||||
info.Attributes, _ = ParseStringList(fields[0])
|
||||
var err error
|
||||
if info.Attributes, err = ParseStringList(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info.Delimiter, _ = fields[1].(string)
|
||||
var ok bool
|
||||
if info.Delimiter, ok = fields[1].(string); !ok {
|
||||
return errors.New("Mailbox delimiter must be a string")
|
||||
}
|
||||
|
||||
name, _ := fields[2].(string)
|
||||
info.Name, _ = utf7.Decoder.String(name)
|
||||
info.Name = CanonicalMailboxName(info.Name)
|
||||
if name, err := ParseString(fields[2]); err != nil {
|
||||
return err
|
||||
} else if name, err := utf7.Encoding.NewDecoder().String(name); err != nil {
|
||||
return err
|
||||
} else {
|
||||
info.Name = CanonicalMailboxName(name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format mailbox info to fields.
|
||||
func (info *MailboxInfo) Format() []interface{} {
|
||||
name, _ := utf7.Encoder.String(info.Name)
|
||||
name, _ := utf7.Encoding.NewEncoder().String(info.Name)
|
||||
attrs := make([]interface{}, len(info.Attributes))
|
||||
for i, attr := range info.Attributes {
|
||||
attrs[i] = Atom(attr)
|
||||
}
|
||||
// Thunderbird doesn't understand delimiters if not quoted
|
||||
return []interface{}{FormatStringList(info.Attributes), Quoted(info.Delimiter), name}
|
||||
return []interface{}{attrs, Quoted(info.Delimiter), name}
|
||||
}
|
||||
|
||||
// TODO: optimize this
|
||||
@@ -125,19 +140,6 @@ func (info *MailboxInfo) Match(reference, pattern string) bool {
|
||||
return info.match(name, pattern)
|
||||
}
|
||||
|
||||
// Mailbox status items.
|
||||
const (
|
||||
MailboxFlags = "FLAGS"
|
||||
MailboxPermanentFlags = "PERMANENTFLAGS"
|
||||
|
||||
// Defined in RFC 3501 section 6.3.10.
|
||||
MailboxMessages = "MESSAGES"
|
||||
MailboxRecent = "RECENT"
|
||||
MailboxUnseen = "UNSEEN"
|
||||
MailboxUidNext = "UIDNEXT"
|
||||
MailboxUidValidity = "UIDVALIDITY"
|
||||
)
|
||||
|
||||
// A mailbox status.
|
||||
type MailboxStatus struct {
|
||||
// The mailbox name.
|
||||
@@ -147,7 +149,7 @@ type MailboxStatus struct {
|
||||
// The mailbox items that are currently filled in. This map's values
|
||||
// should not be used directly, they must only be used by libraries
|
||||
// implementing extensions of the IMAP protocol.
|
||||
Items map[string]interface{}
|
||||
Items map[StatusItem]interface{}
|
||||
|
||||
// The Items map may be accessed in different goroutines. Protect
|
||||
// concurrent writes.
|
||||
@@ -157,6 +159,8 @@ type MailboxStatus struct {
|
||||
Flags []string
|
||||
// The mailbox permanent flags.
|
||||
PermanentFlags []string
|
||||
// The sequence number of the first unseen message in the mailbox.
|
||||
UnseenSeqNum uint32
|
||||
|
||||
// The number of messages in this mailbox.
|
||||
Messages uint32
|
||||
@@ -172,10 +176,10 @@ type MailboxStatus struct {
|
||||
}
|
||||
|
||||
// Create a new mailbox status that will contain the specified items.
|
||||
func NewMailboxStatus(name string, items []string) *MailboxStatus {
|
||||
func NewMailboxStatus(name string, items []StatusItem) *MailboxStatus {
|
||||
status := &MailboxStatus{
|
||||
Name: name,
|
||||
Items: make(map[string]interface{}),
|
||||
Items: make(map[StatusItem]interface{}),
|
||||
}
|
||||
|
||||
for _, k := range items {
|
||||
@@ -186,30 +190,30 @@ func NewMailboxStatus(name string, items []string) *MailboxStatus {
|
||||
}
|
||||
|
||||
func (status *MailboxStatus) Parse(fields []interface{}) error {
|
||||
status.Items = make(map[string]interface{})
|
||||
status.Items = make(map[StatusItem]interface{})
|
||||
|
||||
var k string
|
||||
var k StatusItem
|
||||
for i, f := range fields {
|
||||
if i%2 == 0 {
|
||||
var ok bool
|
||||
if k, ok = f.(string); !ok {
|
||||
return errors.New("Key is not a string")
|
||||
if kstr, ok := f.(string); !ok {
|
||||
return fmt.Errorf("cannot parse mailbox status: key is not a string, but a %T", f)
|
||||
} else {
|
||||
k = StatusItem(strings.ToUpper(kstr))
|
||||
}
|
||||
k = strings.ToUpper(k)
|
||||
} else {
|
||||
status.Items[k] = nil
|
||||
|
||||
var err error
|
||||
switch k {
|
||||
case MailboxMessages:
|
||||
case StatusMessages:
|
||||
status.Messages, err = ParseNumber(f)
|
||||
case MailboxRecent:
|
||||
case StatusRecent:
|
||||
status.Recent, err = ParseNumber(f)
|
||||
case MailboxUnseen:
|
||||
case StatusUnseen:
|
||||
status.Unseen, err = ParseNumber(f)
|
||||
case MailboxUidNext:
|
||||
case StatusUidNext:
|
||||
status.UidNext, err = ParseNumber(f)
|
||||
case MailboxUidValidity:
|
||||
case StatusUidValidity:
|
||||
status.UidValidity, err = ParseNumber(f)
|
||||
default:
|
||||
status.Items[k] = f
|
||||
@@ -228,19 +232,19 @@ func (status *MailboxStatus) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
for k, v := range status.Items {
|
||||
switch k {
|
||||
case MailboxMessages:
|
||||
case StatusMessages:
|
||||
v = status.Messages
|
||||
case MailboxRecent:
|
||||
case StatusRecent:
|
||||
v = status.Recent
|
||||
case MailboxUnseen:
|
||||
case StatusUnseen:
|
||||
v = status.Unseen
|
||||
case MailboxUidNext:
|
||||
case StatusUidNext:
|
||||
v = status.UidNext
|
||||
case MailboxUidValidity:
|
||||
case StatusUidValidity:
|
||||
v = status.UidValidity
|
||||
}
|
||||
|
||||
fields = append(fields, k, v)
|
||||
fields = append(fields, string(k), v)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
299
vendor/github.com/emersion/go-imap/message.go
generated
vendored
299
vendor/github.com/emersion/go-imap/message.go
generated
vendored
@@ -30,30 +30,13 @@ var flags = []string{
|
||||
RecentFlag,
|
||||
}
|
||||
|
||||
// Message attributes that can be fetched, defined in RFC 3501 section 6.4.5.
|
||||
// Attributes that fetches the message contents are defined with
|
||||
// BodySectionName.
|
||||
const (
|
||||
// Non-extensible form of BODYSTRUCTURE.
|
||||
BodyMsgAttr = "BODY"
|
||||
// MIME body structure of the message.
|
||||
BodyStructureMsgAttr = "BODYSTRUCTURE"
|
||||
// The envelope structure of the message.
|
||||
EnvelopeMsgAttr = "ENVELOPE"
|
||||
// The flags that are set for the message.
|
||||
FlagsMsgAttr = "FLAGS"
|
||||
// The internal date of the message.
|
||||
InternalDateMsgAttr = "INTERNALDATE"
|
||||
// The RFC 822 size of the message.
|
||||
SizeMsgAttr = "RFC822.SIZE"
|
||||
// The unique identifier for the message.
|
||||
UidMsgAttr = "UID"
|
||||
)
|
||||
// A PartSpecifier specifies which parts of the MIME entity should be returned.
|
||||
type PartSpecifier string
|
||||
|
||||
// Part specifiers described in RFC 3501 page 55.
|
||||
const (
|
||||
// Refers to the entire part, including headers.
|
||||
EntireSpecifier = ""
|
||||
EntireSpecifier PartSpecifier = ""
|
||||
// Refers to the header of the part. Must include the final CRLF delimiting
|
||||
// the header and the body.
|
||||
HeaderSpecifier = "HEADER"
|
||||
@@ -61,7 +44,7 @@ const (
|
||||
TextSpecifier = "TEXT"
|
||||
// Refers to the MIME Internet Message Body header. Must include the final
|
||||
// CRLF delimiting the header and the body.
|
||||
MimeSpecifier = "MIME"
|
||||
MIMESpecifier = "MIME"
|
||||
)
|
||||
|
||||
// Returns the canonical form of a flag. Flags are case-insensitive.
|
||||
@@ -83,9 +66,9 @@ func ParseParamList(fields []interface{}) (map[string]string, error) {
|
||||
|
||||
var k string
|
||||
for i, f := range fields {
|
||||
p, ok := f.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("Parameter list contains a non-string")
|
||||
p, err := ParseString(f)
|
||||
if err != nil {
|
||||
return nil, errors.New("Parameter list contains a non-string: " + err.Error())
|
||||
}
|
||||
|
||||
if i%2 == 0 {
|
||||
@@ -158,7 +141,7 @@ type Message struct {
|
||||
// The mailbox items that are currently filled in. This map's values
|
||||
// should not be used directly, they must only be used by libraries
|
||||
// implementing extensions of the IMAP protocol.
|
||||
Items map[string]interface{}
|
||||
Items map[FetchItem]interface{}
|
||||
|
||||
// The message envelope.
|
||||
Envelope *Envelope
|
||||
@@ -178,14 +161,14 @@ type Message struct {
|
||||
// The order in which items were requested. This order must be preserved
|
||||
// because some bad IMAP clients (looking at you, Outlook!) refuse responses
|
||||
// containing items in a different order.
|
||||
itemsOrder []string
|
||||
itemsOrder []FetchItem
|
||||
}
|
||||
|
||||
// Create a new empty message that will contain the specified items.
|
||||
func NewMessage(seqNum uint32, items []string) *Message {
|
||||
func NewMessage(seqNum uint32, items []FetchItem) *Message {
|
||||
msg := &Message{
|
||||
SeqNum: seqNum,
|
||||
Items: make(map[string]interface{}),
|
||||
Items: make(map[FetchItem]interface{}),
|
||||
Body: make(map[*BodySectionName]Literal),
|
||||
itemsOrder: items,
|
||||
}
|
||||
@@ -199,65 +182,65 @@ func NewMessage(seqNum uint32, items []string) *Message {
|
||||
|
||||
// Parse a message from fields.
|
||||
func (m *Message) Parse(fields []interface{}) error {
|
||||
m.Items = make(map[string]interface{})
|
||||
m.Items = make(map[FetchItem]interface{})
|
||||
m.Body = map[*BodySectionName]Literal{}
|
||||
m.itemsOrder = nil
|
||||
|
||||
var k string
|
||||
var k FetchItem
|
||||
for i, f := range fields {
|
||||
if i%2 == 0 { // It's a key
|
||||
var ok bool
|
||||
if k, ok = f.(string); !ok {
|
||||
return errors.New("Key is not a string")
|
||||
if kstr, ok := f.(string); !ok {
|
||||
return fmt.Errorf("cannot parse message: key is not a string, but a %T", f)
|
||||
} else {
|
||||
k = FetchItem(strings.ToUpper(kstr))
|
||||
}
|
||||
k = strings.ToUpper(k)
|
||||
} else { // It's a value
|
||||
m.Items[k] = nil
|
||||
m.itemsOrder = append(m.itemsOrder, k)
|
||||
|
||||
switch k {
|
||||
case BodyMsgAttr, BodyStructureMsgAttr:
|
||||
case FetchBody, FetchBodyStructure:
|
||||
bs, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("BODYSTRUCTURE is not a list")
|
||||
return fmt.Errorf("cannot parse message: BODYSTRUCTURE is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.BodyStructure = &BodyStructure{Extended: k == BodyStructureMsgAttr}
|
||||
m.BodyStructure = &BodyStructure{Extended: k == FetchBodyStructure}
|
||||
if err := m.BodyStructure.Parse(bs); err != nil {
|
||||
return err
|
||||
}
|
||||
case EnvelopeMsgAttr:
|
||||
case FetchEnvelope:
|
||||
env, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("ENVELOPE is not a list")
|
||||
return fmt.Errorf("cannot parse message: ENVELOPE is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.Envelope = &Envelope{}
|
||||
if err := m.Envelope.Parse(env); err != nil {
|
||||
return err
|
||||
}
|
||||
case FlagsMsgAttr:
|
||||
case FetchFlags:
|
||||
flags, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("FLAGS is not a list")
|
||||
return fmt.Errorf("cannot parse message: FLAGS is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.Flags = make([]string, len(flags))
|
||||
for i, flag := range flags {
|
||||
s, _ := flag.(string)
|
||||
s, _ := ParseString(flag)
|
||||
m.Flags[i] = CanonicalFlag(s)
|
||||
}
|
||||
case InternalDateMsgAttr:
|
||||
case FetchInternalDate:
|
||||
date, _ := f.(string)
|
||||
m.InternalDate, _ = time.Parse(DateTimeLayout, date)
|
||||
case SizeMsgAttr:
|
||||
case FetchRFC822Size:
|
||||
m.Size, _ = ParseNumber(f)
|
||||
case UidMsgAttr:
|
||||
case FetchUid:
|
||||
m.Uid, _ = ParseNumber(f)
|
||||
default:
|
||||
// Likely to be a section of the body
|
||||
// First check that the section name is correct
|
||||
if section, err := NewBodySectionName(k); err != nil {
|
||||
if section, err := ParseBodySectionName(k); err != nil {
|
||||
// Not a section name, maybe an attribute defined in an IMAP extension
|
||||
m.Items[k] = f
|
||||
} else {
|
||||
@@ -270,24 +253,28 @@ func (m *Message) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) formatItem(k string) []interface{} {
|
||||
func (m *Message) formatItem(k FetchItem) []interface{} {
|
||||
v := m.Items[k]
|
||||
var kk interface{} = k
|
||||
var kk interface{} = string(k)
|
||||
|
||||
switch strings.ToUpper(k) {
|
||||
case BodyMsgAttr, BodyStructureMsgAttr:
|
||||
switch k {
|
||||
case FetchBody, FetchBodyStructure:
|
||||
// Extension data is only returned with the BODYSTRUCTURE fetch
|
||||
m.BodyStructure.Extended = k == BodyStructureMsgAttr
|
||||
m.BodyStructure.Extended = k == FetchBodyStructure
|
||||
v = m.BodyStructure.Format()
|
||||
case EnvelopeMsgAttr:
|
||||
case FetchEnvelope:
|
||||
v = m.Envelope.Format()
|
||||
case FlagsMsgAttr:
|
||||
v = FormatStringList(m.Flags)
|
||||
case InternalDateMsgAttr:
|
||||
case FetchFlags:
|
||||
flags := make([]interface{}, len(m.Flags))
|
||||
for i, flag := range m.Flags {
|
||||
flags[i] = Atom(flag)
|
||||
}
|
||||
v = flags
|
||||
case FetchInternalDate:
|
||||
v = m.InternalDate
|
||||
case SizeMsgAttr:
|
||||
case FetchRFC822Size:
|
||||
v = m.Size
|
||||
case UidMsgAttr:
|
||||
case FetchUid:
|
||||
v = m.Uid
|
||||
default:
|
||||
for section, literal := range m.Body {
|
||||
@@ -307,7 +294,7 @@ func (m *Message) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
|
||||
// First send ordered items
|
||||
processed := make(map[string]bool)
|
||||
processed := make(map[FetchItem]bool)
|
||||
for _, k := range m.itemsOrder {
|
||||
if _, ok := m.Items[k]; ok {
|
||||
fields = append(fields, m.formatItem(k)...)
|
||||
@@ -326,9 +313,11 @@ func (m *Message) Format() []interface{} {
|
||||
}
|
||||
|
||||
// Get the body section with the specified name. Returns nil if it's not found.
|
||||
func (m *Message) GetBody(s string) Literal {
|
||||
for section, body := range m.Body {
|
||||
if section.value == s {
|
||||
func (m *Message) GetBody(section *BodySectionName) Literal {
|
||||
section = section.resp()
|
||||
|
||||
for s, body := range m.Body {
|
||||
if section.Equal(s) {
|
||||
return body
|
||||
}
|
||||
}
|
||||
@@ -338,7 +327,7 @@ func (m *Message) GetBody(s string) Literal {
|
||||
// A body section name.
|
||||
// See RFC 3501 page 55.
|
||||
type BodySectionName struct {
|
||||
*BodyPartName
|
||||
BodyPartName
|
||||
|
||||
// If set to true, do not implicitly set the \Seen flag.
|
||||
Peek bool
|
||||
@@ -347,11 +336,11 @@ type BodySectionName struct {
|
||||
// octets desired.
|
||||
Partial []int
|
||||
|
||||
value string
|
||||
value FetchItem
|
||||
}
|
||||
|
||||
func (section *BodySectionName) parse(s string) (err error) {
|
||||
section.value = s
|
||||
func (section *BodySectionName) parse(s string) error {
|
||||
section.value = FetchItem(s)
|
||||
|
||||
if s == "RFC822" {
|
||||
s = "BODY[]"
|
||||
@@ -385,14 +374,13 @@ func (section *BodySectionName) parse(s string) (err error) {
|
||||
|
||||
b := bytes.NewBufferString(part + string(cr) + string(lf))
|
||||
r := NewReader(b)
|
||||
var fields []interface{}
|
||||
if fields, err = r.ReadFields(); err != nil {
|
||||
return
|
||||
fields, err := r.ReadFields()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
section.BodyPartName = &BodyPartName{}
|
||||
if err = section.BodyPartName.parse(fields); err != nil {
|
||||
return
|
||||
if err := section.BodyPartName.parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(partial) > 0 {
|
||||
@@ -420,17 +408,17 @@ func (section *BodySectionName) parse(s string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (section *BodySectionName) String() (s string) {
|
||||
func (section *BodySectionName) FetchItem() FetchItem {
|
||||
if section.value != "" {
|
||||
return section.value
|
||||
}
|
||||
|
||||
s = "BODY"
|
||||
s := "BODY"
|
||||
if section.Peek {
|
||||
s += ".PEEK"
|
||||
}
|
||||
|
||||
s += "[" + section.BodyPartName.String() + "]"
|
||||
s += "[" + section.BodyPartName.string() + "]"
|
||||
|
||||
if len(section.Partial) > 0 {
|
||||
s += "<"
|
||||
@@ -444,30 +432,39 @@ func (section *BodySectionName) String() (s string) {
|
||||
s += ">"
|
||||
}
|
||||
|
||||
return
|
||||
return FetchItem(s)
|
||||
}
|
||||
|
||||
// Equal checks whether two sections are equal.
|
||||
func (section *BodySectionName) Equal(other *BodySectionName) bool {
|
||||
if section.Peek != other.Peek {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) != len(other.Partial) {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) > 0 && section.Partial[0] != other.Partial[0] {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) > 1 && section.Partial[1] != other.Partial[1] {
|
||||
return false
|
||||
}
|
||||
return section.BodyPartName.Equal(&other.BodyPartName)
|
||||
}
|
||||
|
||||
func (section *BodySectionName) resp() *BodySectionName {
|
||||
var reset bool
|
||||
|
||||
if section.Peek != false {
|
||||
section.Peek = false
|
||||
reset = true
|
||||
resp := *section // Copy section
|
||||
if resp.Peek != false {
|
||||
resp.Peek = false
|
||||
}
|
||||
|
||||
if len(section.Partial) == 2 {
|
||||
section.Partial = []int{section.Partial[0]}
|
||||
reset = true
|
||||
if len(resp.Partial) == 2 {
|
||||
resp.Partial = []int{resp.Partial[0]}
|
||||
}
|
||||
|
||||
if reset && !strings.HasPrefix(section.value, "RFC822") {
|
||||
section.value = "" // Reset cached value
|
||||
}
|
||||
|
||||
return section
|
||||
resp.value = ""
|
||||
return &resp
|
||||
}
|
||||
|
||||
// Returns a subset of the specified bytes matching the partial requested in the
|
||||
// ExtractPartial returns a subset of the specified bytes matching the partial requested in the
|
||||
// section name.
|
||||
func (section *BodySectionName) ExtractPartial(b []byte) []byte {
|
||||
if len(section.Partial) != 2 {
|
||||
@@ -486,17 +483,17 @@ func (section *BodySectionName) ExtractPartial(b []byte) []byte {
|
||||
return b[from:to]
|
||||
}
|
||||
|
||||
// Parse a body section name.
|
||||
func NewBodySectionName(s string) (section *BodySectionName, err error) {
|
||||
section = &BodySectionName{}
|
||||
err = section.parse(s)
|
||||
return
|
||||
// ParseBodySectionName parses a body section name.
|
||||
func ParseBodySectionName(s FetchItem) (*BodySectionName, error) {
|
||||
section := new(BodySectionName)
|
||||
err := section.parse(string(s))
|
||||
return section, err
|
||||
}
|
||||
|
||||
// A body part name.
|
||||
type BodyPartName struct {
|
||||
// The specifier of the requested part.
|
||||
Specifier string
|
||||
Specifier PartSpecifier
|
||||
// The part path. Parts indexes start at 1.
|
||||
Path []int
|
||||
// If Specifier is HEADER, contains header fields that will/won't be returned,
|
||||
@@ -521,11 +518,13 @@ func (part *BodyPartName) parse(fields []interface{}) error {
|
||||
path := strings.Split(strings.ToUpper(name), ".")
|
||||
|
||||
end := 0
|
||||
loop:
|
||||
for i, node := range path {
|
||||
if node == "" || node == HeaderSpecifier || node == MimeSpecifier || node == TextSpecifier {
|
||||
part.Specifier = node
|
||||
switch PartSpecifier(node) {
|
||||
case EntireSpecifier, HeaderSpecifier, MIMESpecifier, TextSpecifier:
|
||||
part.Specifier = PartSpecifier(node)
|
||||
end = i + 1
|
||||
break
|
||||
break loop
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(node)
|
||||
@@ -560,14 +559,14 @@ func (part *BodyPartName) parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (part *BodyPartName) String() (s string) {
|
||||
func (part *BodyPartName) string() string {
|
||||
path := make([]string, len(part.Path))
|
||||
for i, index := range part.Path {
|
||||
path[i] = strconv.Itoa(index)
|
||||
}
|
||||
|
||||
if part.Specifier != "" {
|
||||
path = append(path, part.Specifier)
|
||||
if part.Specifier != EntireSpecifier {
|
||||
path = append(path, string(part.Specifier))
|
||||
}
|
||||
|
||||
if part.Specifier == HeaderSpecifier && len(part.Fields) > 0 {
|
||||
@@ -578,13 +577,47 @@ func (part *BodyPartName) String() (s string) {
|
||||
}
|
||||
}
|
||||
|
||||
s = strings.Join(path, ".")
|
||||
s := strings.Join(path, ".")
|
||||
|
||||
if len(part.Fields) > 0 {
|
||||
s += " (" + strings.Join(part.Fields, " ") + ")"
|
||||
}
|
||||
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
// Equal checks whether two body part names are equal.
|
||||
func (part *BodyPartName) Equal(other *BodyPartName) bool {
|
||||
if part.Specifier != other.Specifier {
|
||||
return false
|
||||
}
|
||||
if part.NotFields != other.NotFields {
|
||||
return false
|
||||
}
|
||||
if len(part.Path) != len(other.Path) {
|
||||
return false
|
||||
}
|
||||
for i, node := range part.Path {
|
||||
if node != other.Path[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(part.Fields) != len(other.Fields) {
|
||||
return false
|
||||
}
|
||||
for _, field := range part.Fields {
|
||||
found := false
|
||||
for _, f := range other.Fields {
|
||||
if strings.EqualFold(field, f) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// An address.
|
||||
@@ -605,17 +638,17 @@ func (addr *Address) Parse(fields []interface{}) error {
|
||||
return errors.New("Address doesn't contain 4 fields")
|
||||
}
|
||||
|
||||
if f, ok := fields[0].(string); ok {
|
||||
addr.PersonalName, _ = decodeHeader(f)
|
||||
if s, err := ParseString(fields[0]); err == nil {
|
||||
addr.PersonalName, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[1].(string); ok {
|
||||
addr.AtDomainList = f
|
||||
if s, err := ParseString(fields[1]); err == nil {
|
||||
addr.AtDomainList, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[2].(string); ok {
|
||||
addr.MailboxName = f
|
||||
if s, err := ParseString(fields[2]); err == nil {
|
||||
addr.MailboxName, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[3].(string); ok {
|
||||
addr.HostName = f
|
||||
if s, err := ParseString(fields[3]); err == nil {
|
||||
addr.HostName, _ = decodeHeader(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -702,7 +735,7 @@ func (e *Envelope) Parse(fields []interface{}) error {
|
||||
if date, ok := fields[0].(string); ok {
|
||||
e.Date, _ = parseMessageDateTime(date)
|
||||
}
|
||||
if subject, ok := fields[1].(string); ok {
|
||||
if subject, err := ParseString(fields[1]); err == nil {
|
||||
e.Subject, _ = decodeHeader(subject)
|
||||
}
|
||||
if from, ok := fields[2].([]interface{}); ok {
|
||||
@@ -755,9 +788,9 @@ type BodyStructure struct {
|
||||
// Basic fields
|
||||
|
||||
// The MIME type.
|
||||
MimeType string
|
||||
MIMEType string
|
||||
// The MIME subtype.
|
||||
MimeSubType string
|
||||
MIMESubType string
|
||||
// The MIME parameters.
|
||||
Params map[string]string
|
||||
|
||||
@@ -796,7 +829,7 @@ type BodyStructure struct {
|
||||
Location []string
|
||||
|
||||
// The MD5 checksum.
|
||||
Md5 string
|
||||
MD5 string
|
||||
}
|
||||
|
||||
func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
@@ -809,7 +842,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
|
||||
switch fields[0].(type) {
|
||||
case []interface{}: // A multipart body part
|
||||
bs.MimeType = "multipart"
|
||||
bs.MIMEType = "multipart"
|
||||
|
||||
end := 0
|
||||
for i, fi := range fields {
|
||||
@@ -829,7 +862,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
bs.MimeSubType, _ = fields[end].(string)
|
||||
bs.MIMESubType, _ = fields[end].(string)
|
||||
end++
|
||||
|
||||
// GMail seems to return only 3 extension data fields. Parse as many fields
|
||||
@@ -873,14 +906,14 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
return errors.New("Non-multipart body part doesn't have 7 fields")
|
||||
}
|
||||
|
||||
bs.MimeType, _ = fields[0].(string)
|
||||
bs.MimeSubType, _ = fields[1].(string)
|
||||
bs.MIMEType, _ = fields[0].(string)
|
||||
bs.MIMESubType, _ = fields[1].(string)
|
||||
|
||||
params, _ := fields[2].([]interface{})
|
||||
bs.Params, _ = parseHeaderParamList(params)
|
||||
|
||||
bs.Id, _ = fields[3].(string)
|
||||
if desc, ok := fields[4].(string); ok {
|
||||
if desc, err := ParseString(fields[4]); err == nil {
|
||||
bs.Description, _ = decodeHeader(desc)
|
||||
}
|
||||
bs.Encoding, _ = fields[5].(string)
|
||||
@@ -889,7 +922,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
end := 7
|
||||
|
||||
// Type-specific fields
|
||||
if bs.MimeType == "message" && bs.MimeSubType == "rfc822" {
|
||||
if bs.MIMEType == "message" && bs.MIMESubType == "rfc822" {
|
||||
if len(fields)-end < 3 {
|
||||
return errors.New("Missing type-specific fields for message/rfc822")
|
||||
}
|
||||
@@ -906,7 +939,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
|
||||
end += 3
|
||||
}
|
||||
if bs.MimeType == "text" {
|
||||
if bs.MIMEType == "text" {
|
||||
if len(fields)-end < 1 {
|
||||
return errors.New("Missing type-specific fields for text/*")
|
||||
}
|
||||
@@ -920,7 +953,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
if len(fields) > end {
|
||||
bs.Extended = true // Contains extension data
|
||||
|
||||
bs.Md5, _ = fields[end].(string)
|
||||
bs.MD5, _ = fields[end].(string)
|
||||
end++
|
||||
}
|
||||
if len(fields) > end {
|
||||
@@ -956,12 +989,12 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
}
|
||||
|
||||
func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
if bs.MimeType == "multipart" {
|
||||
if bs.MIMEType == "multipart" {
|
||||
for _, part := range bs.Parts {
|
||||
fields = append(fields, part.Format())
|
||||
}
|
||||
|
||||
fields = append(fields, bs.MimeSubType)
|
||||
fields = append(fields, bs.MIMESubType)
|
||||
|
||||
if bs.Extended {
|
||||
extended := make([]interface{}, 4)
|
||||
@@ -986,8 +1019,8 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
}
|
||||
} else {
|
||||
fields = make([]interface{}, 7)
|
||||
fields[0] = bs.MimeType
|
||||
fields[1] = bs.MimeSubType
|
||||
fields[0] = bs.MIMEType
|
||||
fields[1] = bs.MIMESubType
|
||||
fields[2] = formatHeaderParamList(bs.Params)
|
||||
|
||||
if bs.Id != "" {
|
||||
@@ -1003,7 +1036,7 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
fields[6] = bs.Size
|
||||
|
||||
// Type-specific fields
|
||||
if bs.MimeType == "message" && bs.MimeSubType == "rfc822" {
|
||||
if bs.MIMEType == "message" && bs.MIMESubType == "rfc822" {
|
||||
var env interface{}
|
||||
if bs.Envelope != nil {
|
||||
env = bs.Envelope.Format()
|
||||
@@ -1016,7 +1049,7 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
|
||||
fields = append(fields, env, bsbs, bs.Lines)
|
||||
}
|
||||
if bs.MimeType == "text" {
|
||||
if bs.MIMEType == "text" {
|
||||
fields = append(fields, bs.Lines)
|
||||
}
|
||||
|
||||
@@ -1024,8 +1057,8 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
if bs.Extended {
|
||||
extended := make([]interface{}, 4)
|
||||
|
||||
if bs.Md5 != "" {
|
||||
extended[0] = bs.Md5
|
||||
if bs.MD5 != "" {
|
||||
extended[0] = bs.MD5
|
||||
}
|
||||
if bs.Disposition != "" {
|
||||
extended[1] = []interface{}{
|
||||
|
51
vendor/github.com/emersion/go-imap/read.go
generated
vendored
51
vendor/github.com/emersion/go-imap/read.go
generated
vendored
@@ -62,7 +62,7 @@ type reader interface {
|
||||
StringReader
|
||||
}
|
||||
|
||||
// Convert a field to a number.
|
||||
// ParseNumber parses a number.
|
||||
func ParseNumber(f interface{}) (uint32, error) {
|
||||
// Useful for tests
|
||||
if n, ok := f.(uint32); ok {
|
||||
@@ -71,7 +71,7 @@ func ParseNumber(f interface{}) (uint32, error) {
|
||||
|
||||
s, ok := f.(string)
|
||||
if !ok {
|
||||
return 0, newParseError("number is not a string")
|
||||
return 0, newParseError("expected a number, got a non-atom")
|
||||
}
|
||||
|
||||
nbr, err := strconv.ParseUint(s, 10, 32)
|
||||
@@ -82,18 +82,44 @@ func ParseNumber(f interface{}) (uint32, error) {
|
||||
return uint32(nbr), nil
|
||||
}
|
||||
|
||||
// ParseString parses a string, which is either a literal, a quoted string or an
|
||||
// atom.
|
||||
func ParseString(f interface{}) (string, error) {
|
||||
if s, ok := f.(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Useful for tests
|
||||
if q, ok := f.(Quoted); ok {
|
||||
return string(q), nil
|
||||
}
|
||||
if a, ok := f.(Atom); ok {
|
||||
return string(a), nil
|
||||
}
|
||||
|
||||
if l, ok := f.(Literal); ok {
|
||||
b := make([]byte, l.Len())
|
||||
if _, err := io.ReadFull(l, b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
return "", newParseError("expected a string")
|
||||
}
|
||||
|
||||
// Convert a field list to a string list.
|
||||
func ParseStringList(f interface{}) ([]string, error) {
|
||||
fields, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return nil, newParseError("string list is not a list")
|
||||
return nil, newParseError("expected a string list, got a non-list")
|
||||
}
|
||||
|
||||
list := make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
var ok bool
|
||||
if list[i], ok = f.(string); !ok {
|
||||
return nil, newParseError("string list contains a non-string")
|
||||
var err error
|
||||
if list[i], err = ParseString(f); err != nil {
|
||||
return nil, newParseError("cannot parse string in string list: " + err.Error())
|
||||
}
|
||||
}
|
||||
return list, nil
|
||||
@@ -121,7 +147,7 @@ func (r *Reader) ReadSp() error {
|
||||
return err
|
||||
}
|
||||
if char != sp {
|
||||
return newParseError("not a space")
|
||||
return newParseError("expected a space")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -134,6 +160,7 @@ func (r *Reader) ReadCrlf() (err error) {
|
||||
}
|
||||
if char != cr {
|
||||
err = newParseError("line doesn't end with a CR")
|
||||
return
|
||||
}
|
||||
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
@@ -305,6 +332,9 @@ func (r *Reader) ReadFields() (fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
if char == cr || char == listEnd || char == respCodeEnd {
|
||||
if char == cr {
|
||||
r.UnreadRune()
|
||||
}
|
||||
return
|
||||
}
|
||||
if char == listStart {
|
||||
@@ -354,7 +384,7 @@ func (r *Reader) ReadLine() (fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadRespCode() (code string, fields []interface{}, err error) {
|
||||
func (r *Reader) ReadRespCode() (code StatusRespCode, fields []interface{}, err error) {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -376,15 +406,16 @@ func (r *Reader) ReadRespCode() (code string, fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
code, ok := fields[0].(string)
|
||||
codeStr, ok := fields[0].(string)
|
||||
if !ok {
|
||||
err = newParseError("response code doesn't start with a string atom")
|
||||
return
|
||||
}
|
||||
if code == "" {
|
||||
if codeStr == "" {
|
||||
err = newParseError("response code is empty")
|
||||
return
|
||||
}
|
||||
code = StatusRespCode(strings.ToUpper(codeStr))
|
||||
|
||||
fields = fields[1:]
|
||||
|
||||
|
241
vendor/github.com/emersion/go-imap/response.go
generated
vendored
241
vendor/github.com/emersion/go-imap/response.go
generated
vendored
@@ -1,17 +1,104 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A value that can be converted to a Resp.
|
||||
type Responser interface {
|
||||
Response() *Resp
|
||||
// Resp is an IMAP response. It is either a *DataResp, a
|
||||
// *ContinuationReq or a *StatusResp.
|
||||
type Resp interface {
|
||||
resp()
|
||||
}
|
||||
|
||||
// A response.
|
||||
// See RFC 3501 section 2.2.2
|
||||
type Resp struct {
|
||||
// ReadResp reads a single response from a Reader.
|
||||
func ReadResp(r *Reader) (Resp, error) {
|
||||
atom, err := r.ReadAtom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag, ok := atom.(string)
|
||||
if !ok {
|
||||
return nil, newParseError("response tag is not an atom")
|
||||
}
|
||||
|
||||
if tag == "+" {
|
||||
if err := r.ReadSp(); err != nil {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
resp := &ContinuationReq{}
|
||||
resp.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if err := r.ReadSp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Can be either data or status
|
||||
// Try to parse a status
|
||||
var fields []interface{}
|
||||
if atom, err := r.ReadAtom(); err == nil {
|
||||
fields = append(fields, atom)
|
||||
|
||||
if err := r.ReadSp(); err == nil {
|
||||
if name, ok := atom.(string); ok {
|
||||
status := StatusRespType(name)
|
||||
switch status {
|
||||
case StatusRespOk, StatusRespNo, StatusRespBad, StatusRespPreauth, StatusRespBye:
|
||||
resp := &StatusResp{
|
||||
Tag: tag,
|
||||
Type: status,
|
||||
}
|
||||
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.UnreadRune()
|
||||
|
||||
if char == '[' {
|
||||
// Contains code & arguments
|
||||
resp.Code, resp.Arguments, err = r.ReadRespCode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
// Not a status so it's data
|
||||
resp := &DataResp{Tag: tag}
|
||||
|
||||
var remaining []interface{}
|
||||
remaining, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Fields = append(fields, remaining...)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DataResp is an IMAP response containing data.
|
||||
type DataResp struct {
|
||||
// The response tag. Can be either "" for untagged responses, "+" for continuation
|
||||
// requests or a previous command's tag.
|
||||
Tag string
|
||||
@@ -19,10 +106,20 @@ type Resp struct {
|
||||
Fields []interface{}
|
||||
}
|
||||
|
||||
func (r *Resp) WriteTo(w *Writer) error {
|
||||
tag := r.Tag
|
||||
// NewUntaggedResp creates a new untagged response.
|
||||
func NewUntaggedResp(fields []interface{}) *DataResp {
|
||||
return &DataResp{
|
||||
Tag: "*",
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *DataResp) resp() {}
|
||||
|
||||
func (r *DataResp) WriteTo(w *Writer) error {
|
||||
tag := Atom(r.Tag)
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
tag = Atom("*")
|
||||
}
|
||||
|
||||
fields := []interface{}{tag}
|
||||
@@ -30,21 +127,15 @@ func (r *Resp) WriteTo(w *Writer) error {
|
||||
return w.writeLine(fields...)
|
||||
}
|
||||
|
||||
// Create a new untagged response.
|
||||
func NewUntaggedResp(fields []interface{}) *Resp {
|
||||
return &Resp{
|
||||
Tag: "*",
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
// A continuation request.
|
||||
type ContinuationResp struct {
|
||||
// ContinuationReq is a continuation request response.
|
||||
type ContinuationReq struct {
|
||||
// The info message sent with the continuation request.
|
||||
Info string
|
||||
}
|
||||
|
||||
func (r *ContinuationResp) WriteTo(w *Writer) error {
|
||||
func (r *ContinuationReq) resp() {}
|
||||
|
||||
func (r *ContinuationReq) WriteTo(w *Writer) error {
|
||||
if err := w.writeString("+"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -58,99 +149,33 @@ func (r *ContinuationResp) WriteTo(w *Writer) error {
|
||||
return w.writeCrlf()
|
||||
}
|
||||
|
||||
// Read a single response from a Reader. Returns either a continuation request,
|
||||
// a status response or a raw response.
|
||||
func ReadResp(r *Reader) (out interface{}, err error) {
|
||||
atom, err := r.ReadAtom()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tag, ok := atom.(string)
|
||||
if !ok {
|
||||
err = errors.New("Response tag is not an atom")
|
||||
// ParseNamedResp attempts to parse a named data response.
|
||||
func ParseNamedResp(resp Resp) (name string, fields []interface{}, ok bool) {
|
||||
data, ok := resp.(*DataResp)
|
||||
if !ok || len(data.Fields) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "+" {
|
||||
if err := r.ReadSp(); err != nil {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
res := &ContinuationResp{}
|
||||
res.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = res
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.ReadSp(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Can be either data or status
|
||||
// Try to parse a status
|
||||
isStatus := false
|
||||
var fields []interface{}
|
||||
|
||||
if atom, err = r.ReadAtom(); err == nil {
|
||||
fields = append(fields, atom)
|
||||
|
||||
if err = r.ReadSp(); err == nil {
|
||||
if name, ok := atom.(string); ok {
|
||||
status := StatusRespType(name)
|
||||
if status == StatusOk || status == StatusNo || status == StatusBad || status == StatusPreauth || status == StatusBye {
|
||||
isStatus = true
|
||||
|
||||
res := &StatusResp{
|
||||
Tag: tag,
|
||||
Type: status,
|
||||
}
|
||||
|
||||
var char rune
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
r.UnreadRune()
|
||||
|
||||
if char == '[' {
|
||||
// Contains code & arguments
|
||||
res.Code, res.Arguments, err = r.ReadRespCode()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = res
|
||||
}
|
||||
// Some responses (namely EXISTS and RECENT) are formatted like so:
|
||||
// [num] [name] [...]
|
||||
// Which is fucking stupid. But we handle that here by checking if the
|
||||
// response name is a number and then rearranging it.
|
||||
if len(data.Fields) > 1 {
|
||||
name, ok := data.Fields[1].(string)
|
||||
if ok {
|
||||
if _, err := ParseNumber(data.Fields[0]); err == nil {
|
||||
fields := []interface{}{data.Fields[0]}
|
||||
fields = append(fields, data.Fields[2:]...)
|
||||
return strings.ToUpper(name), fields, true
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
if !isStatus {
|
||||
// Not a status so it's data
|
||||
res := &Resp{Tag: tag}
|
||||
|
||||
var remaining []interface{}
|
||||
remaining, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res.Fields = append(fields, remaining...)
|
||||
out = res
|
||||
// IMAP commands are formatted like this:
|
||||
// [name] [...]
|
||||
name, ok = data.Fields[0].(string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
return strings.ToUpper(name), data.Fields[1:], true
|
||||
}
|
||||
|
88
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
88
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
@@ -14,59 +14,45 @@ type Authenticate struct {
|
||||
Writer *imap.Writer
|
||||
}
|
||||
|
||||
func (r *Authenticate) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
w := r.Writer
|
||||
func (r *Authenticate) writeLine(l string) error {
|
||||
if _, err := r.Writer.Write([]byte(l + "\r\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Writer.Flush()
|
||||
}
|
||||
|
||||
// Cancel auth if an error occurs
|
||||
defer (func() {
|
||||
if err != nil {
|
||||
w.Write([]byte("*\r\n"))
|
||||
w.Flush()
|
||||
}
|
||||
})()
|
||||
func (r *Authenticate) cancel() error {
|
||||
return r.writeLine("*")
|
||||
}
|
||||
|
||||
for h := range hdlr {
|
||||
cont, ok := h.Resp.(*imap.ContinuationResp)
|
||||
if !ok {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
// Empty challenge, send initial response as stated in RFC 2222 section 5.1
|
||||
if cont.Info == "" && r.InitialResponse != nil {
|
||||
encoded := base64.StdEncoding.EncodeToString(r.InitialResponse)
|
||||
if _, err = w.Write([]byte(encoded + "\r\n")); err != nil {
|
||||
return
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.InitialResponse = nil
|
||||
continue
|
||||
}
|
||||
|
||||
var challenge []byte
|
||||
challenge, err = base64.StdEncoding.DecodeString(cont.Info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var res []byte
|
||||
res, err = r.Mechanism.Next(challenge)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(res)
|
||||
if _, err = w.Write([]byte(encoded + "\r\n")); err != nil {
|
||||
return
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
func (r *Authenticate) Handle(resp imap.Resp) error {
|
||||
cont, ok := resp.(*imap.ContinuationReq)
|
||||
if !ok {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
return
|
||||
// Empty challenge, send initial response as stated in RFC 2222 section 5.1
|
||||
if cont.Info == "" && r.InitialResponse != nil {
|
||||
encoded := base64.StdEncoding.EncodeToString(r.InitialResponse)
|
||||
if err := r.writeLine(encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
r.InitialResponse = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
challenge, err := base64.StdEncoding.DecodeString(cont.Info)
|
||||
if err != nil {
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
reply, err := r.Mechanism.Next(challenge)
|
||||
if err != nil {
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(reply)
|
||||
return r.writeLine(encoded)
|
||||
}
|
||||
|
23
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
23
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
@@ -10,30 +10,11 @@ type Capability struct {
|
||||
Caps []string
|
||||
}
|
||||
|
||||
func (r *Capability) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
for h := range hdlr {
|
||||
caps, ok := h.AcceptNamedResp(imap.Capability)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
r.Caps = make([]string, len(caps))
|
||||
for i, c := range caps {
|
||||
r.Caps[i], _ = c.(string)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Capability) WriteTo(w *imap.Writer) error {
|
||||
fields := []interface{}{imap.Capability}
|
||||
fields := []interface{}{"CAPABILITY"}
|
||||
for _, cap := range r.Caps {
|
||||
fields = append(fields, cap)
|
||||
}
|
||||
|
||||
res := &imap.Resp{Fields: fields}
|
||||
return res.WriteTo(w)
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
|
38
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
38
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
@@ -4,39 +4,37 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const expungeName = "EXPUNGE"
|
||||
|
||||
// An EXPUNGE response.
|
||||
// See RFC 3501 section 7.4.1
|
||||
type Expunge struct {
|
||||
SeqNums chan uint32
|
||||
}
|
||||
|
||||
func (r *Expunge) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.SeqNums)
|
||||
|
||||
for h := range hdlr {
|
||||
res, ok := h.Resp.(*imap.Resp)
|
||||
if !ok || len(res.Fields) < 3 {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
if name, ok := res.Fields[1].(string); !ok || name != imap.Expunge {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
seqNum, _ := imap.ParseNumber(res.Fields[0])
|
||||
r.SeqNums <- seqNum
|
||||
func (r *Expunge) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != expungeName {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
seqNum, err := imap.ParseNumber(fields[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.SeqNums <- seqNum
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Expunge) WriteTo(w *imap.Writer) error {
|
||||
for seqNum := range r.SeqNums {
|
||||
res := imap.NewUntaggedResp([]interface{}{seqNum, imap.Expunge})
|
||||
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp([]interface{}{seqNum, expungeName})
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
52
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
52
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
@@ -4,49 +4,41 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const fetchName = "FETCH"
|
||||
|
||||
// A FETCH response.
|
||||
// See RFC 3501 section 7.4.2
|
||||
type Fetch struct {
|
||||
Messages chan *imap.Message
|
||||
}
|
||||
|
||||
func (r *Fetch) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.Messages)
|
||||
|
||||
for h := range hdlr {
|
||||
res, ok := h.Resp.(*imap.Resp)
|
||||
if !ok || len(res.Fields) < 3 {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
if name, ok := res.Fields[1].(string); !ok || name != imap.Fetch {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
seqNum, _ := imap.ParseNumber(res.Fields[0])
|
||||
fields, _ := res.Fields[2].([]interface{})
|
||||
|
||||
msg := &imap.Message{
|
||||
SeqNum: seqNum,
|
||||
}
|
||||
|
||||
if err := msg.Parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Messages <- msg
|
||||
func (r *Fetch) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != fetchName {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 1 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
seqNum, err := imap.ParseNumber(fields[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgFields, _ := fields[1].([]interface{})
|
||||
msg := &imap.Message{SeqNum: seqNum}
|
||||
if err := msg.Parse(msgFields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Messages <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Fetch) WriteTo(w *imap.Writer) error {
|
||||
for msg := range r.Messages {
|
||||
res := imap.NewUntaggedResp([]interface{}{msg.SeqNum, imap.Fetch, msg.Format()})
|
||||
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp([]interface{}{msg.SeqNum, fetchName, msg.Format()})
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
44
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
44
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
@@ -4,6 +4,11 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const (
|
||||
listName = "LIST"
|
||||
lsubName = "LSUB"
|
||||
)
|
||||
|
||||
// A LIST response.
|
||||
// If Subscribed is set to true, LSUB will be used instead.
|
||||
// See RFC 3501 section 7.2.2
|
||||
@@ -14,43 +19,36 @@ type List struct {
|
||||
|
||||
func (r *List) Name() string {
|
||||
if r.Subscribed {
|
||||
return imap.Lsub
|
||||
return lsubName
|
||||
} else {
|
||||
return imap.List
|
||||
return listName
|
||||
}
|
||||
}
|
||||
|
||||
func (r *List) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.Mailboxes)
|
||||
|
||||
name := r.Name()
|
||||
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
mbox := &imap.MailboxInfo{}
|
||||
if err := mbox.Parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Mailboxes <- mbox
|
||||
func (r *List) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != r.Name() {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
mbox := &imap.MailboxInfo{}
|
||||
if err := mbox.Parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Mailboxes <- mbox
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *List) WriteTo(w *imap.Writer) error {
|
||||
name := r.Name()
|
||||
respName := r.Name()
|
||||
|
||||
for mbox := range r.Mailboxes {
|
||||
fields := []interface{}{name}
|
||||
fields := []interface{}{respName}
|
||||
fields = append(fields, mbox.Format()...)
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
26
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
26
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
@@ -1,2 +1,28 @@
|
||||
// IMAP responses defined in RFC 3501.
|
||||
package responses
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// ErrUnhandled is used when a response hasn't been handled.
|
||||
var ErrUnhandled = errors.New("imap: unhandled response")
|
||||
|
||||
var errNotEnoughFields = errors.New("imap: not enough fields in response")
|
||||
|
||||
// Handler handles responses.
|
||||
type Handler interface {
|
||||
// Handle processes a response. If the response cannot be processed,
|
||||
// ErrUnhandledResp must be returned.
|
||||
Handle(resp imap.Resp) error
|
||||
}
|
||||
|
||||
// HandlerFunc is a function that handles responses.
|
||||
type HandlerFunc func(resp imap.Resp) error
|
||||
|
||||
// Handle implements Handler.
|
||||
func (f HandlerFunc) Handle(resp imap.Resp) error {
|
||||
return f(resp)
|
||||
}
|
||||
|
30
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
30
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
@@ -4,34 +4,38 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const searchName = "SEARCH"
|
||||
|
||||
// A SEARCH response.
|
||||
// See RFC 3501 section 7.2.5
|
||||
type Search struct {
|
||||
Ids []uint32
|
||||
}
|
||||
|
||||
func (r *Search) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(imap.Search)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
func (r *Search) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != searchName {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
for _, f := range fields {
|
||||
id, _ := imap.ParseNumber(f)
|
||||
r.Ids = append(r.Ids, id)
|
||||
r.Ids = make([]uint32, len(fields))
|
||||
for i, f := range fields {
|
||||
if id, err := imap.ParseNumber(f); err != nil {
|
||||
return err
|
||||
} else {
|
||||
r.Ids[i] = id
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Search) WriteTo(w *imap.Writer) (err error) {
|
||||
fields := []interface{}{imap.Search}
|
||||
fields := []interface{}{searchName}
|
||||
for _, id := range r.Ids {
|
||||
fields = append(fields, id)
|
||||
}
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
return res.WriteTo(w)
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
return resp.WriteTo(w)
|
||||
}
|
||||
|
199
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
199
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
@@ -11,135 +11,132 @@ type Select struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (r *Select) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
func (r *Select) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{}
|
||||
r.Mailbox = &imap.MailboxStatus{Items: make(map[imap.StatusItem]interface{})}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
|
||||
mbox.Items = make(map[string]interface{})
|
||||
for h := range hdlr {
|
||||
switch res := h.Resp.(type) {
|
||||
case *imap.Resp:
|
||||
fields, ok := h.AcceptNamedResp(imap.MailboxFlags)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch resp := resp.(type) {
|
||||
case *imap.DataResp:
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != "FLAGS" {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 1 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
flags, _ := fields[0].([]interface{})
|
||||
mbox.Flags, _ = imap.ParseStringList(flags)
|
||||
flags, _ := fields[0].([]interface{})
|
||||
mbox.Flags, _ = imap.ParseStringList(flags)
|
||||
case *imap.StatusResp:
|
||||
if len(resp.Arguments) < 1 {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
var item imap.StatusItem
|
||||
switch resp.Code {
|
||||
case "UNSEEN":
|
||||
mbox.UnseenSeqNum, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
case "PERMANENTFLAGS":
|
||||
flags, _ := resp.Arguments[0].([]interface{})
|
||||
mbox.PermanentFlags, _ = imap.ParseStringList(flags)
|
||||
case "UIDNEXT":
|
||||
mbox.UidNext, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
item = imap.StatusUidNext
|
||||
case "UIDVALIDITY":
|
||||
mbox.UidValidity, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
item = imap.StatusUidValidity
|
||||
default:
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
if item != "" {
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxFlags] = nil
|
||||
mbox.Items[item] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case *imap.StatusResp:
|
||||
if len(res.Arguments) < 1 {
|
||||
h.Accepts <- false
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ErrUnhandled
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
accepted := true
|
||||
switch res.Code {
|
||||
case imap.MailboxUnseen:
|
||||
mbox.Unseen, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUnseen] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxPermanentFlags:
|
||||
flags, _ := res.Arguments[0].([]interface{})
|
||||
mbox.PermanentFlags, _ = imap.ParseStringList(flags)
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxPermanentFlags] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxUidNext:
|
||||
mbox.UidNext, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUidNext] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxUidValidity:
|
||||
mbox.UidValidity, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUidValidity] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
default:
|
||||
accepted = false
|
||||
}
|
||||
h.Accepts <- accepted
|
||||
func (r *Select) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
|
||||
if mbox.Flags != nil {
|
||||
flags := make([]interface{}, len(mbox.Flags))
|
||||
for i, f := range mbox.Flags {
|
||||
flags[i] = imap.Atom(f)
|
||||
}
|
||||
res := imap.NewUntaggedResp([]interface{}{"FLAGS", flags})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if mbox.PermanentFlags != nil {
|
||||
flags := make([]interface{}, len(mbox.PermanentFlags))
|
||||
for i, f := range mbox.PermanentFlags {
|
||||
flags[i] = imap.Atom(f)
|
||||
}
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodePermanentFlags,
|
||||
Arguments: []interface{}{flags},
|
||||
Info: "Flags permitted.",
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Select) WriteTo(w *imap.Writer) (err error) {
|
||||
status := r.Mailbox
|
||||
if mbox.UnseenSeqNum > 0 {
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUnseen,
|
||||
Arguments: []interface{}{mbox.UnseenSeqNum},
|
||||
Info: fmt.Sprintf("Message %d is first unseen", mbox.UnseenSeqNum),
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for k := range r.Mailbox.Items {
|
||||
switch k {
|
||||
case imap.MailboxFlags:
|
||||
flags := make([]interface{}, len(status.Flags))
|
||||
for i, f := range status.Flags {
|
||||
flags[i] = f
|
||||
case imap.StatusMessages:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Messages, "EXISTS"})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
res := imap.NewUntaggedResp([]interface{}{"FLAGS", flags})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxPermanentFlags:
|
||||
flags := make([]interface{}, len(status.PermanentFlags))
|
||||
for i, f := range status.PermanentFlags {
|
||||
flags[i] = f
|
||||
case imap.StatusRecent:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Recent, "RECENT"})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusUidNext:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Code: imap.CodePermanentFlags,
|
||||
Arguments: []interface{}{flags},
|
||||
Info: "Flags permitted.",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxMessages:
|
||||
res := imap.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxRecent:
|
||||
res := imap.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxUnseen:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Code: imap.CodeUnseen,
|
||||
Arguments: []interface{}{status.Unseen},
|
||||
Info: fmt.Sprintf("Message %d is first unseen", status.Unseen),
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxUidNext:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidNext,
|
||||
Arguments: []interface{}{status.UidNext},
|
||||
Arguments: []interface{}{mbox.UidNext},
|
||||
Info: "Predicted next UID",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.MailboxUidValidity:
|
||||
case imap.StatusUidValidity:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidValidity,
|
||||
Arguments: []interface{}{status.UidValidity},
|
||||
Arguments: []interface{}{mbox.UidValidity},
|
||||
Info: "UIDs valid",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
62
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
62
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
@@ -7,57 +7,47 @@ import (
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
const statusName = "STATUS"
|
||||
|
||||
// A STATUS response.
|
||||
// See RFC 3501 section 7.2.4
|
||||
type Status struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (r *Status) HandleFrom(hdlr imap.RespHandler) error {
|
||||
func (r *Status) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
mbox.Items = nil
|
||||
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(imap.Status)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(fields) < 2 {
|
||||
return errors.New("STATUS response expects two fields")
|
||||
}
|
||||
|
||||
name, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("STATUS response expects a string as first argument")
|
||||
}
|
||||
mbox.Name, _ = utf7.Decoder.String(name)
|
||||
mbox.Name = imap.CanonicalMailboxName(mbox.Name)
|
||||
|
||||
var items []interface{}
|
||||
if items, ok = fields[1].([]interface{}); !ok {
|
||||
return errors.New("STATUS response expects a list as second argument")
|
||||
}
|
||||
|
||||
if err := mbox.Parse(items); err != nil {
|
||||
return err
|
||||
}
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != statusName {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 2 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
return nil
|
||||
if name, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if name, err := utf7.Encoding.NewDecoder().String(name); err != nil {
|
||||
return err
|
||||
} else {
|
||||
mbox.Name = imap.CanonicalMailboxName(name)
|
||||
}
|
||||
|
||||
var items []interface{}
|
||||
if items, ok = fields[1].([]interface{}); !ok {
|
||||
return errors.New("STATUS response expects a list as second argument")
|
||||
}
|
||||
|
||||
mbox.Items = nil
|
||||
return mbox.Parse(items)
|
||||
}
|
||||
|
||||
func (r *Status) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
|
||||
fields := []interface{}{imap.Status, mbox.Name, mbox.Format()}
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
name, _ := utf7.Encoding.NewEncoder().String(mbox.Name)
|
||||
fields := []interface{}{statusName, name, mbox.Format()}
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
|
9
vendor/github.com/emersion/go-imap/search.go
generated
vendored
9
vendor/github.com/emersion/go-imap/search.go
generated
vendored
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -43,8 +42,8 @@ func convertField(f interface{}, charsetReader func(io.Reader) io.Reader) string
|
||||
}
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
b := make([]byte, l.Len())
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
@@ -238,7 +237,7 @@ func (c *SearchCriteria) parseField(fields []interface{}, charsetReader func(io.
|
||||
case "UID":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if c.Uid, err = NewSeqSet(maybeString(f)); err != nil {
|
||||
} else if c.Uid, err = ParseSeqSet(maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "UNANSWERED", "UNDELETED", "UNDRAFT", "UNFLAGGED", "UNSEEN":
|
||||
@@ -251,7 +250,7 @@ func (c *SearchCriteria) parseField(fields []interface{}, charsetReader func(io.
|
||||
c.WithoutFlags = append(c.WithoutFlags, CanonicalFlag(maybeString(f)))
|
||||
}
|
||||
default: // Try to parse a sequence set
|
||||
if c.SeqNum, err = NewSeqSet(key); err != nil {
|
||||
if c.SeqNum, err = ParseSeqSet(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
5
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
5
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
@@ -122,8 +122,8 @@ type SeqSet struct {
|
||||
Set []Seq
|
||||
}
|
||||
|
||||
// NewSeqSet returns a new SeqSet instance after parsing the set string.
|
||||
func NewSeqSet(set string) (s *SeqSet, err error) {
|
||||
// ParseSeqSet returns a new SeqSet instance after parsing the set string.
|
||||
func ParseSeqSet(set string) (s *SeqSet, err error) {
|
||||
s = new(SeqSet)
|
||||
return s, s.Add(set)
|
||||
}
|
||||
@@ -268,7 +268,6 @@ func (s *SeqSet) insertAt(i int, v Seq) {
|
||||
s.Set = set
|
||||
}
|
||||
s.Set[i] = v
|
||||
return
|
||||
}
|
||||
|
||||
// search attempts to find the index of the sequence set value that contains q.
|
||||
|
4
vendor/github.com/emersion/go-imap/server/cmd_any.go
generated
vendored
4
vendor/github.com/emersion/go-imap/server/cmd_any.go
generated
vendored
@@ -24,7 +24,7 @@ func (cmd *Noop) Handle(conn Conn) error {
|
||||
ctx := conn.Context()
|
||||
if ctx.Mailbox != nil {
|
||||
// If a mailbox is selected, NOOP can be used to poll for server updates
|
||||
if mbox, ok := ctx.Mailbox.(backend.UpdaterMailbox); ok {
|
||||
if mbox, ok := ctx.Mailbox.(backend.MailboxPoller); ok {
|
||||
return mbox.Poll()
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ type Logout struct {
|
||||
|
||||
func (cmd *Logout) Handle(conn Conn) error {
|
||||
res := &imap.StatusResp{
|
||||
Type: imap.StatusBye,
|
||||
Type: imap.StatusRespBye,
|
||||
Info: "Closing connection",
|
||||
}
|
||||
|
||||
|
20
vendor/github.com/emersion/go-imap/server/cmd_auth.go
generated
vendored
20
vendor/github.com/emersion/go-imap/server/cmd_auth.go
generated
vendored
@@ -29,10 +29,9 @@ func (cmd *Select) Handle(conn Conn) error {
|
||||
return err
|
||||
}
|
||||
|
||||
items := []string{
|
||||
imap.MailboxFlags, imap.MailboxPermanentFlags,
|
||||
imap.MailboxMessages, imap.MailboxRecent, imap.MailboxUnseen,
|
||||
imap.MailboxUidNext, imap.MailboxUidValidity,
|
||||
items := []imap.StatusItem{
|
||||
imap.StatusMessages, imap.StatusRecent, imap.StatusUnseen,
|
||||
imap.StatusUidNext, imap.StatusUidValidity,
|
||||
}
|
||||
|
||||
status, err := mbox.Status(items)
|
||||
@@ -48,12 +47,12 @@ func (cmd *Select) Handle(conn Conn) error {
|
||||
return err
|
||||
}
|
||||
|
||||
code := imap.CodeReadWrite
|
||||
var code imap.StatusRespCode = imap.CodeReadWrite
|
||||
if ctx.MailboxReadOnly {
|
||||
code = imap.CodeReadOnly
|
||||
}
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
@@ -208,7 +207,7 @@ func (cmd *Status) Handle(conn Conn) error {
|
||||
}
|
||||
|
||||
// Only keep items thqat have been requested
|
||||
items := make(map[string]interface{})
|
||||
items := make(map[imap.StatusItem]interface{})
|
||||
for _, k := range cmd.Items {
|
||||
items[k] = status.Items[k]
|
||||
}
|
||||
@@ -231,7 +230,7 @@ func (cmd *Append) Handle(conn Conn) error {
|
||||
mbox, err := ctx.User.GetMailbox(cmd.Mailbox)
|
||||
if err == backend.ErrNoSuchMailbox {
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusNo,
|
||||
Type: imap.StatusRespNo,
|
||||
Code: imap.CodeTryCreate,
|
||||
Info: err.Error(),
|
||||
})
|
||||
@@ -246,10 +245,13 @@ func (cmd *Append) Handle(conn Conn) error {
|
||||
// If APPEND targets the currently selected mailbox, send an untagged EXISTS
|
||||
// Do this only if the backend doesn't send updates itself
|
||||
if conn.Server().Updates == nil && ctx.Mailbox != nil && ctx.Mailbox.Name() == mbox.Name() {
|
||||
status, err := mbox.Status([]string{imap.MailboxMessages})
|
||||
status, err := mbox.Status([]imap.StatusItem{imap.StatusMessages})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status.Flags = nil
|
||||
status.PermanentFlags = nil
|
||||
status.UnseenSeqNum = 0
|
||||
|
||||
res := &responses.Select{Mailbox: status}
|
||||
if err := conn.WriteResp(res); err != nil {
|
||||
|
4
vendor/github.com/emersion/go-imap/server/cmd_noauth.go
generated
vendored
4
vendor/github.com/emersion/go-imap/server/cmd_noauth.go
generated
vendored
@@ -36,7 +36,7 @@ func (cmd *StartTLS) Handle(conn Conn) error {
|
||||
// Send an OK status response to let the client know that the TLS handshake
|
||||
// can begin
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Info: "Begin TLS negotiation now",
|
||||
})
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (cmd *StartTLS) Upgrade(conn Conn) error {
|
||||
|
||||
func afterAuthStatus(conn Conn) error {
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeCapability,
|
||||
Arguments: imap.FormatStringList(conn.Capabilities()),
|
||||
})
|
||||
|
28
vendor/github.com/emersion/go-imap/server/cmd_selected.go
generated
vendored
28
vendor/github.com/emersion/go-imap/server/cmd_selected.go
generated
vendored
@@ -2,7 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
@@ -53,12 +52,8 @@ func (cmd *Close) Handle(conn Conn) error {
|
||||
ctx.Mailbox = nil
|
||||
ctx.MailboxReadOnly = false
|
||||
|
||||
if err := mailbox.Expunge(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No need to send expunge updates here, since the mailbox is already unselected
|
||||
return nil
|
||||
return mailbox.Expunge()
|
||||
}
|
||||
|
||||
type Expunge struct {
|
||||
@@ -206,15 +201,10 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
return ErrMailboxReadOnly
|
||||
}
|
||||
|
||||
itemStr := cmd.Item
|
||||
silent := strings.HasSuffix(itemStr, imap.SilentOp)
|
||||
if silent {
|
||||
itemStr = strings.TrimSuffix(itemStr, imap.SilentOp)
|
||||
}
|
||||
item := imap.FlagsOp(itemStr)
|
||||
|
||||
if item != imap.SetFlags && item != imap.AddFlags && item != imap.RemoveFlags {
|
||||
return errors.New("Unsupported STORE operation")
|
||||
// Only flags operations are supported
|
||||
op, silent, err := imap.ParseFlagsOp(cmd.Item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagsList, ok := cmd.Value.([]interface{})
|
||||
@@ -233,7 +223,7 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
// from receiving them
|
||||
// TODO: find a better way to do this, without conn.silent
|
||||
*conn.silent() = silent
|
||||
err = ctx.Mailbox.UpdateMessagesFlags(uid, cmd.SeqSet, item, flags)
|
||||
err = ctx.Mailbox.UpdateMessagesFlags(uid, cmd.SeqSet, op, flags)
|
||||
*conn.silent() = false
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -244,7 +234,7 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
if conn.Server().Updates == nil && !silent {
|
||||
inner := &Fetch{}
|
||||
inner.SeqSet = cmd.SeqSet
|
||||
inner.Items = []string{"FLAGS"}
|
||||
inner.Items = []imap.FetchItem{imap.FetchFlags}
|
||||
if uid {
|
||||
inner.Items = append(inner.Items, "UID")
|
||||
}
|
||||
@@ -307,7 +297,7 @@ func (cmd *Uid) Handle(conn Conn) error {
|
||||
}
|
||||
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Info: imap.Uid + " " + inner.Name + " completed",
|
||||
Type: imap.StatusRespOk,
|
||||
Info: "UID " + inner.Name + " completed",
|
||||
})
|
||||
}
|
||||
|
75
vendor/github.com/emersion/go-imap/server/conn.go
generated
vendored
75
vendor/github.com/emersion/go-imap/server/conn.go
generated
vendored
@@ -36,7 +36,7 @@ type Conn interface {
|
||||
|
||||
setTLSConn(*tls.Conn)
|
||||
silent() *bool // TODO: remove this
|
||||
serve() error
|
||||
serve(Conn) error
|
||||
commandHandler(cmd *imap.Command) (hdlr Handler, err error)
|
||||
}
|
||||
|
||||
@@ -52,17 +52,21 @@ type Context struct {
|
||||
MailboxReadOnly bool
|
||||
// Responses to send to the client.
|
||||
Responses chan<- imap.WriterTo
|
||||
// Closed when the client is logged out.
|
||||
LoggedOut <-chan struct{}
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
*imap.Conn
|
||||
|
||||
conn Conn // With extensions overrides
|
||||
s *Server
|
||||
ctx *Context
|
||||
l sync.Locker
|
||||
tlsConn *tls.Conn
|
||||
continues chan bool
|
||||
responses chan imap.WriterTo
|
||||
loggedOut chan struct{}
|
||||
silentVal bool
|
||||
}
|
||||
|
||||
@@ -73,6 +77,7 @@ func newConn(s *Server, c net.Conn) *conn {
|
||||
w := imap.NewWriter(nil)
|
||||
|
||||
responses := make(chan imap.WriterTo)
|
||||
loggedOut := make(chan struct{})
|
||||
|
||||
tlsConn, _ := c.(*tls.Conn)
|
||||
|
||||
@@ -84,10 +89,12 @@ func newConn(s *Server, c net.Conn) *conn {
|
||||
ctx: &Context{
|
||||
State: imap.ConnectingState,
|
||||
Responses: responses,
|
||||
LoggedOut: loggedOut,
|
||||
},
|
||||
tlsConn: tlsConn,
|
||||
continues: continues,
|
||||
responses: responses,
|
||||
loggedOut: loggedOut,
|
||||
}
|
||||
|
||||
if s.Debug != nil {
|
||||
@@ -149,14 +156,7 @@ func (c *conn) Close() error {
|
||||
c.ctx.User.Logout()
|
||||
}
|
||||
|
||||
if err := c.Conn.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
close(c.continues)
|
||||
|
||||
c.ctx.State = imap.LogoutState
|
||||
return nil
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *conn) Capabilities() []string {
|
||||
@@ -187,8 +187,8 @@ func (c *conn) send() {
|
||||
// Send continuation requests
|
||||
go func() {
|
||||
for range c.continues {
|
||||
res := &imap.ContinuationResp{Info: "send literal"}
|
||||
if err := res.WriteTo(c.Writer); err != nil {
|
||||
resp := &imap.ContinuationReq{Info: "send literal"}
|
||||
if err := resp.WriteTo(c.Writer); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot send continuation request: ", err)
|
||||
} else if err := c.Writer.Flush(); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot flush connection: ", err)
|
||||
@@ -199,19 +199,22 @@ func (c *conn) send() {
|
||||
// Send responses
|
||||
for {
|
||||
// Get a response that needs to be sent
|
||||
res := <-c.responses
|
||||
select {
|
||||
case res := <-c.responses:
|
||||
// Request to send the response
|
||||
c.l.Lock()
|
||||
|
||||
// Request to send the response
|
||||
c.l.Lock()
|
||||
// Send the response
|
||||
if err := res.WriteTo(c.Writer); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot send response: ", err)
|
||||
} else if err := c.Writer.Flush(); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot flush connection: ", err)
|
||||
}
|
||||
|
||||
// Send the response
|
||||
if err := res.WriteTo(c.Writer); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot send response: ", err)
|
||||
} else if err := c.Writer.Flush(); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot flush connection: ", err)
|
||||
c.l.Unlock()
|
||||
case <-c.loggedOut:
|
||||
return
|
||||
}
|
||||
|
||||
c.l.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +228,7 @@ func (c *conn) greet() error {
|
||||
}
|
||||
|
||||
greeting := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeCapability,
|
||||
Arguments: args,
|
||||
Info: "IMAP4rev1 Service Ready",
|
||||
@@ -262,7 +265,15 @@ func (c *conn) silent() *bool {
|
||||
return &c.silentVal
|
||||
}
|
||||
|
||||
func (c *conn) serve() error {
|
||||
func (c *conn) serve(conn Conn) error {
|
||||
c.conn = conn
|
||||
|
||||
defer func() {
|
||||
c.ctx.State = imap.LogoutState
|
||||
close(c.continues)
|
||||
close(c.loggedOut)
|
||||
}()
|
||||
|
||||
// Send greeting
|
||||
if err := c.greet(); err != nil {
|
||||
return err
|
||||
@@ -286,7 +297,7 @@ func (c *conn) serve() error {
|
||||
if err != nil {
|
||||
if imap.IsParseError(err) {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
} else {
|
||||
@@ -298,7 +309,7 @@ func (c *conn) serve() error {
|
||||
if err := cmd.Parse(fields); err != nil {
|
||||
res = &imap.StatusResp{
|
||||
Tag: cmd.Tag,
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
} else {
|
||||
@@ -307,7 +318,7 @@ func (c *conn) serve() error {
|
||||
if err != nil {
|
||||
res = &imap.StatusResp{
|
||||
Tag: cmd.Tag,
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
}
|
||||
@@ -323,8 +334,8 @@ func (c *conn) serve() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if up != nil && res.Type == imap.StatusOk {
|
||||
if err := up.Upgrade(c); err != nil {
|
||||
if up != nil && res.Type == imap.StatusRespOk {
|
||||
if err := up.Upgrade(c.conn); err != nil {
|
||||
c.s.ErrorLog.Println("cannot upgrade connection:", err)
|
||||
return err
|
||||
}
|
||||
@@ -356,24 +367,24 @@ func (c *conn) handleCommand(cmd *imap.Command) (res *imap.StatusResp, up Upgrad
|
||||
c.l.Unlock()
|
||||
defer c.l.Lock()
|
||||
|
||||
hdlrErr := hdlr.Handle(c)
|
||||
hdlrErr := hdlr.Handle(c.conn)
|
||||
if statusErr, ok := hdlrErr.(*errStatusResp); ok {
|
||||
res = statusErr.resp
|
||||
} else if hdlrErr != nil {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusNo,
|
||||
Type: imap.StatusRespNo,
|
||||
Info: hdlrErr.Error(),
|
||||
}
|
||||
} else {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
res.Tag = cmd.Tag
|
||||
|
||||
if res.Type == imap.StatusOk && res.Info == "" {
|
||||
if res.Type == imap.StatusRespOk && res.Info == "" {
|
||||
res.Info = cmd.Name + " completed"
|
||||
}
|
||||
}
|
||||
|
114
vendor/github.com/emersion/go-imap/server/server.go
generated
vendored
114
vendor/github.com/emersion/go-imap/server/server.go
generated
vendored
@@ -45,7 +45,7 @@ type Upgrader interface {
|
||||
type HandlerFactory func() Handler
|
||||
|
||||
// A function that creates SASL servers.
|
||||
type SaslServerFactory func(conn Conn) sasl.Server
|
||||
type SASLServerFactory func(conn Conn) sasl.Server
|
||||
|
||||
// An IMAP extension.
|
||||
type Extension interface {
|
||||
@@ -94,7 +94,7 @@ type Server struct {
|
||||
conns map[Conn]struct{}
|
||||
|
||||
commands map[string]HandlerFactory
|
||||
auths map[string]SaslServerFactory
|
||||
auths map[string]SASLServerFactory
|
||||
extensions []Extension
|
||||
|
||||
// TCP address to listen on.
|
||||
@@ -104,7 +104,7 @@ type Server struct {
|
||||
// This server's backend.
|
||||
Backend backend.Backend
|
||||
// Backend updates that will be sent to connected clients.
|
||||
Updates <-chan interface{}
|
||||
Updates <-chan backend.Update
|
||||
// Automatically logout clients after a duration. To do not logout users
|
||||
// automatically, set this to zero. The duration MUST be at least
|
||||
// MinAutoLogout (as stated in RFC 3501 section 5.4).
|
||||
@@ -132,7 +132,7 @@ func New(bkd backend.Backend) *Server {
|
||||
ErrorLog: log.New(os.Stderr, "imap/server: ", log.LstdFlags),
|
||||
}
|
||||
|
||||
s.auths = map[string]SaslServerFactory{
|
||||
s.auths = map[string]SASLServerFactory{
|
||||
sasl.Plain: func(conn Conn) sasl.Server {
|
||||
return sasl.NewPlainServer(func(identity, username, password string) error {
|
||||
if identity != "" && identity != username {
|
||||
@@ -153,42 +153,42 @@ func New(bkd backend.Backend) *Server {
|
||||
}
|
||||
|
||||
s.commands = map[string]HandlerFactory{
|
||||
imap.Noop: func() Handler { return &Noop{} },
|
||||
imap.Capability: func() Handler { return &Capability{} },
|
||||
imap.Logout: func() Handler { return &Logout{} },
|
||||
"NOOP": func() Handler { return &Noop{} },
|
||||
"CAPABILITY": func() Handler { return &Capability{} },
|
||||
"LOGOUT": func() Handler { return &Logout{} },
|
||||
|
||||
imap.StartTLS: func() Handler { return &StartTLS{} },
|
||||
imap.Login: func() Handler { return &Login{} },
|
||||
imap.Authenticate: func() Handler { return &Authenticate{} },
|
||||
"STARTTLS": func() Handler { return &StartTLS{} },
|
||||
"LOGIN": func() Handler { return &Login{} },
|
||||
"AUTHENTICATE": func() Handler { return &Authenticate{} },
|
||||
|
||||
imap.Select: func() Handler { return &Select{} },
|
||||
imap.Examine: func() Handler {
|
||||
"SELECT": func() Handler { return &Select{} },
|
||||
"EXAMINE": func() Handler {
|
||||
hdlr := &Select{}
|
||||
hdlr.ReadOnly = true
|
||||
return hdlr
|
||||
},
|
||||
imap.Create: func() Handler { return &Create{} },
|
||||
imap.Delete: func() Handler { return &Delete{} },
|
||||
imap.Rename: func() Handler { return &Rename{} },
|
||||
imap.Subscribe: func() Handler { return &Subscribe{} },
|
||||
imap.Unsubscribe: func() Handler { return &Unsubscribe{} },
|
||||
imap.List: func() Handler { return &List{} },
|
||||
imap.Lsub: func() Handler {
|
||||
"CREATE": func() Handler { return &Create{} },
|
||||
"DELETE": func() Handler { return &Delete{} },
|
||||
"RENAME": func() Handler { return &Rename{} },
|
||||
"SUBSCRIBE": func() Handler { return &Subscribe{} },
|
||||
"UNSUBSCRIBE": func() Handler { return &Unsubscribe{} },
|
||||
"LIST": func() Handler { return &List{} },
|
||||
"LSUB": func() Handler {
|
||||
hdlr := &List{}
|
||||
hdlr.Subscribed = true
|
||||
return hdlr
|
||||
},
|
||||
imap.Status: func() Handler { return &Status{} },
|
||||
imap.Append: func() Handler { return &Append{} },
|
||||
"STATUS": func() Handler { return &Status{} },
|
||||
"APPEND": func() Handler { return &Append{} },
|
||||
|
||||
imap.Check: func() Handler { return &Check{} },
|
||||
imap.Close: func() Handler { return &Close{} },
|
||||
imap.Expunge: func() Handler { return &Expunge{} },
|
||||
imap.Search: func() Handler { return &Search{} },
|
||||
imap.Fetch: func() Handler { return &Fetch{} },
|
||||
imap.Store: func() Handler { return &Store{} },
|
||||
imap.Copy: func() Handler { return &Copy{} },
|
||||
imap.Uid: func() Handler { return &Uid{} },
|
||||
"CHECK": func() Handler { return &Check{} },
|
||||
"CLOSE": func() Handler { return &Close{} },
|
||||
"EXPUNGE": func() Handler { return &Expunge{} },
|
||||
"SEARCH": func() Handler { return &Search{} },
|
||||
"FETCH": func() Handler { return &Fetch{} },
|
||||
"STORE": func() Handler { return &Store{} },
|
||||
"COPY": func() Handler { return &Copy{} },
|
||||
"UID": func() Handler { return &Uid{} },
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -207,7 +207,11 @@ func (s *Server) Serve(l net.Listener) error {
|
||||
delete(s.listeners, l)
|
||||
}()
|
||||
|
||||
go s.listenUpdates()
|
||||
updater, ok := s.Backend.(backend.BackendUpdater)
|
||||
if ok {
|
||||
s.Updates = updater.Updates()
|
||||
go s.listenUpdates()
|
||||
}
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
@@ -274,7 +278,7 @@ func (s *Server) serveConn(conn Conn) error {
|
||||
delete(s.conns, conn)
|
||||
}()
|
||||
|
||||
return conn.serve()
|
||||
return conn.serve(conn)
|
||||
}
|
||||
|
||||
// Get a command handler factory for the provided command name.
|
||||
@@ -289,49 +293,32 @@ func (s *Server) Command(name string) HandlerFactory {
|
||||
return s.commands[name]
|
||||
}
|
||||
|
||||
func (s *Server) listenUpdates() (err error) {
|
||||
updater, ok := s.Backend.(backend.Updater)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
s.Updates = updater.Updates()
|
||||
|
||||
func (s *Server) listenUpdates() {
|
||||
for {
|
||||
item := <-s.Updates
|
||||
update := <-s.Updates
|
||||
|
||||
var (
|
||||
update *backend.Update
|
||||
res imap.WriterTo
|
||||
)
|
||||
|
||||
switch item := item.(type) {
|
||||
var res imap.WriterTo
|
||||
switch update := update.(type) {
|
||||
case *backend.StatusUpdate:
|
||||
update = &item.Update
|
||||
res = item.StatusResp
|
||||
res = update.StatusResp
|
||||
case *backend.MailboxUpdate:
|
||||
update = &item.Update
|
||||
res = &responses.Select{Mailbox: item.MailboxStatus}
|
||||
res = &responses.Select{Mailbox: update.MailboxStatus}
|
||||
case *backend.MessageUpdate:
|
||||
update = &item.Update
|
||||
|
||||
ch := make(chan *imap.Message, 1)
|
||||
ch <- item.Message
|
||||
ch <- update.Message
|
||||
close(ch)
|
||||
|
||||
res = &responses.Fetch{Messages: ch}
|
||||
case *backend.ExpungeUpdate:
|
||||
update = &item.Update
|
||||
|
||||
ch := make(chan uint32, 1)
|
||||
ch <- item.SeqNum
|
||||
ch <- update.SeqNum
|
||||
close(ch)
|
||||
|
||||
res = &responses.Expunge{SeqNums: ch}
|
||||
default:
|
||||
s.ErrorLog.Printf("unhandled update: %T\n", item)
|
||||
s.ErrorLog.Printf("unhandled update: %T\n", update)
|
||||
}
|
||||
|
||||
if update == nil || res == nil {
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -341,10 +328,10 @@ func (s *Server) listenUpdates() (err error) {
|
||||
for conn := range s.conns {
|
||||
ctx := conn.Context()
|
||||
|
||||
if update.Username != "" && (ctx.User == nil || ctx.User.Username() != update.Username) {
|
||||
if update.Username() != "" && (ctx.User == nil || ctx.User.Username() != update.Username()) {
|
||||
continue
|
||||
}
|
||||
if update.Mailbox != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox) {
|
||||
if update.Mailbox() != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox()) {
|
||||
continue
|
||||
}
|
||||
if *conn.silent() {
|
||||
@@ -374,12 +361,11 @@ func (s *Server) listenUpdates() (err error) {
|
||||
for done := 0; done < wait; done++ {
|
||||
<-sends
|
||||
}
|
||||
close(sends)
|
||||
|
||||
backend.DoneUpdate(update)
|
||||
close(update.Done())
|
||||
}()
|
||||
} else {
|
||||
backend.DoneUpdate(update)
|
||||
close(update.Done())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,6 +407,6 @@ func (s *Server) Enable(extensions ...Extension) {
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (s *Server) EnableAuth(name string, f SaslServerFactory) {
|
||||
func (s *Server) EnableAuth(name string, f SASLServerFactory) {
|
||||
s.auths[name] = f
|
||||
}
|
||||
|
46
vendor/github.com/emersion/go-imap/status.go
generated
vendored
46
vendor/github.com/emersion/go-imap/status.go
generated
vendored
@@ -12,13 +12,13 @@ const (
|
||||
// The OK response indicates an information message from the server. When
|
||||
// tagged, it indicates successful completion of the associated command.
|
||||
// The untagged form indicates an information-only message.
|
||||
StatusOk StatusRespType = "OK"
|
||||
StatusRespOk StatusRespType = "OK"
|
||||
|
||||
// The NO response indicates an operational error message from the
|
||||
// server. When tagged, it indicates unsuccessful completion of the
|
||||
// associated command. The untagged form indicates a warning; the
|
||||
// command can still complete successfully.
|
||||
StatusNo = "NO"
|
||||
StatusRespNo = "NO"
|
||||
|
||||
// The BAD response indicates an error message from the server. When
|
||||
// tagged, it reports a protocol-level error in the client's command;
|
||||
@@ -26,32 +26,34 @@ const (
|
||||
// form indicates a protocol-level error for which the associated
|
||||
// command can not be determined; it can also indicate an internal
|
||||
// server failure.
|
||||
StatusBad = "BAD"
|
||||
StatusRespBad = "BAD"
|
||||
|
||||
// The PREAUTH response is always untagged, and is one of three
|
||||
// possible greetings at connection startup. It indicates that the
|
||||
// connection has already been authenticated by external means; thus
|
||||
// no LOGIN command is needed.
|
||||
StatusPreauth = "PREAUTH"
|
||||
StatusRespPreauth = "PREAUTH"
|
||||
|
||||
// The BYE response is always untagged, and indicates that the server
|
||||
// is about to close the connection.
|
||||
StatusBye = "BYE"
|
||||
StatusRespBye = "BYE"
|
||||
)
|
||||
|
||||
type StatusRespCode string
|
||||
|
||||
// Status response codes defined in RFC 3501 section 7.1.
|
||||
const (
|
||||
CodeAlert = "ALERT"
|
||||
CodeBadCharset = "BADCHARSET"
|
||||
CodeCapability = "CAPABILITY"
|
||||
CodeParse = "PARSE"
|
||||
CodePermanentFlags = "PERMANENTFLAGS"
|
||||
CodeReadOnly = "READ-ONLY"
|
||||
CodeReadWrite = "READ-WRITE"
|
||||
CodeTryCreate = "TRYCREATE"
|
||||
CodeUidNext = "UIDNEXT"
|
||||
CodeUidValidity = "UIDVALIDITY"
|
||||
CodeUnseen = "UNSEEN"
|
||||
CodeAlert StatusRespCode = "ALERT"
|
||||
CodeBadCharset = "BADCHARSET"
|
||||
CodeCapability = "CAPABILITY"
|
||||
CodeParse = "PARSE"
|
||||
CodePermanentFlags = "PERMANENTFLAGS"
|
||||
CodeReadOnly = "READ-ONLY"
|
||||
CodeReadWrite = "READ-WRITE"
|
||||
CodeTryCreate = "TRYCREATE"
|
||||
CodeUidNext = "UIDNEXT"
|
||||
CodeUidValidity = "UIDVALIDITY"
|
||||
CodeUnseen = "UNSEEN"
|
||||
)
|
||||
|
||||
// A status response.
|
||||
@@ -59,21 +61,19 @@ const (
|
||||
type StatusResp struct {
|
||||
// The response tag. If empty, it defaults to *.
|
||||
Tag string
|
||||
|
||||
// The status type.
|
||||
Type StatusRespType
|
||||
|
||||
// The status code.
|
||||
// See https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml
|
||||
Code string
|
||||
|
||||
Code StatusRespCode
|
||||
// Arguments provided with the status code.
|
||||
Arguments []interface{}
|
||||
|
||||
// The status info.
|
||||
Info string
|
||||
}
|
||||
|
||||
func (r *StatusResp) resp() {}
|
||||
|
||||
// If this status is NO or BAD, returns an error with the status info.
|
||||
// Otherwise, returns nil.
|
||||
func (r *StatusResp) Err() error {
|
||||
@@ -82,14 +82,14 @@ func (r *StatusResp) Err() error {
|
||||
return errors.New("imap: connection closed during command execution")
|
||||
}
|
||||
|
||||
if r.Type == StatusNo || r.Type == StatusBad {
|
||||
if r.Type == StatusRespNo || r.Type == StatusRespBad {
|
||||
return errors.New(r.Info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *StatusResp) WriteTo(w *Writer) error {
|
||||
tag := r.Tag
|
||||
tag := Atom(r.Tag)
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
}
|
||||
|
24
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
24
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
@@ -5,15 +5,11 @@ import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var ErrBadUtf7 = errors.New("bad utf-7 encoding")
|
||||
|
||||
var Decoder = &encoding.Decoder{
|
||||
Transformer: &decoder{true},
|
||||
}
|
||||
// ErrInvalidUTF7 means that a transformer encountered invalid UTF-7.
|
||||
var ErrInvalidUTF7 = errors.New("utf7: invalid UTF-7")
|
||||
|
||||
type decoder struct {
|
||||
ascii bool
|
||||
@@ -24,7 +20,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
ch := src[i]
|
||||
|
||||
if ch < min || ch > max { // Illegal code point in ASCII mode
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,14 +43,14 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
start := i + 1
|
||||
for i++; i < len(src) && src[i] != '-'; i++ {
|
||||
if src[i] == '\r' || src[i] == '\n' { // base64 package ignores CR and LF
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(src) { // Implicit shift ("&...")
|
||||
if atEOF {
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
} else {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
@@ -67,7 +63,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
d.ascii = true
|
||||
} else { // Control or non-ASCII code points in base64
|
||||
if !d.ascii { // Null shift ("&...-&...-")
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,7 +72,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
}
|
||||
|
||||
if len(b) == 0 { // Bad encoding
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -116,16 +112,16 @@ func decode(b64 []byte) []byte {
|
||||
if n := len(b64); b64[n-1] == '=' {
|
||||
return nil
|
||||
} else if n&3 == 0 {
|
||||
b = make([]byte, enc.DecodedLen(n)*3)
|
||||
b = make([]byte, b64Enc.DecodedLen(n)*3)
|
||||
} else {
|
||||
n += 4 - n&3
|
||||
b = make([]byte, n+enc.DecodedLen(n)*3)
|
||||
b = make([]byte, n+b64Enc.DecodedLen(n)*3)
|
||||
copy(b[copy(b, b64):n], []byte("=="))
|
||||
b64, b = b[:n], b[n:]
|
||||
}
|
||||
|
||||
// Decode Base64 into the first 1/3rd of b
|
||||
n, err := enc.Decode(b, b64)
|
||||
n, err := b64Enc.Decode(b, b64)
|
||||
if err != nil || n&1 == 1 {
|
||||
return nil
|
||||
}
|
||||
|
9
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
9
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
@@ -4,14 +4,9 @@ import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var Encoder = &encoding.Encoder{
|
||||
Transformer: &encoder{},
|
||||
}
|
||||
|
||||
type encoder struct{}
|
||||
|
||||
func (e *encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
@@ -81,9 +76,9 @@ func encode(s []byte) []byte {
|
||||
}
|
||||
|
||||
// Encode as base64
|
||||
n := enc.EncodedLen(len(b)) + 2
|
||||
n := b64Enc.EncodedLen(len(b)) + 2
|
||||
b64 := make([]byte, n)
|
||||
enc.Encode(b64[1:], b)
|
||||
b64Enc.Encode(b64[1:], b)
|
||||
|
||||
// Strip padding
|
||||
n -= 2 - (len(b)+2)%3
|
||||
|
21
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
21
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
@@ -3,6 +3,8 @@ package utf7
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,4 +14,21 @@ const (
|
||||
repl = '\uFFFD' // Unicode replacement code point
|
||||
)
|
||||
|
||||
var enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
|
||||
var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
|
||||
|
||||
type enc struct{}
|
||||
|
||||
func (e enc) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{
|
||||
Transformer: &decoder{true},
|
||||
}
|
||||
}
|
||||
|
||||
func (e enc) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{
|
||||
Transformer: &encoder{},
|
||||
}
|
||||
}
|
||||
|
||||
// Encoding is the modified UTF-7 encoding.
|
||||
var Encoding encoding.Encoding = enc{}
|
||||
|
19
vendor/github.com/emersion/go-imap/write.go
generated
vendored
19
vendor/github.com/emersion/go-imap/write.go
generated
vendored
@@ -14,8 +14,12 @@ type flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// A string that will be quoted.
|
||||
type Quoted string
|
||||
type (
|
||||
// A string that will be quoted.
|
||||
Quoted string
|
||||
// A raw atom.
|
||||
Atom string
|
||||
)
|
||||
|
||||
type WriterTo interface {
|
||||
WriteTo(w *Writer) error
|
||||
@@ -83,8 +87,7 @@ func (w *Writer) writeAstring(s string) error {
|
||||
return w.writeLiteral(bytes.NewBufferString(s))
|
||||
}
|
||||
|
||||
specials := string([]rune{dquote, listStart, listEnd, literalStart, sp})
|
||||
if strings.ToUpper(s) == nilAtom || s == "" || strings.ContainsAny(s, specials) {
|
||||
if strings.ToUpper(s) == nilAtom || s == "" || strings.ContainsAny(s, atomSpecials) {
|
||||
return w.writeQuoted(s)
|
||||
}
|
||||
|
||||
@@ -162,6 +165,8 @@ func (w *Writer) writeField(field interface{}) error {
|
||||
return w.writeAstring(field)
|
||||
case Quoted:
|
||||
return w.writeQuoted(string(field))
|
||||
case Atom:
|
||||
return w.writeAtom(string(field))
|
||||
case int:
|
||||
return w.writeNumber(uint32(field))
|
||||
case uint32:
|
||||
@@ -184,18 +189,18 @@ func (w *Writer) writeField(field interface{}) error {
|
||||
return w.writeString(field.String())
|
||||
case *BodySectionName:
|
||||
// Can contain spaces - that's why we don't just pass it as a string
|
||||
return w.writeString(field.String())
|
||||
return w.writeString(string(field.FetchItem()))
|
||||
}
|
||||
|
||||
return fmt.Errorf("imap: cannot format field: %v", field)
|
||||
}
|
||||
|
||||
func (w *Writer) writeRespCode(code string, args []interface{}) error {
|
||||
func (w *Writer) writeRespCode(code StatusRespCode, args []interface{}) error {
|
||||
if err := w.writeString(string(respCodeStart)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := []interface{}{code}
|
||||
fields := []interface{}{string(code)}
|
||||
fields = append(fields, args...)
|
||||
|
||||
if err := w.writeFields(fields); err != nil {
|
||||
|
Reference in New Issue
Block a user