imap

package
v0.0.0-...-b12309f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2019 License: BSD-3-Clause Imports: 20 Imported by: 0

Documentation

Overview

Package imap implements an IMAP4rev1 client, as defined in RFC 3501.

The implementation provides low-level access to all protocol features described in the relevant RFCs (see list below), and assumes that the developer is familiar with the basic rules governing connection states, command execution, server responses, and IMAP data types. Reading this documentation alone is not sufficient for writing a working IMAP client. As a starting point, you should read RFC 2683 to understand some of the nuances of the protocol operation.

The rest of the documentation deals with the implementation of this package and not the protocol in general.

Introduction

The package provides three main objects for interacting with an IMAP4 server: Client, Command, and Response. The client sends commands to the server and receives responses. The Response object is capable of representing all possible server responses and provides helper methods for decoding common data formats, such as LIST, FETCH, SEARCH, etc.

The client has two interfaces for issuing commands. The Send method is the raw command interface that can be used for implementing new commands, which are not already supported by the package. All standard commands, as well as those from a number of popular extensions, have dedicated methods that perform capability and field type checks, and properly encode the command arguments before passing them to Send.

Response Delivery

To support execution of multiple concurrent commands, each server response goes through a filtering process to identify its owner. Each command in progress has an associated ResponseFilter function for this purpose. The Client calls all active filters in the command-issue order until one of the filters "claims" the response. Claimed responses are appended to the Command.Data queue of the claimer. Responses rejected by all filters are referred to as "unilateral server data" and are appended to the Client.Data queue. Commands documented as expecting "no specific responses" use a nil ResponseFilter, which never claims anything. Thus, responses for commands such as NOOP are always delivered to Client.Data queue.

The Client/Command state can only be updated by a call to Client.Recv. Each call receives and delivers at most one response, but these calls are often implicit, such as when using the Wait helper function (see below). Be sure to inspect and clear out the Client data queue after all receive operations to avoid missing important server updates. The Client example below demonstrates correct response handling.

Concurrency

The Client and its Command objects cannot be used concurrently from multiple goroutines. It is safe to pass Response objects to other goroutines for processing, but the Client assumes "single-threaded" model of operation, so all method calls for the same connection must be serialized with sync.Mutex or some other synchronization mechanism. Likewise, it is not safe to access Client.Data and Command.Data in parallel with a call that can append new responses to these fields.

Asynchronous Commands

Unless a command is marked as being "synchronous", which is usually those commands that change the connection state, the associated method returns as soon as the command is sent to the server, without waiting for completion. This allows the client to issue multiple concurrent commands, and then process the responses and command completions as they arrive.

A call to Command.Result on a command that is "in progress" will block until that command is finished. There is also a convenience function that turns any asynchronous command into a synchronous one:

cmd, err := imap.Wait(c.Fetch(...))

If err is nil when the call returns, the command was completed with the OK status and all data responses (if any) are queued in cmd.Data.

Logging Out

The Client launches a goroutine to support receive operations with timeouts. The user must call Client.Logout to close the connection and stop the goroutine. The only time it is unnecessary to call Client.Logout is when the server closes the connection first and Client.Recv returns io.EOF error.

RFCs

The following RFCs are implemented by this package:

http://tools.ietf.org/html/rfc2087 -- IMAP4 QUOTA extension
http://tools.ietf.org/html/rfc2088 -- IMAP4 non-synchronizing literals
http://tools.ietf.org/html/rfc2177 -- IMAP4 IDLE command
http://tools.ietf.org/html/rfc2971 -- IMAP4 ID extension
http://tools.ietf.org/html/rfc3501 -- INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
http://tools.ietf.org/html/rfc3516 -- IMAP4 Binary Content Extension
http://tools.ietf.org/html/rfc3691 -- Internet Message Access Protocol (IMAP) UNSELECT command
http://tools.ietf.org/html/rfc4315 -- Internet Message Access Protocol (IMAP) - UIDPLUS extension
http://tools.ietf.org/html/rfc4616 -- The PLAIN Simple Authentication and Security Layer (SASL) Mechanism
http://tools.ietf.org/html/rfc4959 -- IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response
http://tools.ietf.org/html/rfc4978 -- The IMAP COMPRESS Extension
http://tools.ietf.org/html/rfc5161 -- The IMAP ENABLE Extension
http://tools.ietf.org/html/rfc5738 -- IMAP Support for UTF-8

The following RFCs are either informational, not fully implemented, or place no implementation requirements on the package, but may be relevant to other parts of a client application:

http://tools.ietf.org/html/rfc2595 -- Using TLS with IMAP, POP3 and ACAP
http://tools.ietf.org/html/rfc2683 -- IMAP4 Implementation Recommendations
http://tools.ietf.org/html/rfc4466 -- Collected Extensions to IMAP4 ABNF
http://tools.ietf.org/html/rfc4469 -- Internet Message Access Protocol (IMAP) CATENATE Extension
http://tools.ietf.org/html/rfc4549 -- Synchronization Operations for Disconnected IMAP4 Clients
http://tools.ietf.org/html/rfc5530 -- IMAP Response Codes

Index

Examples

Constants

View Source
const (
	Login                   // Not authenticated
	Auth                    // Authenticated
	Selected                // Mailbox selected
	Logout                  // Connection closing
	Closed   = ConnState(0) // Connection closed
)

Client connection states.

View Source
const (
	Status   = RespType(1 << iota) // Untagged status
	Data                           // Untagged data
	Continue                       // Continuation request
	Done                           // Tagged command completion
)

Server response types.

View Source
const (
	OK      = RespStatus(1 << iota) // Success
	NO                              // Operational error
	BAD                             // Protocol-level error
	PREAUTH                         // Greeting status indicating Auth state (untagged-only)
	BYE                             // Connection closing (untagged-only)
)

Status conditions used by Status and Done response types.

View Source
const (
	Atom          = FieldType(1 << iota) // String consisting of non-special ASCII characters
	Number                               // Unsigned 32-bit integer
	QuotedString                         // String enclosed in double quotes
	LiteralString                        // String or binary data
	List                                 // Parenthesized list
	Bytes                                // Decoded Base64 data
	NIL                                  // Case-insensitive atom string "NIL"
)

