aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-imap/client/cmd_auth.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-imap/client/cmd_auth.go')
-rw-r--r--vendor/github.com/emersion/go-imap/client/cmd_auth.go380
1 files changed, 380 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/client/cmd_auth.go b/vendor/github.com/emersion/go-imap/client/cmd_auth.go
new file mode 100644
index 0000000..a280017
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/client/cmd_auth.go
@@ -0,0 +1,380 @@
+package client
+
+import (
+ "errors"
+ "time"
+
+ "github.com/emersion/go-imap"
+ "github.com/emersion/go-imap/commands"
+ "github.com/emersion/go-imap/responses"
+)
+
+// ErrNotLoggedIn is returned if a function that requires the client to be
+// logged in is called then the client isn't.
+var ErrNotLoggedIn = errors.New("Not logged in")
+
+func (c *Client) ensureAuthenticated() error {
+ state := c.State()
+ if state != imap.AuthenticatedState && state != imap.SelectedState {
+ return ErrNotLoggedIn
+ }
+ return nil
+}
+
+// Select selects a mailbox so that messages in the mailbox can be accessed. Any
+// currently selected mailbox is deselected before attempting the new selection.
+// Even if the readOnly parameter is set to false, the server can decide to open
+// the mailbox in read-only mode.
+func (c *Client) Select(name string, readOnly bool) (*imap.MailboxStatus, error) {
+ if err := c.ensureAuthenticated(); err != nil {
+ return nil, err
+ }
+
+ cmd := &commands.Select{
+ Mailbox: name,
+ ReadOnly: readOnly,
+ }
+
+ mbox := &imap.MailboxStatus{Name: name, Items: make(map[imap.StatusItem]interface{})}
+ res := &responses.Select{
+ Mailbox: mbox,
+ }
+ c.locker.Lock()
+ c.mailbox = mbox
+ c.locker.Unlock()
+
+ status, err := c.execute(cmd, res)
+ if err != nil {
+ c.locker.Lock()
+ c.mailbox = nil
+ c.locker.Unlock()
+ return nil, err
+ }
+ if err := status.Err(); err != nil {
+ c.locker.Lock()
+ c.mailbox = nil
+ c.locker.Unlock()
+ return nil, err
+ }
+
+ c.locker.Lock()
+ mbox.ReadOnly = (status.Code == imap.CodeReadOnly)
+ c.state = imap.SelectedState
+ c.locker.Unlock()
+ return mbox, nil
+}
+
+// Create creates a mailbox with the given name.
+func (c *Client) Create(name string) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Create{
+ Mailbox: name,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Delete permanently removes the mailbox with the given name.
+func (c *Client) Delete(name string) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Delete{
+ Mailbox: name,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Rename changes the name of a mailbox.
+func (c *Client) Rename(existingName, newName string) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Rename{
+ Existing: existingName,
+ New: newName,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Subscribe adds the specified mailbox name to the server's set of "active" or
+// "subscribed" mailboxes.
+func (c *Client) Subscribe(name string) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Subscribe{
+ Mailbox: name,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Unsubscribe removes the specified mailbox name from the server's set of
+// "active" or "subscribed" mailboxes.
+func (c *Client) Unsubscribe(name string) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Unsubscribe{
+ Mailbox: name,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// List returns a subset of names from the complete set of all names available
+// to the client.
+//
+// An empty name argument is a special request to return the hierarchy delimiter
+// and the root name of the name given in the reference. The character "*" is a
+// wildcard, and matches zero or more characters at this position. The
+// character "%" is similar to "*", but it does not match a hierarchy delimiter.
+func (c *Client) List(ref, name string, ch chan *imap.MailboxInfo) error {
+ defer close(ch)
+
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.List{
+ Reference: ref,
+ Mailbox: name,
+ }
+ res := &responses.List{Mailboxes: ch}
+
+ status, err := c.execute(cmd, res)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Lsub returns a subset of names from the set of names that the user has
+// declared as being "active" or "subscribed".
+func (c *Client) Lsub(ref, name string, ch chan *imap.MailboxInfo) error {
+ defer close(ch)
+
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.List{
+ Reference: ref,
+ Mailbox: name,
+ Subscribed: true,
+ }
+ res := &responses.List{
+ Mailboxes: ch,
+ Subscribed: true,
+ }
+
+ status, err := c.execute(cmd, res)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Status requests the status of the indicated mailbox. It does not change the
+// currently selected mailbox, nor does it affect the state of any messages in
+// the queried mailbox.
+//
+// See RFC 3501 section 6.3.10 for a list of items that can be requested.
+func (c *Client) Status(name string, items []imap.StatusItem) (*imap.MailboxStatus, error) {
+ if err := c.ensureAuthenticated(); err != nil {
+ return nil, err
+ }
+
+ cmd := &commands.Status{
+ Mailbox: name,
+ Items: items,
+ }
+ res := &responses.Status{
+ Mailbox: new(imap.MailboxStatus),
+ }
+
+ status, err := c.execute(cmd, res)
+ if err != nil {
+ return nil, err
+ }
+ return res.Mailbox, status.Err()
+}
+
+// Append appends the literal argument as a new message to the end of the
+// specified destination mailbox. This argument SHOULD be in the format of an
+// RFC 2822 message. flags and date are optional arguments and can be set to
+// nil and the empty struct.
+func (c *Client) Append(mbox string, flags []string, date time.Time, msg imap.Literal) error {
+ if err := c.ensureAuthenticated(); err != nil {
+ return err
+ }
+
+ cmd := &commands.Append{
+ Mailbox: mbox,
+ Flags: flags,
+ Date: date,
+ Message: msg,
+ }
+
+ status, err := c.execute(cmd, nil)
+ if err != nil {
+ return err
+ }
+ return status.Err()
+}
+
+// Enable requests the server to enable the named extensions. The extensions
+// which were successfully enabled are returned.
+//
+// See RFC 5161 section 3.1.
+func (c *Client) Enable(caps []string) ([]string, error) {
+ if ok, err := c.Support("ENABLE"); !ok || err != nil {
+ return nil, ErrExtensionUnsupported
+ }
+
+ // ENABLE is invalid if a mailbox has been selected.
+ if c.State() != imap.AuthenticatedState {
+ return nil, ErrNotLoggedIn
+ }
+
+ cmd := &commands.Enable{Caps: caps}
+ res := &responses.Enabled{}
+
+ if status, err := c.Execute(cmd, res); err != nil {
+ return nil, err
+ } else {
+ return res.Caps, status.Err()
+ }
+}
+
+func (c *Client) idle(stop <-chan struct{}) error {
+ cmd := &commands.Idle{}
+
+ res := &responses.Idle{
+ Stop: stop,
+ RepliesCh: make(chan []byte, 10),
+ }
+
+ if status, err := c.Execute(cmd, res); err != nil {
+ return err
+ } else {
+ return status.Err()
+ }
+}
+
+// IdleOptions holds options for Client.Idle.
+type IdleOptions struct {
+ // LogoutTimeout is used to avoid being logged out by the server when
+ // idling. Each LogoutTimeout, the IDLE command is restarted. If set to
+ // zero, a default is used. If negative, this behavior is disabled.
+ LogoutTimeout time.Duration
+ // Poll interval when the server doesn't support IDLE. If zero, a default
+ // is used. If negative, polling is always disabled.
+ PollInterval time.Duration
+}
+
+// Idle indicates to the server that the client is ready to receive unsolicited
+// mailbox update messages. When the client wants to send commands again, it
+// must first close stop.
+//
+// If the server doesn't support IDLE, go-imap falls back to polling.
+func (c *Client) Idle(stop <-chan struct{}, opts *IdleOptions) error {
+ if ok, err := c.Support("IDLE"); err != nil {
+ return err
+ } else if !ok {
+ return c.idleFallback(stop, opts)
+ }
+
+ logoutTimeout := 25 * time.Minute
+ if opts != nil {
+ if opts.LogoutTimeout > 0 {
+ logoutTimeout = opts.LogoutTimeout
+ } else if opts.LogoutTimeout < 0 {
+ return c.idle(stop)
+ }
+ }
+
+ t := time.NewTicker(logoutTimeout)
+ defer t.Stop()
+
+ for {
+ stopOrRestart := make(chan struct{})
+ done := make(chan error, 1)
+ go func() {
+ done <- c.idle(stopOrRestart)
+ }()
+
+ select {
+ case <-t.C:
+ close(stopOrRestart)
+ if err := <-done; err != nil {
+ return err
+ }
+ case <-stop:
+ close(stopOrRestart)
+ return <-done
+ case err := <-done:
+ close(stopOrRestart)
+ if err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (c *Client) idleFallback(stop <-chan struct{}, opts *IdleOptions) error {
+ pollInterval := time.Minute
+ if opts != nil {
+ if opts.PollInterval > 0 {
+ pollInterval = opts.PollInterval
+ } else if opts.PollInterval < 0 {
+ return ErrExtensionUnsupported
+ }
+ }
+
+ t := time.NewTicker(pollInterval)
+ defer t.Stop()
+
+ for {
+ select {
+ case <-t.C:
+ if err := c.Noop(); err != nil {
+ return err
+ }
+ case <-stop:
+ return nil
+ case <-c.LoggedOut():
+ return errors.New("disconnected while idling")
+ }
+ }
+}