Documentation
¶
Overview ¶
Package imap provides IMAP client with connection pooling, rate limiting, and retry.
Index ¶
- Variables
- type Client
- func (c *Client) AccountID() string
- func (c *Client) Close() error
- func (c *Client) CopyMessage(ctx context.Context, mailbox string, uid uint32, destination string) error
- func (c *Client) CreateFolder(ctx context.Context, name string) error
- func (c *Client) DeleteDraft(ctx context.Context, uid uint32) error
- func (c *Client) DeleteMessage(ctx context.Context, mailbox string, uid uint32, permanent bool) error
- func (c *Client) GetAttachment(ctx context.Context, mailbox string, uid uint32, index int) ([]byte, string, error)
- func (c *Client) GetAttachments(ctx context.Context, mailbox string, uid uint32) ([]models.AttachmentInfo, error)
- func (c *Client) GetDraft(ctx context.Context, uid uint32) ([]byte, error)
- func (c *Client) GetMessage(ctx context.Context, mailbox string, uid uint32) (*models.Email, error)
- func (c *Client) IsConnected() bool
- func (c *Client) LastActivity() time.Time
- func (c *Client) ListFolders(ctx context.Context) ([]models.Folder, error)
- func (c *Client) ListMessages(ctx context.Context, mailbox string, limit, offset int, includeBody bool) ([]models.Email, error)
- func (c *Client) ListUnread(ctx context.Context, mailbox string, limit int, includeBody bool) ([]models.Email, error)
- func (c *Client) MarkRead(ctx context.Context, mailbox string, uid uint32, read bool) error
- func (c *Client) MoveMessage(ctx context.Context, mailbox string, uid uint32, destination string) error
- func (c *Client) RateLimitInfo() (int, int, time.Time)
- func (c *Client) SaveDraft(ctx context.Context, literal []byte) (uint32, error)
- func (c *Client) Search(ctx context.Context, mailbox, query, from, to, since, before string, limit int, ...) ([]models.Email, error)
- func (c *Client) SearchByMessageID(ctx context.Context, mailbox, messageID string) ([]models.Email, error)
- func (c *Client) SetFlag(ctx context.Context, mailbox string, uid uint32, flagged bool) error
- type Connector
- type Operations
- type Pool
- func (p *Pool) AccountStatus() []models.AccountStatus
- func (p *Pool) Close(ctx context.Context)
- func (p *Pool) CopyMessage(ctx context.Context, accountID, folder string, uid uint32, dest string) error
- func (p *Pool) CreateFolder(ctx context.Context, accountID, name string) error
- func (p *Pool) DefaultAccountID() string
- func (p *Pool) DeleteDraft(ctx context.Context, accountID string, uid uint32) error
- func (p *Pool) DeleteMessage(ctx context.Context, accountID, folder string, uid uint32, permanent bool) error
- func (p *Pool) Get(ctx context.Context, accountID string) (*Client, error)
- func (p *Pool) GetAttachment(ctx context.Context, accountID, folder string, uid uint32, index int) ([]byte, string, error)
- func (p *Pool) GetAttachments(ctx context.Context, accountID, folder string, uid uint32) ([]models.AttachmentInfo, error)
- func (p *Pool) GetDraft(ctx context.Context, accountID string, uid uint32) ([]byte, error)
- func (p *Pool) GetFolderByRole(ctx context.Context, accountID string, role models.FolderRole) (string, error)
- func (p *Pool) GetMessage(ctx context.Context, accountID, folder string, uid uint32) (*models.Email, error)
- func (p *Pool) ListFolders(ctx context.Context, accountID string) ([]models.Folder, error)
- func (p *Pool) ListMessages(ctx context.Context, accountID, folder string, limit, offset int, ...) ([]models.Email, error)
- func (p *Pool) ListUnread(ctx context.Context, accountID, folder string, limit int, includeBody bool) ([]models.Email, error)
- func (p *Pool) MarkRead(ctx context.Context, accountID, folder string, uid uint32, read bool) error
- func (p *Pool) MoveMessage(ctx context.Context, accountID, folder string, uid uint32, dest string) error
- func (p *Pool) SaveDraft(ctx context.Context, accountID string, msg []byte) (uint32, error)
- func (p *Pool) Search(ctx context.Context, accountID, mailbox, query, from, to, since, before string, ...) ([]models.Email, error)
- func (p *Pool) SearchByMessageID(ctx context.Context, accountID, folder, messageID string) ([]models.Email, error)
- func (p *Pool) SetFlag(ctx context.Context, accountID, folder string, uid uint32, flagged bool) error
Constants ¶
This section is empty.
Variables ¶
var ErrPoolClosed = errors.New("pool is closed")
ErrPoolClosed is returned when Get is called on a closed pool.
var ErrTokenUnchanged = errors.New("OAuth2 token unchanged after refresh")
ErrTokenUnchanged indicates token refresh returned the same token.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps an IMAP connection with rate limiting and retry.
func (*Client) CopyMessage ¶
func (c *Client) CopyMessage( ctx context.Context, mailbox string, uid uint32, destination string, ) error
CopyMessage copies a message to a different mailbox.
func (*Client) CreateFolder ¶
CreateFolder creates a new mailbox.
func (*Client) DeleteDraft ¶
DeleteDraft deletes a draft after sending.
func (*Client) DeleteMessage ¶
func (c *Client) DeleteMessage( ctx context.Context, mailbox string, uid uint32, permanent bool, ) error
DeleteMessage deletes a message (moves to Trash or expunges).
func (*Client) GetAttachment ¶
func (c *Client) GetAttachment( ctx context.Context, mailbox string, uid uint32, index int, ) ([]byte, string, error)
GetAttachment downloads a specific attachment by index, returning raw bytes and filename.
func (*Client) GetAttachments ¶
func (c *Client) GetAttachments( ctx context.Context, mailbox string, uid uint32, ) ([]models.AttachmentInfo, error)
GetAttachments returns attachment metadata for a message by parsing its BODYSTRUCTURE.
func (*Client) GetMessage ¶
func (c *Client) GetMessage( ctx context.Context, mailbox string, uid uint32, ) (*models.Email, error)
GetMessage retrieves a single message by UID.
func (*Client) IsConnected ¶
IsConnected checks if the connection is still alive.
func (*Client) LastActivity ¶
LastActivity returns the time of the last IMAP activity.
func (*Client) ListFolders ¶
ListFolders returns all mailboxes for the account. Uses STATUS-only (no Select) for O(n) roundtrips instead of O(2n).
func (*Client) ListMessages ¶
func (c *Client) ListMessages( ctx context.Context, mailbox string, limit, offset int, includeBody bool, ) ([]models.Email, error)
ListMessages returns messages from a mailbox.
func (*Client) ListUnread ¶
func (c *Client) ListUnread( ctx context.Context, mailbox string, limit int, includeBody bool, ) ([]models.Email, error)
ListUnread returns unread messages from a mailbox.
func (*Client) MoveMessage ¶
func (c *Client) MoveMessage( ctx context.Context, mailbox string, uid uint32, destination string, ) error
MoveMessage moves a message to a different mailbox.
func (*Client) RateLimitInfo ¶
RateLimitInfo returns current rate limit state.
func (*Client) Search ¶
func (c *Client) Search( ctx context.Context, mailbox, query, from, to, since, before string, limit int, includeBody bool, ) ([]models.Email, error)
Search searches for messages matching criteria.
func (*Client) SearchByMessageID ¶
func (c *Client) SearchByMessageID( ctx context.Context, mailbox, messageID string, ) ([]models.Email, error)
SearchByMessageID searches for messages whose Message-ID or References header contains the given message ID. Results are deduplicated and sorted by date ascending.
type Connector ¶
type Connector interface {
// Login performs IMAP LOGIN command (password authentication).
Login(username, password string) error
// Authenticate performs SASL authentication (e.g., XOAUTH2).
Authenticate(saslClient sasl.Client) error
// Select selects a mailbox.
Select(mailbox string, opts *imap.SelectOptions) (*imap.SelectData, error)
// List lists mailboxes matching a pattern and collects all results.
List(ref, pattern string, opts *imap.ListOptions) ([]*imap.ListData, error)
// Status gets mailbox status.
Status(mailbox string, opts *imap.StatusOptions) (*imap.StatusData, error)
// Fetch fetches messages and collects all results into buffers.
Fetch(seqSet imap.NumSet, opts *imap.FetchOptions) ([]*imapclient.FetchMessageBuffer, error)
// Search searches for messages matching criteria.
Search(criteria *imap.SearchCriteria, opts *imap.SearchOptions) (*imap.SearchData, error)
// Move moves messages to a destination mailbox.
Move(uidSet imap.UIDSet, dest string) error
// Copy copies messages to a destination mailbox.
Copy(uidSet imap.UIDSet, dest string) error
// Store modifies message flags, draining all results.
Store(seqSet imap.NumSet, flags *imap.StoreFlags, opts *imap.StoreOptions) error
// Expunge permanently removes messages marked as deleted.
Expunge() error
// Append appends a message literal to a mailbox.
Append(mailbox string, literal []byte, opts *imap.AppendOptions) (*imap.AppendData, error)
// Create creates a new mailbox.
Create(name string, opts *imap.CreateOptions) error
// Close closes the underlying connection.
Close() error
}
Connector abstracts the imapclient.Client methods used by Client. This enables mock injection for unit testing without a live IMAP server.
Design decision: Uses a high-level interface that collapses the two-step `cmd := conn.Method(); result, err := cmd.Wait()` pattern into single calls. This is necessary because *imapclient.XxxCommand types require an active connection for their Wait()/Next()/Collect() methods to function — they cannot be meaningfully constructed in test code.
type Operations ¶
type Operations interface {
ListFolders(ctx context.Context, accountID string) ([]models.Folder, error)
GetFolderByRole(ctx context.Context, accountID string, role models.FolderRole) (string, error)
ListMessages(
ctx context.Context, accountID, folder string,
limit, offset int, includeBody bool,
) ([]models.Email, error)
ListUnread(
ctx context.Context, accountID, folder string,
limit int, includeBody bool,
) ([]models.Email, error)
Search(
ctx context.Context,
accountID, mailbox, query, from, to, since, before string,
limit int, includeBody bool,
) ([]models.Email, error)
GetMessage(ctx context.Context, accountID, folder string, uid uint32) (*models.Email, error)
SearchByMessageID(ctx context.Context, accountID, folder, messageID string) ([]models.Email, error)
GetAttachments(ctx context.Context, accountID, folder string, uid uint32) ([]models.AttachmentInfo, error)
GetAttachment(ctx context.Context, accountID, folder string, uid uint32, index int) ([]byte, string, error)
MoveMessage(ctx context.Context, accountID, folder string, uid uint32, dest string) error
CopyMessage(ctx context.Context, accountID, folder string, uid uint32, dest string) error
DeleteMessage(
ctx context.Context, accountID, folder string,
uid uint32, permanent bool,
) error
MarkRead(ctx context.Context, accountID, folder string, uid uint32, read bool) error
SetFlag(ctx context.Context, accountID, folder string, uid uint32, flagged bool) error
CreateFolder(ctx context.Context, accountID, name string) error
SaveDraft(ctx context.Context, accountID string, msg []byte) (uint32, error)
GetDraft(ctx context.Context, accountID string, uid uint32) ([]byte, error)
DeleteDraft(ctx context.Context, accountID string, uid uint32) error
AccountStatus() []models.AccountStatus
DefaultAccountID() string
}
Operations defines the IMAP operations interface. Tool handlers accept this interface instead of concrete *Pool, enabling mock injection for unit tests.
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool manages IMAP connections for multiple accounts.
Every public method that performs I/O calls wg.Add(1)/defer wg.Done() to track in-flight operations. Close() sets the closing flag and waits on wg before tearing down connections — this ensures clean shutdown without interrupting active IMAP commands mid-operation.
func (*Pool) AccountStatus ¶
func (p *Pool) AccountStatus() []models.AccountStatus
AccountStatus returns connection status for all accounts.
func (*Pool) Close ¶
Close gracefully shuts down the pool. It rejects new Get calls, waits for in-flight operations to complete (or until ctx is cancelled), then closes all connections. On timeout, connections are force-closed to unblock hung operations.
func (*Pool) CopyMessage ¶
func (p *Pool) CopyMessage( ctx context.Context, accountID, folder string, uid uint32, dest string, ) error
CopyMessage copies a message to a different mailbox for the given account.
func (*Pool) CreateFolder ¶
CreateFolder creates a new mailbox for the given account.
func (*Pool) DefaultAccountID ¶
DefaultAccountID returns the configured default account ID.
func (*Pool) DeleteDraft ¶
DeleteDraft deletes a draft for the given account.
func (*Pool) DeleteMessage ¶
func (p *Pool) DeleteMessage( ctx context.Context, accountID, folder string, uid uint32, permanent bool, ) error
DeleteMessage deletes a message for the given account.
func (*Pool) Get ¶
Get returns an IMAP client for the given account, creating one if needed. The pool lock is NOT held during client creation (network I/O), so concurrent requests for different accounts do not block each other. Returns ErrPoolClosed if the pool is shutting down.
func (*Pool) GetAttachment ¶
func (p *Pool) GetAttachment( ctx context.Context, accountID, folder string, uid uint32, index int, ) ([]byte, string, error)
GetAttachment downloads a specific attachment by index, returning raw bytes and filename.
func (*Pool) GetAttachments ¶
func (p *Pool) GetAttachments( ctx context.Context, accountID, folder string, uid uint32, ) ([]models.AttachmentInfo, error)
GetAttachments returns attachment metadata for a message.
func (*Pool) GetFolderByRole ¶
func (p *Pool) GetFolderByRole( ctx context.Context, accountID string, role models.FolderRole, ) (string, error)
GetFolderByRole resolves a folder role to a mailbox name for the given account.
func (*Pool) GetMessage ¶
func (p *Pool) GetMessage( ctx context.Context, accountID, folder string, uid uint32, ) (*models.Email, error)
GetMessage retrieves a single message by UID for the given account.
func (*Pool) ListFolders ¶
ListFolders returns all mailboxes for the given account.
func (*Pool) ListMessages ¶
func (p *Pool) ListMessages( ctx context.Context, accountID, folder string, limit, offset int, includeBody bool, ) ([]models.Email, error)
ListMessages returns messages from a mailbox for the given account.
func (*Pool) ListUnread ¶
func (p *Pool) ListUnread( ctx context.Context, accountID, folder string, limit int, includeBody bool, ) ([]models.Email, error)
ListUnread returns unread messages from a mailbox for the given account.
func (*Pool) MarkRead ¶
func (p *Pool) MarkRead( ctx context.Context, accountID, folder string, uid uint32, read bool, ) error
MarkRead marks a message as read or unread for the given account.
func (*Pool) MoveMessage ¶
func (p *Pool) MoveMessage( ctx context.Context, accountID, folder string, uid uint32, dest string, ) error
MoveMessage moves a message to a different mailbox for the given account.
func (*Pool) Search ¶
func (p *Pool) Search( ctx context.Context, accountID, mailbox, query, from, to, since, before string, limit int, includeBody bool, ) ([]models.Email, error)
Search searches for messages matching criteria for the given account.