Valid field data types.

View Source
const (
	LogConn  = LogMask(1 << iota)   // Connection events
	LogState                        // State changes
	LogCmd                          // Command execution
	LogRaw                          // Raw data stream excluding literals
	LogGo                           // Goroutine execution
	LogAll   = LogMask(1<<iota - 1) // All messages
	LogNone  = LogMask(0)           // No messages
)

Debug message categories.

View Source
const DATETIME = `"_2-Jan-2006 15:04:05 -0700"`

Date-time format used by INTERNALDATE.

Variables

View Source
var (
	ErrCompressionActive = errors.New("imap: compression already enabled")
	ErrEncryptionActive  = errors.New("imap: encryption already enabled")
)

Errors returned by the low-level data transport.

View Source
var (
	DefaultLogger  = log.New(os.Stderr, "[imap] ", log.Ltime)
	DefaultLogMask = LogNone
)

Default debug logging configuration for new Client instances.

View Source
var BufferSize = 65536

BufferSize sets the size of the send and receive buffers (in bytes). This is also the length limit of physical lines. In practice, the client should restrict line length to approximately 1000 bytes, as described in RFC 2683.

View Source
var ErrAborted = errors.New("imap: command aborted")

ErrAborted is returned by Command.Result when the command execution is interrupted prior to receiving a completion response from the server. This is usually caused by a break in the connection.

View Source
var ErrBadUTF7 = errors.New("imap: bad utf-7 encoding")

ErrBadUTF7 is returned to indicate invalid modified UTF-7 encoding.

View Source
var ErrExclusive = errors.New("imap: exclusive client access violation")

ErrExclusive is returned when an attempt is made to execute multiple commands in parallel, but one of the commands requires exclusive client access.

View Source
var ErrNotAllowed = errors.New("imap: command not allowed in the current state")

ErrNotAllowed is returned when a command cannot be issued in the current connection state. Client.CommandConfig[<name>].States determines valid states for each command.

View Source
var ErrTimeout = errors.New("imap: operation timeout")

ErrTimeout is returned when an operation does not finish successfully in the allocated time.

View Source
var SelectFilter = LabelFilter(
	"FLAGS", "EXISTS", "RECENT",
	"UNSEEN", "PERMANENTFLAGS", "UIDNEXT", "UIDVALIDITY",
	"UIDNOTSTICKY",
)

SelectFilter accepts SELECT and EXAMINE command responses.

Functions

func AsAtom

func AsAtom(f Field) string

AsAtom returns the value of an atom field. An empty string is returned if TypeOf(f) != Atom.

func AsBytes

func AsBytes(f Field) []byte

AsBytes returns the value of a data field. Nil is returned if TypeOf(f)&(QuotedString|LiteralString|Bytes) == 0.

func AsDateTime

func AsDateTime(f Field) time.Time

AsDateTime returns the value of a date-time quoted string field (e.g. INTERNALDATE). The zero value of time.Time is returned if f does not contain a valid date-time string.

func AsMailbox

func AsMailbox(f Field) string

AsMailbox returns the value of a mailbox name field. All valid atoms and strings encoded as quoted UTF-8 or modified UTF-7 are decoded appropriately. The special case-insensitive name "INBOX" is always converted to upper case.

func AsNumber

func AsNumber(f Field) uint32

AsNumber returns the value of a numeric field. Zero is returned if TypeOf(f) != Number.

func AsString

func AsString(f Field) string

AsString returns the value of an astring (string or atom) field. Quoted strings are decoded to their original representation. An empty string is returned if TypeOf(f)&(Atom|QuotedString|LiteralString) == 0 or the string is invalid.

func ByeFilter

func ByeFilter(_ *Command, rsp *Response) bool

ByeFilter accepts the response if rsp.Status is BYE.

func FetchFilter

func FetchFilter(cmd *Command, rsp *Response) bool

FetchFilter accepts FETCH and STORE command responses by matching message sequence numbers or UIDs, depending on the command type. UID matches are more exact because there is no risk of mistaking unilateral server data (e.g. an unsolicited flags update) for command data.

func NameFilter

func NameFilter(cmd *Command, rsp *Response) bool

NameFilter accepts the response if rsp.Label matches the command name.

func Quote

func Quote(s string, utf8quoted bool) string

Quote returns the input as a quoted string for use in a command. An empty string is returned if the input cannot be quoted and must be sent as a literal. Setting utf8quoted to true indicates server support for utf8-quoted string format, as described in RFC 5738. The utf8-quoted form will be used only if the input contains non-ASCII characters.

func QuoteBytes

func QuoteBytes(s []byte, utf8quoted bool) []byte

QuoteBytes returns the input as a quoted byte slice. Nil is returned if the input cannot be quoted and must be sent as a literal.

func Quoted

func Quoted(f Field) bool

Quoted returns true if a string or []byte appears to contain a quoted string, based on the presence of surrounding double quotes. The string contents are not checked, so it may still contain illegal characters or escape sequences. The string may be encoded in utf8-quoted format, as described in RFC 5738.

func QuotedUTF8

func QuotedUTF8(f Field) bool

QuotedUTF8 returns true if a string or []byte appears to contain a quoted string encoded in utf8-quoted format.

func UTF7Decode

func UTF7Decode(u string) (s string, err error)

UTF7Decode converts a string from modified UTF-7 encoding to UTF-8.

func UTF7DecodeBytes

func UTF7DecodeBytes(u []byte) (s []byte, err error)

UTF7DecodeBytes converts a byte slice from modified UTF-7 encoding to UTF-8.

func UTF7Encode

func UTF7Encode(s string) string

UTF7Encode converts a string from UTF-8 encoding to modified UTF-7. This encoding is used by the Mailbox International Naming Convention (RFC 3501 section 5.1.3). Invalid UTF-8 byte sequences are replaced by the Unicode replacement code point (U+FFFD).

func UTF7EncodeBytes

