Documentation
¶
Overview ¶
Package client wraps go-imap with reconnect logic and higher-level helpers.
Index ¶
- type Client
- func (c *Client) AppendMessage(ctx context.Context, folder string, msg *imap.Message) error
- func (c *Client) Cancel()
- func (c *Client) CreateMailbox(ctx context.Context, name string) (bool, error)
- func (c *Client) FetchMessageIDSet(ctx context.Context, folder string) (map[string]struct{}, error)
- func (c *Client) FetchMessageMap(ctx context.Context, folder string) (map[string]uint32, uint64, error)
- func (c *Client) GetDelimiter() string
- func (c *Client) ListMailboxes(ctx context.Context) ([]*MailboxInfo, error)
- func (c *Client) ListSubfolders(ctx context.Context, folder, delimiter string) ([]string, error)
- func (c *Client) Logout() error
- func (c *Client) MailboxExists(ctx context.Context, name string) (bool, error)
- func (c *Client) SetPrefix(p string)
- func (c *Client) SetProgressTracker(t ProgressTracker)
- func (c *Client) SetProgressWriter(pw ProgressWriter)
- func (c *Client) StreamMessagesByUIDs(ctx context.Context, folder string, uids []uint32, ...) error
- type ErrClass
- type MailboxInfo
- type Options
- type ProgressTracker
- type ProgressWriter
- type Provider
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps a go-imap client with reconnect/retry logic.
The underlying *imapclient.Client is held in an atomic.Pointer so that Reconnect can swap it without racing with the watcher goroutine spawned by withCancel. All IMAP operations go through safeCall, which receives the current client as an argument; on a transient error it transparently reconnects and retries the closure once with the fresh client.
func New ¶
New establishes a connection and logs into the IMAP server.
ctx is used only for the initial dial and login; once Client is returned, reconnects are driven by the internal cancellation channel (closed by Cancel) so that long-running consumers do not need to keep the original context alive.
func (*Client) AppendMessage ¶
AppendMessage uploads a single message to the destination folder.
The body is streamed straight from the *imap.Message (which already holds it in memory) into the APPEND literal — no extra io.ReadAll buffer. The trade-off is that the literal is not replayable: a transient network error mid-APPEND cannot be retried, because the body has been partially sent. That's acceptable for our flow: the next sync run is idempotent (the Message-Id diff will pick up the message again), and avoiding the second in-memory copy halves peak RAM with multi-MB attachments and 10 workers.
func (*Client) Cancel ¶ added in v1.0.4
func (c *Client) Cancel()
Cancel marks the client as canceled and terminates the underlying connection. It is safe to call multiple times.
func (*Client) CreateMailbox ¶
CreateMailbox ensures the destination folder (and any missing parents) exist on the server. Returns true when a new mailbox was created on this call, false when it already existed.
func (*Client) FetchMessageIDSet ¶ added in v1.1.0
FetchMessageIDSet returns the set of Message-Ids in folder, dropping the UIDs. Convenience wrapper around FetchMessageMap for the destination side of a sync where UIDs are irrelevant.
func (*Client) FetchMessageMap ¶ added in v1.1.0
func (c *Client) FetchMessageMap(ctx context.Context, folder string) (map[string]uint32, uint64, error)
FetchMessageMap returns Message-Id → UID for every message in folder, plus the sum of RFC822.SIZE across all messages.
One pass over the folder yields all three pieces. Messages without a usable Message-Id are counted and reported once via the progress writer — without that header the diff has no key to match on, so they cannot be tracked across servers and will be silently skipped. The folder-wide size total is used by callers (sync preview) to estimate transfer volume; messages without a Message-Id still contribute to the total because they exist on the wire.
The returned map is suitable both as the source side of a Message-Id diff and (with UIDs ignored) as the destination side; callers that only need the keys can use FetchMessageIDSet for a slightly thinner allocation.
func (*Client) GetDelimiter ¶ added in v1.0.1
GetDelimiter returns the cached hierarchy delimiter for this server.
func (*Client) ListMailboxes ¶
func (c *Client) ListMailboxes(ctx context.Context) ([]*MailboxInfo, error)
ListMailboxes fetches all folders plus lightweight statistics for each.
func (*Client) ListSubfolders ¶ added in v1.0.4
ListSubfolders returns subfolders of folder, derived from the cached mailbox list. delimiter is used to compute the prefix; when empty the client's cached server delimiter is substituted.
func (*Client) MailboxExists ¶ added in v1.1.0
MailboxExists reports whether a mailbox with the given name exists on the server. Backed by the per-Client mailbox cache so that the many existence checks done at planning time amortize to a single LIST.
func (*Client) SetProgressTracker ¶
func (c *Client) SetProgressTracker(t ProgressTracker)
SetProgressTracker sets an optional progress tracker for updating progress. Safe for concurrent use; see SetProgressWriter.
func (*Client) SetProgressWriter ¶
func (c *Client) SetProgressWriter(pw ProgressWriter)
SetProgressWriter sets the progress writer for logging. Safe to call from any goroutine, including while another goroutine is using the writer.
func (*Client) StreamMessagesByUIDs ¶ added in v1.1.0
func (c *Client) StreamMessagesByUIDs(ctx context.Context, folder string, uids []uint32, onMessage func(*imap.Message) error) error
StreamMessagesByUIDs fetches the bodies for the given UIDs in batches and invokes onMessage for each as it arrives. The caller is expected to feed in UIDs already filtered against the destination — typically the result of a Message-Id diff produced from two FetchMessageMap calls.
Streaming avoids materializing every body into memory at once; large mailboxes can produce many GB of cumulative body data.
If onMessage returns an error, the channel from the in-flight batch is drained (so the producer goroutine exits cleanly) and the error is returned without scheduling further batches.
type ErrClass ¶ added in v1.1.0
type ErrClass int
ErrClass categorizes IMAP errors so that the reconnect machinery can pick a strategy: retry quickly, give up, or back off for a long time.
Classification is heuristic — IMAP responses are textual and there is no standard error code, so we match on error strings emitted by go-imap and the most common server-side messages (Gmail in particular).
const ( // ClassUnknown is the zero value: the error has not been classified. // Caller should treat it as non-retryable to avoid loops on novel errors. ClassUnknown ErrClass = iota // ClassTransient indicates a network-level failure (EOF, closed conn, // timeout). Reconnect-and-retry is appropriate. ClassTransient // ClassPermanent indicates an authoritative refusal (auth failure, bad // command, missing mailbox). Retry will not help. ClassPermanent // ClassThrottled indicates a server-signaled rate limit or quota hit. // Reconnecting immediately would compound the problem; back off. ClassThrottled )
type MailboxInfo ¶
MailboxInfo describes message counts and sizes for a single folder.
type Options ¶ added in v1.1.0
type Options struct {
TLSConfig *tls.Config
ReadLimiter *rate.Limiter
WriteLimiter *rate.Limiter
DialTimeout time.Duration
UseTLS bool
Verbose bool
}
Options carries the optional knobs for New. Zero-value is fine for plain TLS connections without throttling.
ReadLimiter and WriteLimiter, when non-nil, are typically shared across every Client that talks to the same account so that the byte budget is a global cap, not a per-connection cap.
type ProgressTracker ¶
type ProgressTracker interface {
UpdateMessage(msg string)
UpdateTotal(total int64)
Increment(value int64)
MarkAsErrored()
}
ProgressTracker is a minimal interface for updating tracker messages.
type ProgressWriter ¶
ProgressWriter is a minimal interface for progress tracking and logging. This avoids circular dependency with the progress package.
type Provider ¶ added in v1.1.0
type Provider struct {
Name string
Notes string
DownBPS int // recommended ceiling, bytes/sec
UpBPS int
DailyDownMB int
DailyUpMB int
MaxConnections int
}
Provider describes a known IMAP service and the practical limits clients should respect to avoid hitting server-side throttling.
Numbers come from imapsync's empirical recommendations (FAQ.Gmail.txt) and from the official Workspace bandwidth documentation. They are guidance, not hard constants — a server may tighten or relax them at any time.
func DetectProvider ¶ added in v1.1.0
DetectProvider returns Provider data for serverAddr if its host is known. serverAddr may include a port (host:port).