- Go 100%
| acceptance_test.go | ||
| append.go | ||
| append_test.go | ||
| client.go | ||
| client_test.go | ||
| conn.go | ||
| doc.go | ||
| errors.go | ||
| examine_test.go | ||
| fetch.go | ||
| fixes_test.go | ||
| go.mod | ||
| go.sum | ||
| idle.go | ||
| LICENSE | ||
| limiter_test.go | ||
| list.go | ||
| list_test.go | ||
| namespace.go | ||
| namespace_test.go | ||
| NOTICE | ||
| notify.go | ||
| parser.go | ||
| password.go | ||
| password_test.go | ||
| peek.go | ||
| peek_test.go | ||
| README.md | ||
| sanitize.go | ||
| sanitize_test.go | ||
| search.go | ||
| search_test.go | ||
| security.go | ||
| security_test.go | ||
| select.go | ||
| store.go | ||
| testserver_test.go | ||
| types.go | ||
| utf7.go | ||
| validate.go | ||
imapclient
A small, dependency-light IMAP client for Go, built directly on
RFC 3501 with optional CONDSTORE (RFC 7162) and NAMESPACE (RFC 2342)
support. Context-cancellable I/O on every operation; typed values
throughout; credentials wrapped in a self-redacting Password type.
The only runtime dependency outside the standard library is
golang.org/x/time/rate (a token-bucket command rate limiter).
Status: pre-1.0 (
v0.x). The API may change.
Install
go get go.schlittermann.de/heiko/imapclient
Requires Go 1.26 or newer.
Quick start
ctx := context.Background()
cfg := imapclient.Config{
Name: "primary",
Host: "imap.example.com",
Port: 993,
TLS: true,
Username: "alice@example.com",
Password: imapclient.NewPassword(os.Getenv("IMAP_PASSWORD")),
Mailbox: "INBOX",
}
c, mbox, err := imapclient.Connect(ctx, cfg, false /* condStore */)
if err != nil {
log.Fatal(err)
}
defer c.Close()
uids, err := c.SearchUIDs(ctx, imapclient.SearchCriteria{Since: time.Now().AddDate(0, 0, -7)})
if err != nil {
log.Fatal(err)
}
for _, uid := range uids {
body, err := c.FetchBody(ctx, uid)
if err != nil {
log.Fatal(err)
}
_ = body // parse with net/mail, etc.
if err := c.Store(ctx, uid, imapclient.FlagAdd, imapclient.FlagSeen); err != nil {
log.Fatal(err)
}
}
_ = mbox // *MailboxInfo from the SELECT
Transport security
The zero value of Config is safe: with neither TLS nor
InsecurePlaintext set, Dial requires the server to advertise
STARTTLS before sending any credentials. If the server does not
advertise it, Dial returns ErrSTARTTLSUnsupported before LOGIN.
- Implicit TLS (
Config.TLS = true, port 993): TLS before any IMAP bytes. - Mandatory STARTTLS (default, port 143): plaintext upgraded before LOGIN.
- Plaintext (
Config.InsecurePlaintext = true): no encryption — local testing only.
There is no "STARTTLS optional" downgrade mode by design.
Each insecure opt-out fires Config.OnSecurityEvent once per connect:
| Field | Meaning | SecurityEventKind |
|---|---|---|
TLSNoVerify |
skip certificate verification | TLSNoVerifyActive |
InsecurePlaintext |
stay plaintext for the whole session | STARTTLSDisabled |
InsecureDebug |
show cleartext password in DebugWriter |
InsecureDebugActive |
cfg.OnSecurityEvent = func(e imapclient.SecurityEvent) {
log.Printf("imap insecure transport: %s source=%s host=%s", e.Kind, e.Name, e.Host)
}
The package imports no logging framework; routing the signal is the caller's choice.
Credentials
Password redacts in every formatting, logging, and marshalling path.
Only Password.Reveal returns the plaintext, and the package calls it
exactly once — just before writing the LOGIN command.
License
Apache-2.0. See LICENSE and NOTICE.