func UTF7EncodeBytes(s []byte) []byte

UTF7EncodeBytes converts a byte slice from UTF-8 encoding to modified UTF-7.

func Unquote

func Unquote(q string) (s string, ok bool)

Unquote is the reverse of Quote. An empty string is returned and ok is set to false if the input is not a valid quoted string. RFC 3501 specifications are relaxed to accept all valid UTF-8 encoded strings with or without the use of utf8-quoted format (RFC 5738). Rules disallowing the use of NUL, CR, and LF characters still apply. All (and only) double quote and backslash characters must be escaped with a backslash.

func UnquoteBytes

func UnquoteBytes(q []byte) (s []byte, ok bool)

UnquoteBytes is the reverse of QuoteBytes.

Types

type Client

type Client struct {
	// FIFO queue for unilateral server data. The first response is the server
	// greeting. Subsequent responses are those that were rejected by all active
	// command filters. Commands documented as expecting "no specific responses"
	// (e.g. NOOP) use nil filters by default, which reject all responses.
	Data []*Response

	// Set of current server capabilities. It is updated automatically anytime
	// new capabilities are received, which could be in a data response or a
	// status response code.
	Caps map[string]bool

	// Status of the selected mailbox. It is set to nil unless the Client is in
	// the Selected state. The fields are updated automatically as the server
	// sends solicited and unsolicited status updates.
	Mailbox *MailboxStatus

	// Execution parameters of known commands. Client.Send will return an error
	// if an attempt is made to execute a command whose name does not appear in
	// this map. The server may not support all commands known to the client.
	CommandConfig map[string]*CommandConfig
	// contains filtered or unexported fields
}

Client manages a single connection to an IMAP server.

Example
package main

import (
	"bytes"
	"fmt"
	"net/mail"
	"time"

	"github.com/mxk/go-imap/imap"
)

func main() {
	//
	// Note: most of error handling code is omitted for brevity
	//
	var (
		c   *imap.Client
		cmd *imap.Command
		rsp *imap.Response
	)

	// Connect to the server
	c, _ = imap.Dial("imap.example.com")

	// Remember to log out and close the connection when finished
	defer c.Logout(30 * time.Second)

	// Print server greeting (first response in the unilateral server data queue)
	fmt.Println("Server says hello:", c.Data[0].Info)
	c.Data = nil

	// Enable encryption, if supported by the server
	if c.Caps["STARTTLS"] {
		c.StartTLS(nil)
	}

	// Authenticate
	if c.State() == imap.Login {
		c.Login("user@example.com", "mysupersecretpassword")
	}

	// List all top-level mailboxes, wait for the command to finish
	cmd, _ = imap.Wait(c.List("", "%"))

	// Print mailbox information
	fmt.Println("\nTop-level mailboxes:")
	for _, rsp = range cmd.Data {
		fmt.Println("|--", rsp.MailboxInfo())
	}

	// Check for new unilateral server data responses
	for _, rsp = range c.Data {
		fmt.Println("Server data:", rsp)
	}
	c.Data = nil

	// Open a mailbox (synchronous command - no need for imap.Wait)
	c.Select("INBOX", true)
	fmt.Print("\nMailbox status:\n", c.Mailbox)

	// Fetch the headers of the 10 most recent messages
	set, _ := imap.NewSeqSet("")
	if c.Mailbox.Messages >= 10 {
		set.AddRange(c.Mailbox.Messages-9, c.Mailbox.Messages)
	} else {
		set.Add("1:*")
	}
	cmd, _ = c.Fetch(set, "RFC822.HEADER")

	// Process responses while the command is running
	fmt.Println("\nMost recent messages:")
	for cmd.InProgress() {
		// Wait for the next response (no timeout)
		c.Recv(-1)

		// Process command data
		for _, rsp = range cmd.Data {
			header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"])
			if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil {
				fmt.Println("|--", msg.Header.Get("Subject"))
			}
		}
		cmd.Data = nil

		// Process unilateral server data
		for _, rsp = range c.Data {
			fmt.Println("Server data:", rsp)
		}
		c.Data = nil
	}

	// Check command completion status
	if rsp, err := cmd.Result(imap.OK); err != nil {
		if err == imap.ErrAborted {
			fmt.Println("Fetch command aborted")
		} else {
			fmt.Println("Fetch error:", rsp.Info)
		}
	}
}
Output:

func Dial

func Dial(addr string) (c *Client, err error)

Dial returns a new Client connected to an IMAP server at addr.

func DialLocalAddr

func DialLocalAddr(laddr string, addr string) (c *Client, err error)

Dial using a specific local address

func DialLocalAddrTLS

func DialLocalAddrTLS(laddr string, addr string, config *tls.Config) (c *Client, err error)

func DialTLS

func DialTLS(addr string, config *tls.Config) (c *Client, err error)

DialTLS returns a new Client connected to an IMAP server at addr using the specified config for encryption.

func NewClient

func NewClient(conn net.Conn, host string, timeout time.Duration) (c *Client, err error)

NewClient returns a new Client instance connected to an IMAP server via conn. The function waits for the server to send a greeting message, and then requests server capabilities if they weren't included in the greeting. An error is returned if either operation fails or does not complete before the timeout, which must be positive to have any effect. If an error is returned, it is the caller's responsibility to close the connection.

func (*Client) Append

func (c *Client) Append(mbox string, flags FlagSet, idate *time.Time, msg Literal) (cmd *Command, err error)

Append appends the literal argument as a new message to the end of the specified destination mailbox. Flags and internal date arguments are optional and may be set to nil.

func (*Client) Auth

func (c *Client) Auth(a SASL) (cmd *Command, err error)

Auth performs SASL challenge-response authentication. The client automatically requests new capabilities if authentication is successful.

This command is synchronous.

func (*Client) Capability

func (c *Client) Capability() (cmd *Command, err error)

Capability requests a listing of capabilities supported by the server. The client automatically requests capabilities when the connection is first established, after a successful STARTTLS command, and after user authentication, making it unnecessary to call this method directly in most cases. The current capabilities are available in c.Caps.

