aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-imap/conn.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-imap/conn.go')
-rw-r--r--vendor/github.com/emersion/go-imap/conn.go284
1 files changed, 284 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/conn.go b/vendor/github.com/emersion/go-imap/conn.go
new file mode 100644
index 0000000..09ce633
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/conn.go
@@ -0,0 +1,284 @@
+package imap
+
+import (
+ "bufio"
+ "crypto/tls"
+ "io"
+ "net"
+ "sync"
+)
+
+// A connection state.
+// See RFC 3501 section 3.
+type ConnState int
+
+const (
+ // In the connecting state, the server has not yet sent a greeting and no
+ // command can be issued.
+ ConnectingState = 0
+
+ // In the not authenticated state, the client MUST supply
+ // authentication credentials before most commands will be
+ // permitted. This state is entered when a connection starts
+ // unless the connection has been pre-authenticated.
+ NotAuthenticatedState ConnState = 1 << 0
+
+ // In the authenticated state, the client is authenticated and MUST
+ // select a mailbox to access before commands that affect messages
+ // will be permitted. This state is entered when a
+ // pre-authenticated connection starts, when acceptable
+ // authentication credentials have been provided, after an error in
+ // selecting a mailbox, or after a successful CLOSE command.
+ AuthenticatedState = 1 << 1
+
+ // In a selected state, a mailbox has been selected to access.
+ // This state is entered when a mailbox has been successfully
+ // selected.
+ SelectedState = AuthenticatedState + 1<<2
+
+ // In the logout state, the connection is being terminated. This
+ // state can be entered as a result of a client request (via the
+ // LOGOUT command) or by unilateral action on the part of either
+ // the client or server.
+ LogoutState = 1 << 3
+
+ // ConnectedState is either NotAuthenticatedState, AuthenticatedState or
+ // SelectedState.
+ ConnectedState = NotAuthenticatedState | AuthenticatedState | SelectedState
+)
+
+// A function that upgrades a connection.
+//
+// This should only be used by libraries implementing an IMAP extension (e.g.
+// COMPRESS).
+type ConnUpgrader func(conn net.Conn) (net.Conn, error)
+
+type Waiter struct {
+ start sync.WaitGroup
+ end sync.WaitGroup
+ finished bool
+}
+
+func NewWaiter() *Waiter {
+ w := &Waiter{finished: false}
+ w.start.Add(1)
+ w.end.Add(1)
+ return w
+}
+
+func (w *Waiter) Wait() {
+ if !w.finished {
+ // Signal that we are ready for upgrade to continue.
+ w.start.Done()
+ // Wait for upgrade to finish.
+ w.end.Wait()
+ w.finished = true
+ }
+}
+
+func (w *Waiter) WaitReady() {
+ if !w.finished {
+ // Wait for reader/writer goroutine to be ready for upgrade.
+ w.start.Wait()
+ }
+}
+
+func (w *Waiter) Close() {
+ if !w.finished {
+ // Upgrade is finished, close chanel to release reader/writer
+ w.end.Done()
+ }
+}
+
+type LockedWriter struct {
+ lock sync.Mutex
+ writer io.Writer
+}
+
+// NewLockedWriter - goroutine safe writer.
+func NewLockedWriter(w io.Writer) io.Writer {
+ return &LockedWriter{writer: w}
+}
+
+func (w *LockedWriter) Write(b []byte) (int, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ return w.writer.Write(b)
+}
+
+type debugWriter struct {
+ io.Writer
+
+ local io.Writer
+ remote io.Writer
+}
+
+// NewDebugWriter creates a new io.Writer that will write local network activity
+// to local and remote network activity to remote.
+func NewDebugWriter(local, remote io.Writer) io.Writer {
+ return &debugWriter{Writer: local, local: local, remote: remote}
+}
+
+type multiFlusher struct {
+ flushers []flusher
+}
+
+func (mf *multiFlusher) Flush() error {
+ for _, f := range mf.flushers {
+ if err := f.Flush(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func newMultiFlusher(flushers ...flusher) flusher {
+ return &multiFlusher{flushers}
+}
+
+// Underlying connection state information.
+type ConnInfo struct {
+ RemoteAddr net.Addr
+ LocalAddr net.Addr
+
+ // nil if connection is not using TLS.
+ TLS *tls.ConnectionState
+}
+
+// An IMAP connection.
+type Conn struct {
+ net.Conn
+ *Reader
+ *Writer
+
+ br *bufio.Reader
+ bw *bufio.Writer
+
+ waiter *Waiter
+
+ // Print all commands and responses to this io.Writer.
+ debug io.Writer
+}
+
+// NewConn creates a new IMAP connection.
+func NewConn(conn net.Conn, r *Reader, w *Writer) *Conn {
+ c := &Conn{Conn: conn, Reader: r, Writer: w}
+
+ c.init()
+ return c
+}
+
+func (c *Conn) createWaiter() *Waiter {
+ // create new waiter each time.
+ w := NewWaiter()
+ c.waiter = w
+ return w
+}
+
+func (c *Conn) init() {
+ r := io.Reader(c.Conn)
+ w := io.Writer(c.Conn)
+
+ if c.debug != nil {
+ localDebug, remoteDebug := c.debug, c.debug
+ if debug, ok := c.debug.(*debugWriter); ok {
+ localDebug, remoteDebug = debug.local, debug.remote
+ }
+ // If local and remote are the same, then we need a LockedWriter.
+ if localDebug == remoteDebug {
+ localDebug = NewLockedWriter(localDebug)
+ remoteDebug = localDebug
+ }
+
+ if localDebug != nil {
+ w = io.MultiWriter(c.Conn, localDebug)
+ }
+ if remoteDebug != nil {
+ r = io.TeeReader(c.Conn, remoteDebug)
+ }
+ }
+
+ if c.br == nil {
+ c.br = bufio.NewReader(r)
+ c.Reader.reader = c.br
+ } else {
+ c.br.Reset(r)
+ }
+
+ if c.bw == nil {
+ c.bw = bufio.NewWriter(w)
+ c.Writer.Writer = c.bw
+ } else {
+ c.bw.Reset(w)
+ }
+
+ if f, ok := c.Conn.(flusher); ok {
+ c.Writer.Writer = struct {
+ io.Writer
+ flusher
+ }{
+ c.bw,
+ newMultiFlusher(c.bw, f),
+ }
+ }
+}
+
+func (c *Conn) Info() *ConnInfo {
+ info := &ConnInfo{
+ RemoteAddr: c.RemoteAddr(),
+ LocalAddr: c.LocalAddr(),
+ }
+
+ tlsConn, ok := c.Conn.(*tls.Conn)
+ if ok {
+ state := tlsConn.ConnectionState()
+ info.TLS = &state
+ }
+
+ return info
+}
+
+// Write implements io.Writer.
+func (c *Conn) Write(b []byte) (n int, err error) {
+ return c.Writer.Write(b)
+}
+
+// Flush writes any buffered data to the underlying connection.
+func (c *Conn) Flush() error {
+ return c.Writer.Flush()
+}
+
+// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
+// tunnel.
+func (c *Conn) Upgrade(upgrader ConnUpgrader) error {
+ // Block reads and writes during the upgrading process
+ w := c.createWaiter()
+ defer w.Close()
+
+ upgraded, err := upgrader(c.Conn)
+ if err != nil {
+ return err
+ }
+
+ c.Conn = upgraded
+ c.init()
+ return nil
+}
+
+// Called by reader/writer goroutines to wait for Upgrade to finish
+func (c *Conn) Wait() {
+ c.waiter.Wait()
+}
+
+// Called by Upgrader to wait for reader/writer goroutines to be ready for
+// upgrade.
+func (c *Conn) WaitReady() {
+ c.waiter.WaitReady()
+}
+
+// SetDebug defines an io.Writer to which all network activity will be logged.
+// If nil is provided, network activity will not be logged.
+func (c *Conn) SetDebug(w io.Writer) {
+ c.debug = w
+ c.init()
+}