aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-imap/client/cmd_selected.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-imap/client/cmd_selected.go')
-rw-r--r--vendor/github.com/emersion/go-imap/client/cmd_selected.go367
1 files changed, 367 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/client/cmd_selected.go b/vendor/github.com/emersion/go-imap/client/cmd_selected.go
new file mode 100644
index 0000000..0fb71ad
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/client/cmd_selected.go
@@ -0,0 +1,367 @@
+package client
+
+import (
+ "errors"
+
+ "github.com/emersion/go-imap"
+ "github.com/emersion/go-imap/commands"
+ "github.com/emersion/go-imap/responses"
+)
+
+var (
+ // ErrNoMailboxSelected is returned if a command that requires a mailbox to be
+ // selected is called when there isn't.
+ ErrNoMailboxSelected = errors.New("No mailbox selected")
+
+ // ErrExtensionUnsupported is returned if a command uses a extension that
+ // is not supported by the server.
+ ErrExtensionUnsupported = errors.New("The required extension is not supported by the server")
+)
+
+// Check requests a checkpoint of the currently selected mailbox. A checkpoint
+// refers to any implementation-dependent housekeeping associated with the
+// mailbox that is not normally executed as part of each command.
+func (c *Client) Check() error {
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ cmd := new(commands.Check)
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+
+ return status.Err()
+}
+
+// Close permanently removes all messages that have the \Deleted flag set from
+// the currently selected mailbox, and returns to the authenticated state from
+// the selected state.
+func (c *Client) Close() error {
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ cmd := new(commands.Close)
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ } else if err := status.Err(); err != nil {
+ return err
+ }
+
+ c.locker.Lock()
+ c.state = imap.AuthenticatedState
+ c.mailbox = nil
+ c.locker.Unlock()
+ return nil
+}
+
+// Terminate closes the tcp connection
+func (c *Client) Terminate() error {
+ return c.conn.Close()
+}
+
+// Expunge permanently removes all messages that have the \Deleted flag set from
+// the currently selected mailbox. If ch is not nil, sends sequence IDs of each
+// deleted message to this channel.
+func (c *Client) Expunge(ch chan uint32) error {
+ if ch != nil {
+ defer close(ch)
+ }
+
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ cmd := new(commands.Expunge)
+
+ var h responses.Handler
+ if ch != nil {
+ h = &responses.Expunge{SeqNums: ch}
+ }
+
+ status, err := c.execute(cmd, h)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+func (c *Client) executeSearch(uid bool, criteria *imap.SearchCriteria, charset string) (ids []uint32, status *imap.StatusResp, err error) {
+ if c.State() != imap.SelectedState {
+ err = ErrNoMailboxSelected
+ return
+ }
+
+ var cmd imap.Commander = &commands.Search{
+ Charset: charset,
+ Criteria: criteria,
+ }
+ if uid {
+ cmd = &commands.Uid{Cmd: cmd}
+ }
+
+ res := new(responses.Search)
+
+ status, err = c.execute(cmd, res)
+ if err != nil {
+ return
+ }
+
+ err, ids = status.Err(), res.Ids
+ return
+}
+
+func (c *Client) search(uid bool, criteria *imap.SearchCriteria) (ids []uint32, err error) {
+ ids, status, err := c.executeSearch(uid, criteria, "UTF-8")
+ if status != nil && status.Code == imap.CodeBadCharset {
+ // Some servers don't support UTF-8
+ ids, _, err = c.executeSearch(uid, criteria, "US-ASCII")
+ }
+ return
+}
+
+// Search searches the mailbox for messages that match the given searching
+// criteria. Searching criteria consist of one or more search keys. The response
+// contains a list of message sequence IDs corresponding to those messages that
+// match the searching criteria. When multiple keys are specified, the result is
+// the intersection (AND function) of all the messages that match those keys.
+// Criteria must be UTF-8 encoded. See RFC 3501 section 6.4.4 for a list of
+// searching criteria. When no criteria has been set, all messages in the mailbox
+// will be searched using ALL criteria.
+func (c *Client) Search(criteria *imap.SearchCriteria) (seqNums []uint32, err error) {
+ return c.search(false, criteria)
+}
+
+// UidSearch is identical to Search, but UIDs are returned instead of message
+// sequence numbers.
+func (c *Client) UidSearch(criteria *imap.SearchCriteria) (uids []uint32, err error) {
+ return c.search(true, criteria)
+}
+
+func (c *Client) fetch(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
+ defer close(ch)
+
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ var cmd imap.Commander = &commands.Fetch{
+ SeqSet: seqset,
+ Items: items,
+ }
+ if uid {
+ cmd = &commands.Uid{Cmd: cmd}
+ }
+
+ res := &responses.Fetch{Messages: ch, SeqSet: seqset, Uid: uid}
+
+ status, err := c.execute(cmd, res)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Fetch retrieves data associated with a message in the mailbox. See RFC 3501
+// section 6.4.5 for a list of items that can be requested.
+func (c *Client) Fetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
+ return c.fetch(false, seqset, items, ch)
+}
+
+// UidFetch is identical to Fetch, but seqset is interpreted as containing
+// unique identifiers instead of message sequence numbers.
+func (c *Client) UidFetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
+ return c.fetch(true, seqset, items, ch)
+}
+
+func (c *Client) store(uid bool, seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
+ if ch != nil {
+ defer close(ch)
+ }
+
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ // TODO: this could break extensions (this only works when item is FLAGS)
+ if fields, ok := value.([]interface{}); ok {
+ for i, field := range fields {
+ if s, ok := field.(string); ok {
+ fields[i] = imap.RawString(s)
+ }
+ }
+ }
+
+ // If ch is nil, the updated values are data which will be lost, so don't
+ // retrieve it.
+ if ch == nil {
+ op, _, err := imap.ParseFlagsOp(item)
+ if err == nil {
+ item = imap.FormatFlagsOp(op, true)
+ }
+ }
+
+ var cmd imap.Commander = &commands.Store{
+ SeqSet: seqset,
+ Item: item,
+ Value: value,
+ }
+ if uid {
+ cmd = &commands.Uid{Cmd: cmd}
+ }
+
+ var h responses.Handler
+ if ch != nil {
+ h = &responses.Fetch{Messages: ch, SeqSet: seqset, Uid: uid}
+ }
+
+ status, err := c.execute(cmd, h)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Store alters data associated with a message in the mailbox. If ch is not nil,
+// the updated value of the data will be sent to this channel. See RFC 3501
+// section 6.4.6 for a list of items that can be updated.
+func (c *Client) Store(seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
+ return c.store(false, seqset, item, value, ch)
+}
+
+// UidStore is identical to Store, but seqset is interpreted as containing
+// unique identifiers instead of message sequence numbers.
+func (c *Client) UidStore(seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
+ return c.store(true, seqset, item, value, ch)
+}
+
+func (c *Client) copy(uid bool, seqset *imap.SeqSet, dest string) error {
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ var cmd imap.Commander = &commands.Copy{
+ SeqSet: seqset,
+ Mailbox: dest,
+ }
+ if uid {
+ cmd = &commands.Uid{Cmd: cmd}
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Copy copies the specified message(s) to the end of the specified destination
+// mailbox.
+func (c *Client) Copy(seqset *imap.SeqSet, dest string) error {
+ return c.copy(false, seqset, dest)
+}
+
+// UidCopy is identical to Copy, but seqset is interpreted as containing unique
+// identifiers instead of message sequence numbers.
+func (c *Client) UidCopy(seqset *imap.SeqSet, dest string) error {
+ return c.copy(true, seqset, dest)
+}
+
+func (c *Client) move(uid bool, seqset *imap.SeqSet, dest string) error {
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ if ok, err := c.Support("MOVE"); err != nil {
+ return err
+ } else if !ok {
+ return c.moveFallback(uid, seqset, dest)
+ }
+
+ var cmd imap.Commander = &commands.Move{
+ SeqSet: seqset,
+ Mailbox: dest,
+ }
+ if uid {
+ cmd = &commands.Uid{Cmd: cmd}
+ }
+
+ if status, err := c.Execute(cmd, nil); err != nil {
+ return err
+ } else {
+ return status.Err()
+ }
+}
+
+// moveFallback uses COPY, STORE and EXPUNGE for servers which don't support
+// MOVE.
+func (c *Client) moveFallback(uid bool, seqset *imap.SeqSet, dest string) error {
+ item := imap.FormatFlagsOp(imap.AddFlags, true)
+ flags := []interface{}{imap.DeletedFlag}
+ if uid {
+ if err := c.UidCopy(seqset, dest); err != nil {
+ return err
+ }
+
+ if err := c.UidStore(seqset, item, flags, nil); err != nil {
+ return err
+ }
+ } else {
+ if err := c.Copy(seqset, dest); err != nil {
+ return err
+ }
+
+ if err := c.Store(seqset, item, flags, nil); err != nil {
+ return err
+ }
+ }
+
+ return c.Expunge(nil)
+}
+
+// Move moves the specified message(s) to the end of the specified destination
+// mailbox.
+//
+// If the server doesn't support the MOVE extension defined in RFC 6851,
+// go-imap will fallback to copy, store and expunge.
+func (c *Client) Move(seqset *imap.SeqSet, dest string) error {
+ return c.move(false, seqset, dest)
+}
+
+// UidMove is identical to Move, but seqset is interpreted as containing unique
+// identifiers instead of message sequence numbers.
+func (c *Client) UidMove(seqset *imap.SeqSet, dest string) error {
+ return c.move(true, seqset, dest)
+}
+
+// Unselect frees server's resources associated with the selected mailbox and
+// returns the server to the authenticated state. This command performs the same
+// actions as Close, except that no messages are permanently removed from the
+// currently selected mailbox.
+//
+// If client does not support the UNSELECT extension, ErrExtensionUnsupported
+// is returned.
+func (c *Client) Unselect() error {
+ if ok, err := c.Support("UNSELECT"); !ok || err != nil {
+ return ErrExtensionUnsupported
+ }
+
+ if c.State() != imap.SelectedState {
+ return ErrNoMailboxSelected
+ }
+
+ cmd := &commands.Unselect{}
+ if status, err := c.Execute(cmd, nil); err != nil {
+ return err
+ } else if err := status.Err(); err != nil {
+ return err
+ }
+
+ c.SetState(imap.AuthenticatedState, nil)
+ return nil
+}