This command is synchronous.

func (*Client) Check

func (c *Client) Check() (cmd *Command, err error)

Check requests a checkpoint of the currently selected mailbox. A checkpoint is an implementation detail of the server and may be equivalent to a NOOP.

func (*Client) Close

func (c *Client) Close(expunge bool) (cmd *Command, err error)

Close closes the currently selected mailbox, returning the client to the authenticated state. If expunge is true, all messages marked for deletion are permanently removed from the mailbox.

If expunge is false and UNSELECT capability is not advertised, the client issues the EXAMINE command with a non-existent mailbox name. This closes the current mailbox without expunging it, but the "successful" command completion status will be NO instead of OK.

This command is synchronous.

func (*Client) CompressDeflate

func (c *Client) CompressDeflate(level int) (cmd *Command, err error)

CompressDeflate enables data compression using the DEFLATE algorithm. The compression level must be between -1 and 9 (see compress/flate). See RFC 4978 for additional information.

This command is synchronous.

func (*Client) Copy

func (c *Client) Copy(seq *SeqSet, mbox string) (cmd *Command, err error)

Copy copies the specified message(s) to the end of the specified destination mailbox.

func (*Client) Create

func (c *Client) Create(mbox string) (cmd *Command, err error)

Create creates a new mailbox on the server.

func (*Client) Delete

func (c *Client) Delete(mbox string) (cmd *Command, err error)

Delete permanently removes a mailbox and all of its contents from the server.

func (*Client) Enable

func (c *Client) Enable(caps ...string) (cmd *Command, err error)

Enable takes a list of capability names and requests the server to enable the named extensions. See RFC 5161 for additional information.

This command is synchronous.

func (*Client) Expunge

func (c *Client) Expunge(uids *SeqSet) (cmd *Command, err error)

Expunge permanently removes all messages that have the \Deleted flag set from the currently selected mailbox. If UIDPLUS capability is advertised, the operation can be restricted to messages with specific UIDs by specifying a non-nil uids argument.

func (*Client) Fetch

func (c *Client) Fetch(seq *SeqSet, items ...string) (cmd *Command, err error)

Fetch retrieves data associated with the specified message(s) in the mailbox. See RFC 3501 section 6.4.5 for a list of all valid message data items and macros.

func (*Client) GetQuota

func (c *Client) GetQuota(root string, quota ...*Quota) (cmd *Command, err error)

GetQuota returns the quota root's resource usage and limits. See RFC 2087 for additional information.

func (*Client) GetQuotaRoot

func (c *Client) GetQuotaRoot(mbox string) (cmd *Command, err error)

GetQuotaRoot returns the list of quota roots for the specified mailbox, and the resource usage and limits for each quota root. See RFC 2087 for additional information.

func (*Client) ID

func (c *Client) ID(info ...string) (cmd *Command, err error)

ID provides client identification information to the server. See RFC 2971 for additional information.

func (*Client) Idle

func (c *Client) Idle() (cmd *Command, err error)

Idle places the client into an idle state where the server is free to send unsolicited mailbox update messages. No other commands are allowed to run while the client is idling. Use c.IdleTerm to terminate the command. See RFC 2177 for additional information.

func (*Client) IdleTerm

func (c *Client) IdleTerm() (cmd *Command, err error)

IdleTerm terminates the IDLE command. It returns the same Command instance as the original Idle call.

func (*Client) LSub

func (c *Client) LSub(ref, mbox string) (cmd *Command, err error)

LSub returns a subset of mailbox names from the set of names that the user has declared as being "active" or "subscribed".

func (*Client) List

func (c *Client) List(ref, mbox string) (cmd *Command, err error)

List returns a subset of mailbox names from the complete set of all names available to the client.

See RFC 3501 sections 6.3.8 and 7.2.2, and RFC 2683 for detailed information about the LIST and LSUB commands.

func (Client) Log

func (d Client) Log(mask LogMask, v ...interface{})

Log formats its arguments using default formatting, analogous to Print(), and records the text in the debug log if logging is enabled for the specified mask.

func (Client) Logf

func (d Client) Logf(mask LogMask, format string, v ...interface{})

Logf formats its arguments according to the format, analogous to Printf(), and records the text in the debug log if logging is enabled for the specified mask.

func (*Client) Login

func (c *Client) Login(username, password string) (cmd *Command, err error)

Login performs plaintext username/password authentication. This command is disabled when the server advertises LOGINDISABLED capability. The client automatically requests new capabilities if authentication is successful.

This command is synchronous.

func (Client) Logln

func (d Client) Logln(mask LogMask, v ...interface{})

Logln formats its arguments using default formatting, analogous to Println(), and records the text in the debug log if logging is enabled for the specified mask.

func (*Client) Logout

func (c *Client) Logout(timeout time.Duration) (cmd *Command, err error)

Logout informs the server that the client is done with the connection. This method must be called to close the connection and free all client resources.

A negative timeout allows the client to wait indefinitely for the normal logout sequence to complete. A timeout of 0 causes the connection to be closed immediately without actually sending the LOGOUT command. A positive timeout behaves as expected, returning ErrTimeout if the normal logout sequence is not completed in the allocated time. The connection is always closed when this method returns.

This command is synchronous.

func (*Client) Noop

func (c *Client) Noop() (cmd *Command, err error)

Noop does nothing, but it allows the server to send status updates, which are delivered to the unilateral server data queue (c.Data). It can also be used to reset any inactivity autologout timer on the server.

func (*Client) Quote

func (c *Client) Quote(v interface{}) Field

Quote attempts to represent v, which must be string, []byte, or fmt.Stringer, as a quoted string for use with Client.Send. A literal string representation is used if v cannot be quoted.

func (*Client) Recv

func (c *Client) Recv(timeout time.Duration) error

Recv receives at most one response from the server, updates the client state, and delivers the response to its final destination (c.Data or one of the commands in progress). io.EOF is returned once all responses have been received and the connection is closed.

If the timeout is negative, Recv blocks indefinitely until a response is received or an error is encountered. If the timeout is zero, Recv polls for buffered responses, returning ErrTimeout immediately if none are available. Otherwise, Recv blocks until a response is received or the timeout expires.

func (*Client) Rename

func (c *Client) Rename(old, new string) (cmd *Command, err error)

Rename changes the name of a mailbox.

func (*Client) Search

func (c *Client) Search(spec ...Field) (cmd *Command, err error)

Search searches the mailbox for messages that match the given searching criteria. See RFC 3501 section 6.4.4 for a list of all valid search keys. It is the caller's responsibility to quote strings when necessary. All strings must use UTF-8 encoding.

func (*Client) Select

func (c *Client) Select(mbox string, readonly bool) (cmd *Command, err error)

Select opens a mailbox on the server for read-write or read-only access. The EXAMINE command is used when readonly is set to true. However, even when readonly is false, the server may decide not to give read-write access. The server may also change access while the mailbox is open. The current mailbox status is available from c.Mailbox while the client is in the Selected state.

This command is synchronous.

func (*Client) Send

func (c *Client) Send(name string, fields ...Field) (cmd *Command, err error)

Send issues a new command, returning as soon as the last line is flushed from the send buffer. This may involve waiting for continuation requests if non-synchronizing literals (RFC 2088) are not supported by the server.

This is the raw command interface that does not encode or perform any validation of the supplied fields. It should only be used for implementing new commands that do not change the connection state. For commands already supported by this package, use the provided wrapper methods instead.

func (*Client) SetLiteralReader

func (c *Client) SetLiteralReader(lr LiteralReader) LiteralReader

SetLiteralReader installs a custom LiteralReader implementation into the response receiver pipeline. It returns the previously installed LiteralReader instance.

func (Client) SetLogMask

func (d Client) SetLogMask(mask LogMask) LogMask

SetLogMask enables/disables debug message categories and returns the previous mask.

func (Client) SetLogger

func (d Client) SetLogger(log *log.Logger) *log.Logger

SetLogger sets the destination of debug messages and returns the previous logger.

func (*Client) SetQuota

func (c *Client) SetQuota(root string, quota ...*Quota) (cmd *Command, err error)

SetQuota changes the resource limits of the specified quota root. See RFC 2087 for additional information.

func (*Client) StartTLS

func (c *Client) StartTLS(config *tls.Config) (cmd *Command, err error)

StartTLS enables session privacy protection and integrity checking. The server must advertise STARTTLS capability for this command to be available. The client automatically requests new capabilities if the TLS handshake is successful.

This command is synchronous.

func (*Client) State

func (c *Client) State() ConnState

State returns the current connection state (Login, Auth, Selected, Logout, or Closed). See RFC 3501 page 15 for a state diagram. The caller must continue receiving responses until this method returns Closed (same as c.Recv returning io.EOF). Failure to do so may result in memory leaks.

func (*Client) Status

func (c *Client) Status(mbox string, items ...string) (cmd *Command, err error)

Status requests the status of the indicated mailbox. The currently defined status data items that can be requested are: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, and UNSEEN. All data items are requested by default.

func (*Client) Store

func (c *Client) Store(seq *SeqSet, item string, value Field) (cmd *Command, err error)

Store alters data associated with the specified message(s) in the mailbox.

func (*Client) Subscribe

func (c *Client) Subscribe(mbox string) (cmd *Command, err error)

Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed" mailboxes as returned by the LSUB command.

func (*Client) UIDCopy

func (c *Client) UIDCopy(seq *SeqSet, mbox string) (cmd *Command, err error)

UIDCopy is identical to Copy, but the seq argument is interpreted as containing unique identifiers instead of message sequence numbers.

func (*Client) UIDFetch

func (c *Client) UIDFetch(seq *SeqSet, items ...string) (cmd *Command, err error)

UIDFetch is identical to Fetch, but the seq argument is interpreted as containing unique identifiers instead of message sequence numbers.

func (*Client) UIDSearch

func (c *Client) UIDSearch(spec ...Field) (cmd *Command, err error)

UIDSearch is identical to Search, but the numbers returned in the response are unique identifiers instead of message sequence numbers.

func (*Client) UIDStore

func (c *Client) UIDStore(seq *SeqSet, item string, value Field) (cmd *Command, err error)

UIDStore is identical to Store, but the seq argument is interpreted as containing unique identifiers instead of message sequence numbers.

func (*Client) Unsubscribe

func (c *Client) Unsubscribe(mbox string) (cmd *Command, err error)

Unsubscribe removes the specified mailbox name from the server's set of "active" or "subscribed" mailboxes as returned by the LSUB command.

type Command

type Command struct {
	// FIFO queue for command data. These are the responses that were accepted
	// by this command's filter. New responses are appended to the end as they
	// are received.
	Data []*Response
	// contains filtered or unexported fields
}

Command represents a single command sent to the server.

func Wait

func Wait(cmd *Command, err error) (*Command, error)

Wait is a convenience function for transforming asynchronous commands into synchronous ones. The error is nil if and only if the command is completed with OK status condition. Usage example:

cmd, err := imap.Wait(c.Fetch(...))

func (*Command) Client

func (cmd *Command) Client() *Client

Client returns the Client instance that created this command.

func (*Command) InProgress

func (cmd *Command) InProgress() bool

InProgress returns true until the command completion result is available. No new responses will be appended to cmd.Data after this method returns false.

func (*Command) Name

func (cmd *Command) Name(full bool) string

Name returns the command name. If full == true, the UID prefix is included for UID commands.

func (*Command) Result

func (cmd *Command) Result(expect RespStatus) (rsp *Response, err error)

Result returns the command completion result. The call blocks until the command is no longer in progress. If expect != 0, an error is returned if the completion status is other than expected. ErrAborted is returned if the command execution was interrupted prior to receiving a completion response.

func (*Command) String

func (cmd *Command) String() string

String returns the raw command text without CRLFs or literal data.

func (*Command) Tag

func (cmd *Command) Tag() string

Tag returns the command tag assigned by the Client.

func (*Command) UID

func (cmd *Command) UID() bool

UID returns true if the command is using UIDs instead of message sequence numbers.

type CommandConfig

type CommandConfig struct {
	States    ConnState      // Mask of states in which this command may be issued
	Filter    ResponseFilter // Filter for identifying command responses
	Exclusive bool           // Exclusive Client access flag
}

CommandConfig specifies command execution parameters.

type ConnState

type ConnState uint8

ConnState represents client connection states. See RFC 3501 page 15 for a state diagram.

func (ConnState) GoString

func (v ConnState) GoString() string

func (ConnState) String

func (v ConnState) String() string

type Field

type Field interface{}

Field represents a single data item in a command or response. Fields are separated from one another by a single space. Field slices represent parenthesized lists.

func AsList

func AsList(f Field) []Field

AsList returns the value of a parenthesized list. Nil is returned if TypeOf(f) != List.

type FieldMap

type FieldMap map[string]Field

FieldMap represents key-value pairs of data items, such as those returned in a FETCH response. Key names are atoms converted to upper case.

func AsFieldMap

func AsFieldMap(f Field) FieldMap

AsFieldMap returns a map of key-value pairs extracted from a parenthesized list. Nil is returned if TypeOf(f) != List, the number of fields in the list is not even, or one of the keys is not an Atom.

func (FieldMap) String

func (fm FieldMap) String() string

type FieldType

type FieldType uint8

FieldType describes the data type of a single response field.

func TypeOf

func TypeOf(f Field) FieldType

TypeOf returns the field data type. Valid types are Atom, Number, QuotedString, LiteralString, List, Bytes, and NIL. Zero is returned for unknown data types.

func (FieldType) GoString

func (v FieldType) GoString() string

func (FieldType) String

func (v FieldType) String() string

type FlagSet

type FlagSet map[string]bool

FlagSet represents the flags enabled for a single mailbox or message. The map values are always set to true; a flag must be deleted from the map to indicate that it is not enabled.

func AsFlagSet

func AsFlagSet(f Field) FlagSet

AsFlags returns a set of flags extracted from a parenthesized list. The function does not check every atom for the leading backslash, because it is not permitted in user-defined flags (keywords). Nil is returned if TypeOf(f) != List or one of the fields is not an atom.

func NewFlagSet

func NewFlagSet(flags ...string) FlagSet

NewFlagSet returns a new flag set with the specified flags enabled.

func (FlagSet) Replace

func (fs FlagSet) Replace(f Field)

Replace removes all existing flags from the set and inserts new ones.

func (FlagSet) String

func (fs FlagSet) String() string

type Literal

type Literal interface {
	// WriteTo writes Info().Length bytes to the Writer w. For the default
	// Literal implementation, use AsString or AsBytes field functions to access
	// the incoming data directly without copying everything through a Writer.
	io.WriterTo

	// Info returns information about the contained literal.
	Info() LiteralInfo
}

Literal represents a single incoming or outgoing literal string, as described in RFC 3501 section 4.3. Incoming literals are constructed by a LiteralReader. The default implementation saves all literals to memory. A custom LiteralReader implementation can save literals directly to files. This could be advantageous when the client is receiving message bodies containing attachments several MB in size. Likewise, a custom Literal implementation can transmit outgoing literals by reading directly from files or other data sources.

func NewLiteral

func NewLiteral(b []byte) Literal

NewLiteral creates a new literal string from a byte slice. The Literal will point to the same underlying array as the original slice, so it is not safe to modify the array data until the literal has been sent in a command. It is the caller's responsibility to create a copy of the data, if needed.

func NewLiteral8

func NewLiteral8(b []byte) Literal

NewLiteral8 creates a new binary literal string from a byte slice. This literal is sent using the literal8 syntax, as described in RFC 3516. The server must advertise "BINARY" capability for such literals to be accepted.

type LiteralInfo

type LiteralInfo struct {
	Len uint32 // Literal octet count
	Bin bool   // RFC 3516 literal8 binary format flag
}

LiteralInfo describes the attributes of an incoming or outgoing literal string.

type LiteralReader

type LiteralReader interface {
	ReadLiteral(r io.Reader, i LiteralInfo) (Literal, error)
}

LiteralReader is the interface for receiving literal strings from the server.

ReadLiteral reads exactly i.Length bytes from r into a new literal. It must return a Literal instance even when i.Length == 0 (empty string). A return value of (nil, nil) is invalid.

type LogMask

type LogMask uint8

LogMask represents the categories of debug messages that can be logged by the Client.

func (LogMask) GoString

func (v LogMask) GoString() string

func (LogMask) String

func (v LogMask) String() string

type MailboxInfo

type MailboxInfo struct {
	Attrs FlagSet // Mailbox attributes (e.g. `\Noinferiors`, `\Noselect`)
	Delim string  // Hierarchy delimiter (empty string == NIL, i.e. flat name)
	Name  string  // Mailbox name decoded to UTF-8
}

MailboxInfo represents the mailbox attributes returned in a LIST or LSUB response.

type MailboxStatus

type MailboxStatus struct {
	Name         string  // Mailbox name
	ReadOnly     bool    // Mailbox read/write access (client-only)
	Flags        FlagSet // Defined flags in the mailbox (client-only)
	PermFlags    FlagSet // Flags that the client can change permanently (client-only)
	Messages     uint32  // Number of messages in the mailbox
	Recent       uint32  // Number of messages with the \Recent flag set
	Unseen       uint32  // Sequence number of the first unseen message
	UIDNext      uint32  // The next unique identifier value
	UIDValidity  uint32  // The unique identifier validity value
	UIDNotSticky bool    // UIDPLUS extension (client-only)
}

MailboxStatus represents the mailbox status information returned in a STATUS response. It is also used by the Client to keep an updated view of the currently selected mailbox. Fields that are only set by the Client are marked as client-only.

func (*MailboxStatus) String

func (m *MailboxStatus) String() string

type MemoryReader

type MemoryReader struct{}

MemoryReader implements the LiteralReader interface by saving all incoming literals to memory.

func (MemoryReader) ReadLiteral

func (MemoryReader) ReadLiteral(r io.Reader, i LiteralInfo) (Literal, error)

type MessageInfo

type MessageInfo struct {
	Attrs        FieldMap  // All returned attributes
	Seq          uint32    // Message sequence number
	UID          uint32    // Unique identifier (optional in non-UID FETCH)
	Flags        FlagSet   // Flags that are set for this message (optional)
	InternalDate time.Time // Internal to the server message timestamp (optional)
	Size         uint32    // Message size in bytes (optional)
}

MessageInfo represents the message attributes returned in a FETCH response. The values of attributes marked optional are valid only if that attribute also appears in Attrs (e.g. UID is valid if and only if Attrs["UID"] != nil). These attributes are extracted from Attrs purely for convenience.

type MockServer

type MockServer interface {
	Compressed() bool
	Encrypted() bool
	Closed() bool
	ReadLine() (line []byte, err error)
	WriteLine(line []byte) error
	Read(p []byte) (n int, err error)
	Write(p []byte) (n int, err error)
	Flush() error
	EnableDeflate(level int) error
	EnableTLS(config *tls.Config) error
	Close(flush bool) error
}

MockServer is an internal type exposed for use by the mock package.

func NewMockServer

func NewMockServer(conn net.Conn) MockServer

NewMockServer is an internal function exposed for use by the mock package.

type NotAvailableError

type NotAvailableError string

NotAvailableError is returned when the requested command, feature, or capability is not supported by the client and/or server. The error may be temporary. For example, servers should disable the LOGIN command by advertising LOGINDISABLED capability while the connection is unencrypted. Enabling encryption via STARTTLS should allow the use of LOGIN.

func (NotAvailableError) Error

func (err NotAvailableError) Error() string

type ParserError

type ParserError struct {
	Info   string // Short message explaining the problem
	Line   []byte // Full or partial response line, starting with the tag
	Offset int    // Parser offset, starting at 0
}

ParserError indicates a problem with the server response format. This could be the result of an unsupported extension or nonstandard server behavior.

func (*ParserError) Error

func (err *ParserError) Error() string

type ProtocolError

type ProtocolError struct {
	Info string // Short message explaining the problem
	Line []byte // Full or partial command/response line
}

ProtocolError indicates a low-level problem with the data being sent by the client or server.

func (*ProtocolError) Error

func (err *ProtocolError) Error() string

type Quota

type Quota struct {
	Resource string // Resource name (e.g. STORAGE, MESSAGE)
	Usage    uint32 // Current usage (in units of 1024 octets for STORAGE)
	Limit    uint32 // Current limit
}

Quota represents a single resource limit on a mailbox quota root returned in a QUOTA response, as described in RFC 2087.

type RespStatus

type RespStatus uint8

RespStatus is the code sent in status messages to indicate success, failure, or changes in the connection state.

func (RespStatus) GoString

func (v RespStatus) GoString() string

func (RespStatus) String

func (v RespStatus) String() string

type RespType

type RespType uint8

RespType indicates the type of information contained in the response.

func (RespType) GoString

func (v RespType) GoString() string

func (RespType) String

func (v RespType) String() string

type Response

type Response struct {
	// Order in which this response was received, starting at 1 for the server
	// greeting.
	Order int64

	// Original response line from which this Response object was constructed.
	// Literal strings and CRLFs are omitted.
	Raw []byte

	// All literal strings in the order they were received. Do not assume that a
	// FETCH request for BODY[], for example, will return exactly one literal
	// with the requested data. Use the decoder methods, or navigate Fields
	// according to the response format, to get the desired information.
	Literals []Literal

	// Response tag ("*", "+", or command tag).
	Tag string

	// Response type (Status, Data, Continue, or Done).
	Type RespType

	// Status condition if Type is Status or Done (OK, NO, BAD, PREAUTH, or
	// BYE). Only OK, NO, and BAD may be used in tagged (Done) responses.
	Status RespStatus

	// Human-readable text portion of a Status, Continue, or Done response, or
	// the original Base64 text of a challenge-response authentication request.
	Info string

	// First atom in Fields (usually index 0 or 1) converted to upper case. This
	// determines the format of Fields, as described in RFC 3501 section 7. A
	// Continue response containing Base64 data is labeled "BASE64".
	Label string

	// Data or response code fields extracted by the parser. For a Data
	// response, this is everything after the "*" tag. For a Status or Done
	// response, this is the response code (if there is one). For a Continue
	// response with a "BASE64" Label, Fields[0] is the decoded byte slice.
	Fields []Field

	// Cached decoder output. This is used by the decoder methods to avoid
	// traversing Fields multiple times. User code should not modify or access
	// this field except when writing a custom decoder (see response.go for
	// examples).
	Decoded interface{}
}

Response represents a single status, data, or command continuation response. All response types are parsed into the same general format, which can then be decoded to more specific representations either by calling the provided decoder methods, or by manually navigating Fields and other attributes. Here are a few examples of the parser output:

S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI
S: * OK [UNSEEN 12] Message 12 is first unseen
S: A142 OK [read-write] SELECT completed

Response objects:

&imap.Response{
	Raw:    []byte("* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI"),
	Tag:    "*",
	Type:   imap.Data,
	Label:  "CAPABILITY",
	Fields: []Field{"CAPABILITY", "IMAP4rev1", "STARTTLS", "AUTH=GSSAPI"},
}
&imap.Response{
	Raw:    []byte("* OK [UNSEEN 12] Message 12 is first unseen"),
	Tag:    "*",
	Type:   imap.Status,
	Status: imap.OK,
	Info:   "Message 12 is first unseen",
	Label:  "UNSEEN",
	Fields: []Field{"UNSEEN", uint32(12)},
}
&imap.Response{
	Raw:    []byte("A142 OK [read-write] SELECT completed"),
	Tag:    "A142",
	Type:   imap.Done,
	Status: imap.OK,
	Info:   "SELECT completed",
	Label:  "READ-WRITE",
	Fields: []Field{"read-write"},
}

func (*Response) Challenge

func (rsp *Response) Challenge() []byte

Challenge returns the decoded Base64 data from a continuation request sent during challenge-response authentication.

func (*Response) MailboxFlags

func (rsp *Response) MailboxFlags() FlagSet

MailboxFlags returns a FlagSet extracted from a FLAGS or PERMANENTFLAGS response. Note that FLAGS is a Data response, while PERMANENTFLAGS is Status.

func (*Response) MailboxInfo

func (rsp *Response) MailboxInfo() *MailboxInfo

MailboxInfo returns the mailbox attributes extracted from a LIST or LSUB response.

func (*Response) MailboxStatus

func (rsp *Response) MailboxStatus() *MailboxStatus

MailboxStatus returns the mailbox status information extracted from a STATUS response.

func (*Response) MessageInfo

func (rsp *Response) MessageInfo() *MessageInfo

MessageInfo returns the message attributes extracted from a FETCH response.

func (*Response) Quota

func (rsp *Response) Quota() (root string, quota []*Quota)

Quota returns the resource quotas extracted from a QUOTA response.

func (*Response) QuotaRoot

func (rsp *Response) QuotaRoot() (mbox string, roots []string)

QuotaRoot returns the mailbox name and associated quota roots from a QUOTAROOT response.

func (*Response) SearchResults

func (rsp *Response) SearchResults() []uint32

SearchResults returns a slice of message sequence numbers or UIDs extracted from a SEARCH response.

func (*Response) String

func (rsp *Response) String() string

String returns the raw text from which this Response object was constructed. Literal strings and CRLFs are omitted.

func (*Response) Value

func (rsp *Response) Value() uint32

Value returns the first unsigned 32-bit integer in Fields without descending into parenthesized lists. This decoder is primarily intended for Status/Data responses labeled EXISTS, RECENT, EXPUNGE, UNSEEN, UIDNEXT, and UIDVALIDITY.

type ResponseError

type ResponseError struct {
	*Response
	Reason string
}

ResponseError wraps a Response pointer for use in an error context, such as when a command fails with a NO or BAD status condition. For Status and Done response types, the value of Response.Info may be presented to the user. Reason provides additional information about the cause of the error.

func (ResponseError) Error

func (rsp ResponseError) Error() string

type ResponseFilter

type ResponseFilter func(cmd *Command, rsp *Response) bool

ResponseFilter defines the signature of functions that determine response ownership. The function returns true if rsp belongs to cmd. A nil filter rejects all responses. A response that is rejected by all active filters is considered to be unilateral server data.

func LabelFilter

func LabelFilter(labels ...string) ResponseFilter

LabelFilter returns a new filter configured to accept responses with the specified labels.

type SASL

type SASL interface {
	// Start begins SASL authentication with the server. It returns the
	// authentication mechanism name and "initial response" data (if required by
	// the selected mechanism). A non-nil error causes the client to abort the
	// authentication attempt.
	//
	// A nil ir value is different from a zero-length value. The nil value
	// indicates that the selected mechanism does not use an initial response,
	// while a zero-length value indicates an empty initial response, which must
	// be sent to the server.
	Start(s *ServerInfo) (mech string, ir []byte, err error)

	// Next continues challenge-response authentication. A non-nil error causes
	// the client to abort the authentication attempt.
	Next(challenge []byte) (response []byte, err error)
}

SASL is the interface for performing challenge-response authentication.

func ExternalAuth

func ExternalAuth(identity string) SASL

ExternalAuth returns an implementation of the EXTERNAL authentication mechanism, as described in RFC 4422. Authorization identity may be left blank to indicate that the client is requesting to act as the identity associated with the authentication credentials.

func PlainAuth

func PlainAuth(username, password, identity string) SASL

PlainAuth returns an implementation of the PLAIN authentication mechanism, as described in RFC 4616. Authorization identity may be left blank to indicate that it is the same as the username.

type SeqSet

type SeqSet struct {
	// contains filtered or unexported fields
}

SeqSet is used to represent a set of message sequence numbers or UIDs (see sequence-set ABNF rule). The zero value is an empty set.

func NewSeqSet

func NewSeqSet(set string) (s *SeqSet, err error)

NewSeqSet returns a new SeqSet instance after parsing the set string.

func (*SeqSet) Add

func (s *SeqSet) Add(set string) error

Add inserts new sequence values into the set. The string format is described by RFC 3501 sequence-set ABNF rule. If an error is encountered, all values inserted successfully prior to the error remain in the set.

func (*SeqSet) AddNum

func (s *SeqSet) AddNum(q ...uint32)

AddNum inserts new sequence numbers into the set. The value 0 represents "*".

func (*SeqSet) AddRange

func (s *SeqSet) AddRange(start, stop uint32)

AddRange inserts a new sequence range into the set.

func (*SeqSet) AddSet

func (s *SeqSet) AddSet(t *SeqSet)

AddSet inserts all values from t into s.

func (*SeqSet) Clear

func (s *SeqSet) Clear()

Clear removes all values from the set.

func (SeqSet) Contains

func (s SeqSet) Contains(q uint32) bool

Contains returns true if the non-zero sequence number or UID q is contained in the set. The dynamic range "n:*" contains all q >= n. It is the caller's responsibility to handle the special case where q is the maximum UID in the mailbox and q < n (i.e. the set cannot match UIDs against "*:n" or "*" since it doesn't know what the maximum value is).

func (SeqSet) Dynamic

func (s SeqSet) Dynamic() bool

Dynamic returns true if the set contains "*" or "n:*" values.

func (SeqSet) Empty

func (s SeqSet) Empty() bool

Empty returns true if the sequence set does not contain any values.

func (SeqSet) String

func (s SeqSet) String() string

String returns a sorted representation of all contained sequence values.

type SeqSetError

type SeqSetError string

SeqSetError is used to report problems with the format of a sequence set value.

func (SeqSetError) Error

func (err SeqSetError) Error() string

type ServerInfo

type ServerInfo struct {
	Name string   // Server name
	TLS  bool     // Encryption status
	Auth []string // Supported authentication mechanisms
}

ServerInfo contains information about the IMAP server with which SASL authentication is about to be attempted.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL