Documentation
¶
Overview ¶
Delivery — sending a Message over a transport — mirroring the Ruby `mail` gem's delivery methods (Mail.defaults { delivery_method … } + message.deliver).
Every transport is modelled behind an injectable seam: SMTP dials through a Dialer (defaulting to net.Dial), sendmail runs through a swappable runner, and the file/logger sinks go through overridable openers. This keeps the code pure-Go (CGO-free) yet fully drivable by in-process fake servers in tests — no real mail server is ever contacted.
Package mail is a pure-Go (CGO-free) reimplementation of Ruby's `mail` gem — the RFC 5322 / MIME message parser and generator. It builds and reads email messages: header fold/unfold, structured address fields, RFC 2047 encoded-words, RFC 2045 MIME (multipart, boundaries, nested parts, attachments, and base64 / quoted-printable transfer encodings) — all without any Ruby runtime.
API shape ¶
New parses a raw message (or, given a builder function, constructs one); Read reads a message from a file. The resulting Message exposes the gem-faithful accessors From/To/Cc/Bcc/ReplyTo/Subject/Body/Date/MessageID and the MIME view Parts/Attachments/Multipart/ContentType, plus Message.Encoded / Message.String to serialise it back.
Delivery and retrieval ¶
Sending and fetching are implemented here too, mirroring the gem's delivery and retriever methods: configure them with Defaults and send with Message.Deliver, or fetch with Find / First / Last / All. The delivery methods are SMTP (net/smtp, with STARTTLS/implicit-TLS and PLAIN/ LOGIN/CRAM-MD5 auth), Sendmail, TestDelivery, FileDelivery and LoggerDelivery; the retrievers are POP3 and IMAP, both pure-Go clients. Every transport reaches its server through an injectable Dialer seam, so it stays CGO-free and fully testable against in-process servers.
Retrieval — fetching messages from a mailbox — mirroring the Ruby `mail` gem's retriever methods (Mail.defaults { retriever_method … } + Mail.find/first/ last/all). Two transports are implemented in pure Go: POP3 and IMAP, each dialing through the same injectable Dialer seam so an in-process fake server can drive the exchange in tests.
Index ¶
- func Defaults(fn func(*Config))
- type Address
- type AddressList
- type Body
- type Config
- type DeliveryMethod
- type Dialer
- type Field
- type FileDelivery
- type FindOptions
- type Header
- type IMAP
- type Logger
- type LoggerDelivery
- type Message
- func (m *Message) AddHeader(name, v string) *Message
- func (m *Message) AddPart(p *Part) *Message
- func (m *Message) Attachments() []*Part
- func (m *Message) Bcc() []string
- func (m *Message) Body() *Body
- func (m *Message) Cc() []string
- func (m *Message) Charset() string
- func (m *Message) ContentDisposition() string
- func (m *Message) ContentID() string
- func (m *Message) ContentType() string
- func (m *Message) ContentTypeParameters() map[string]string
- func (m *Message) Date() (time.Time, bool)
- func (m *Message) Decoded() []byte
- func (m *Message) Deliver() error
- func (m *Message) DeliverWith(d DeliveryMethod) error
- func (m *Message) Encoded() string
- func (m *Message) Field(name string) string
- func (m *Message) Filename() string
- func (m *Message) From() []string
- func (m *Message) HTMLPart() *Part
- func (m *Message) Header() *Header
- func (m *Message) InReplyTo() string
- func (m *Message) IsAttachment() bool
- func (m *Message) MIMEVersion() string
- func (m *Message) MessageID() string
- func (m *Message) MimeType() string
- func (m *Message) Multipart() bool
- func (m *Message) Parts() []*Part
- func (m *Message) References() []string
- func (m *Message) ReplyTo() []string
- func (m *Message) SetBcc(v string) *Message
- func (m *Message) SetBody(v string) *Message
- func (m *Message) SetCc(v string) *Message
- func (m *Message) SetContentTransferEncoding(v string) *Message
- func (m *Message) SetContentType(v string) *Message
- func (m *Message) SetContentTypeParams(mediaType string, order []string, params map[string]string) *Message
- func (m *Message) SetDate(t time.Time) *Message
- func (m *Message) SetDateString(v string) *Message
- func (m *Message) SetFrom(v string) *Message
- func (m *Message) SetHeader(name, v string) *Message
- func (m *Message) SetInReplyTo(v string) *Message
- func (m *Message) SetMessageID(v string) *Message
- func (m *Message) SetReferences(v string) *Message
- func (m *Message) SetReplyTo(v string) *Message
- func (m *Message) SetSubject(v string) *Message
- func (m *Message) SetTo(v string) *Message
- func (m *Message) String() string
- func (m *Message) Subject() string
- func (m *Message) TextPart() *Part
- func (m *Message) To() []string
- type POP3
- type Part
- type RetrieverMethod
- type SMTP
- type Sendmail
- type TestDelivery
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Address ¶
type Address struct {
// DisplayName is the phrase before the angle-addr (unquoted), or "" when the
// address is a bare addr-spec.
DisplayName string
// Local is the local-part (before "@").
Local string
// Domain is the domain (after "@"), or "" for an addr with no domain.
Domain string
// Comments holds any parenthesised comments found in the mailbox.
Comments []string
// contains filtered or unexported fields
}
Address is a single RFC 5322 mailbox, mirroring Ruby's Mail::Address. It is parsed from a mailbox token such as `John Doe <john@example.com>`, `john@example.com (Johnny)`, or a bare `john@example.com`.
func NewAddress ¶
NewAddress parses a single mailbox token into an Address, mirroring Mail::Address.new. Surrounding whitespace is ignored.
func (*Address) Address ¶
Address returns the addr-spec ("local@domain", or just "local" when there is no domain), matching Mail::Address#address.
type AddressList ¶
type AddressList struct {
// contains filtered or unexported fields
}
AddressList is an ordered collection of addresses parsed from an address field (To/Cc/…), mirroring Mail::AddressList. Group syntax (`name: a@x, b@x;`) is flattened: the members appear in Addresses tagged with their group name.
func NewAddressList ¶
func NewAddressList(s string) *AddressList
NewAddressList parses a comma-separated address field, honouring RFC 5322 group syntax and quoted display names, into an AddressList.
func (*AddressList) Addresses ¶
func (al *AddressList) Addresses() []*Address
Addresses returns the parsed addresses in order.
type Body ¶
Body holds a message or part body, mirroring Mail::Body. Raw is the body exactly as it appeared on the wire (still transfer-encoded); Encoding names the Content-Transfer-Encoding used to decode it.
func (*Body) Decoded ¶
Decoded returns the body decoded according to its Content-Transfer-Encoding, matching Mail::Body#decoded. Unknown or identity encodings (7bit/8bit/binary) return the raw bytes unchanged.
func (*Body) DecodedString ¶
DecodedString is Decoded as a string, for convenience.
type Config ¶
type Config struct{}
Config is the receiver of a Defaults block, mirroring the object yielded by Ruby's Mail.defaults. Set the process-wide delivery and retriever methods on it.
func (*Config) SetDeliveryMethod ¶
func (c *Config) SetDeliveryMethod(d DeliveryMethod)
SetDeliveryMethod installs d as the process-wide delivery method used by Message.Deliver, mirroring `delivery_method :smtp, …`.
func (*Config) SetRetrieverMethod ¶
func (c *Config) SetRetrieverMethod(r RetrieverMethod)
SetRetrieverMethod installs r as the process-wide retriever used by Find / First / Last / All, mirroring `retriever_method :pop3, …`.
type DeliveryMethod ¶
type DeliveryMethod interface {
// Deliver sends m, returning any transport or validation error.
Deliver(m *Message) error
}
DeliveryMethod is a transport that sends a Message, mirroring the gem's delivery-method objects (each responds to deliver!). Implementations: SMTP, Sendmail, TestDelivery, FileDelivery and LoggerDelivery.
type Dialer ¶
Dialer establishes a transport connection to address (host:port), mirroring the signature of net.Dial. It is the seam through which every socket-opening delivery/retrieval method reaches its server; tests inject a Dialer that returns one end of an in-process net.Pipe so a fake server can drive the exchange without any real network.
type Field ¶
type Field struct {
Name string // canonical field name, e.g. "Subject"
Value string // unfolded raw value (whitespace-collapsed continuation)
}
Field is a single header field (a name and its unfolded value), mirroring the role of Mail::Field. The stored Value is already unfolded but not yet encoded-word decoded; Decoded returns the human-readable form.
type FileDelivery ¶
type FileDelivery struct {
// Location is the output directory (default "./mails").
Location string
// Extension is appended to each recipient-derived filename.
Extension string
}
FileDelivery writes the message to one file per unique recipient under Location, appending if the file exists, mirroring Mail::FileDelivery.
func (*FileDelivery) Deliver ¶
func (f *FileDelivery) Deliver(m *Message) error
Deliver writes m into Location, one file per unique recipient.
type FindOptions ¶
type FindOptions struct {
// What is "first" or "last" (default "first").
What string
// Order is "asc" or "desc" (default "asc").
Order string
// Count caps how many messages are returned (default 10); ignored when All.
Count int
// All returns every message, mirroring Mail.all / count => :all.
All bool
// DeleteAfterFind deletes each retrieved message from the server.
DeleteAfterFind bool
// Mailbox is the IMAP mailbox to open (default "INBOX"). Ignored by POP3.
Mailbox string
// Keys are the IMAP SEARCH criteria (default ["ALL"]). Ignored by POP3.
Keys []string
// ReadOnly opens the IMAP mailbox with EXAMINE instead of SELECT.
ReadOnly bool
}
FindOptions selects which messages a RetrieverMethod returns, mirroring the options hash of Mail.find. The zero value is completed with the gem's defaults (what=first, order=asc, count=10, mailbox=INBOX, keys=[ALL]).
type Header ¶
type Header struct {
// contains filtered or unexported fields
}
Header is an ordered list of fields plus a case-insensitive lookup, mirroring Mail::Header. Multiple fields with the same name are preserved in order.
type IMAP ¶
type IMAP struct {
// Address is the IMAP server host (default "localhost").
Address string
// Port is the IMAP server port (default 143).
Port int
// UserName / Password are the login credentials.
UserName string
Password string
// EnableSSL requests an implicit-TLS (IMAPS) connection.
EnableSSL bool
// EnableStartTLS upgrades a plaintext connection via STARTTLS before login.
EnableStartTLS bool
// Dial is the connection seam; nil means net.Dial.
Dial Dialer
// TLSConfig, when set, overrides the default (verify-none) TLS config.
TLSConfig *tls.Config
}
IMAP retrieves messages from an IMAP mailbox, mirroring `retriever_method :imap` with mailbox select/search/fetch. It speaks IMAP in pure Go over a connection from IMAP.Dial (net.Dial by default).
func (*IMAP) Find ¶
func (p *IMAP) Find(opts FindOptions) ([]*Message, error)
Find retrieves messages from the IMAP mailbox per opts.
type Logger ¶
type Logger interface {
Print(v ...any)
}
Logger is the sink for LoggerDelivery; *log.Logger satisfies it.
type LoggerDelivery ¶
type LoggerDelivery struct {
// Logger is the destination; nil logs to stdout.
Logger Logger
}
LoggerDelivery "delivers" a message by logging its wire form, mirroring Mail::LoggerDelivery. Useful for development.
func (*LoggerDelivery) Deliver ¶
func (l *LoggerDelivery) Deliver(m *Message) error
Deliver logs m's envelope message.
type Message ¶
type Message struct {
// contains filtered or unexported fields
}
Message is a parsed or constructed email message, mirroring Ruby's Mail message object. Build one with New; read it back with the accessors, or serialise it with Message.Encoded.
func Find ¶
func Find(opts FindOptions) ([]*Message, error)
Find returns messages from the process-wide retriever configured with Defaults, mirroring Mail.find.
func First ¶
First returns the oldest message from the configured retriever, mirroring Mail.first (nil when the mailbox is empty).
func Last ¶
Last returns the newest message from the configured retriever, mirroring Mail.last (nil when the mailbox is empty).
func New ¶
New parses raw as an RFC 5322 message, mirroring Mail.new(raw). If raw is empty and builders are supplied, it constructs a message by running each builder against the fresh message instead (mirroring the `Mail.new { … }` block form).
func (*Message) AddHeader ¶
AddHeader appends a header field without removing existing same-named fields.
func (*Message) AddPart ¶
AddPart appends a child part and marks the message multipart if it is not already (the caller is expected to set the multipart Content-Type/boundary, or rely on the default applied at emission).
func (*Message) Attachments ¶
Attachments returns the parts that are attachments (see Message.IsAttachment), recursing into nested multipart parts, mirroring Mail#attachments.
func (*Message) Body ¶
Body returns the message's Body, mirroring Mail#body. For a multipart message this is the raw wrapper body; use Message.Parts for the children.
func (*Message) ContentDisposition ¶
ContentDisposition returns the Content-Disposition media value (e.g. "attachment" / "inline"), or "".
func (*Message) ContentID ¶
ContentID returns the Content-ID header value with any surrounding angle brackets removed, mirroring Mail#content_id.
func (*Message) ContentType ¶
ContentType returns the part's Content-Type header value with its parameters preserved (e.g. `text/plain; charset=UTF-8`), or "" when absent — mirroring Mail::Part#content_type.
func (*Message) ContentTypeParameters ¶
ContentTypeParameters returns the parsed parameters of the Content-Type header (keys lower-cased), mirroring Mail#content_type_parameters.
func (*Message) Date ¶
Date returns the parsed Date header. The bool reports whether a Date was present and parseable, mirroring the fact that Mail#date returns nil when absent.
func (*Message) Decoded ¶
Decoded returns the part's body decoded per its Content-Transfer-Encoding, mirroring Mail::Part#decoded.
func (*Message) Deliver ¶
Deliver sends the message via the process-wide delivery method configured with Defaults, mirroring Mail::Message#deliver. It errors if no delivery method has been configured.
func (*Message) DeliverWith ¶
func (m *Message) DeliverWith(d DeliveryMethod) error
DeliverWith sends the message via the given delivery method, mirroring assigning a per-message delivery_method then calling deliver.
func (*Message) Encoded ¶
Encoded serialises the message back to its RFC 5322 / MIME wire form, mirroring Mail#encoded / Mail#to_s. Header fields are emitted in insertion order (so a parsed message round-trips its field order), unstructured values carrying non-ASCII bytes are RFC 2047 encoded, and long fields are folded at 78 columns. A multipart message re-emits its parts between the boundary delimiters.
func (*Message) Filename ¶
Filename returns the attachment filename from the Content-Disposition (filename=) or the Content-Type (name=) parameter, mirroring how Mail derives an attachment's filename; "" when the part is not a named attachment.
func (*Message) Header ¶
Header returns the message's header for direct field access, mirroring Mail#header.
func (*Message) InReplyTo ¶
InReplyTo returns the In-Reply-To msg-id without angle brackets. When multiple ids are present it returns the first (as the gem does for a scalar accessor).
func (*Message) IsAttachment ¶
IsAttachment reports whether the part is an attachment: either an explicit `Content-Disposition: attachment`, or any part that carries a filename.
func (*Message) MIMEVersion ¶
MIMEVersion returns the MIME-Version header value, or "".
func (*Message) MessageID ¶
MessageID returns the Message-ID with angle brackets stripped, mirroring Mail#message_id (which returns the msg-id without the angle brackets).
func (*Message) MimeType ¶
MimeType returns just the media type of the Content-Type (e.g. "text/plain"), lower-cased, mirroring Mail#mime_type.
func (*Message) Multipart ¶
Multipart reports whether the message is multipart (has a multipart/* Content-Type and at least one parsed part), mirroring Mail#multipart?.
func (*Message) Parts ¶
Parts returns the child MIME parts, mirroring Mail#parts (empty for a non-multipart message).
func (*Message) References ¶
References returns the References msg-ids (angle brackets stripped), in order, mirroring Mail#references.
func (*Message) SetContentTransferEncoding ¶
SetContentTransferEncoding sets the Content-Transfer-Encoding field and keeps the body's decode encoding in sync.
func (*Message) SetContentType ¶
SetContentType sets the Content-Type field.
func (*Message) SetContentTypeParams ¶
func (m *Message) SetContentTypeParams(mediaType string, order []string, params map[string]string) *Message
SetContentTypeParams sets the Content-Type field from a media type plus parameters, emitting them in the given key order (each value quoted when required). Mirrors building a Content-Type with content_type_parameters.
func (*Message) SetDateString ¶
SetDateString sets the Date field to a raw string (already formatted).
func (*Message) SetInReplyTo ¶
SetInReplyTo sets the In-Reply-To field.
func (*Message) SetMessageID ¶
SetMessageID sets the Message-ID field, adding angle brackets if the caller passed a bare id.
func (*Message) SetReferences ¶
SetReferences sets the References field.
func (*Message) SetReplyTo ¶
SetReplyTo sets the Reply-To field.
func (*Message) SetSubject ¶
SetSubject sets the Subject field. The value is stored raw; header emission RFC 2047 encodes it when it contains non-ASCII bytes.
type POP3 ¶
type POP3 struct {
// Address is the POP3 server host (default "localhost").
Address string
// Port is the POP3 server port (default 110).
Port int
// UserName / Password are the login credentials.
UserName string
Password string
// EnableSSL requests an implicit-TLS (POP3S) connection.
EnableSSL bool
// Dial is the connection seam; nil means net.Dial.
Dial Dialer
// TLSConfig, when set, overrides the default (verify-none) TLS config.
TLSConfig *tls.Config
}
POP3 retrieves messages from a POP3 mailbox, mirroring `retriever_method :pop3`. It speaks POP3 in pure Go over a connection from POP3.Dial (net.Dial by default).
func (*POP3) Find ¶
func (p *POP3) Find(opts FindOptions) ([]*Message, error)
Find retrieves messages from the POP3 mailbox per opts.
type Part ¶
type Part = Message
Part is one MIME part of a multipart message, mirroring Mail::Part. It shares the Message shape (a part is itself a message-like entity with its own header and body, and may nest further parts).
type RetrieverMethod ¶
type RetrieverMethod interface {
// Find returns the messages selected by opts.
Find(opts FindOptions) ([]*Message, error)
}
RetrieverMethod fetches messages from a mailbox, mirroring the gem's retriever objects. Implementations: POP3 and IMAP.
type SMTP ¶
type SMTP struct {
// Address is the SMTP server host (default "localhost").
Address string
// Port is the SMTP server port (default 25).
Port int
// Domain is the argument to EHLO/HELO; when empty the client's default is
// used and no explicit HELO is sent.
Domain string
// UserName / Password are the AUTH credentials.
UserName string
Password string
// Authentication selects the AUTH mechanism: "plain", "login" or "cram_md5"
// (empty means no authentication).
Authentication string
// EnableStartTLSAuto upgrades a plaintext connection to TLS via STARTTLS when
// the server advertises it, mirroring :enable_starttls_auto.
EnableStartTLSAuto bool
// OpenSSLVerifyMode of "none" disables certificate verification (maps to
// InsecureSkipVerify), mirroring :openssl_verify_mode => 'none'.
OpenSSLVerifyMode string
// SSL / TLS request an implicit-TLS (SMTPS) connection, mirroring :ssl/:tls.
SSL bool
TLS bool
// Dial is the connection seam; nil means net.Dial.
Dial Dialer
// TLSConfig, when set, overrides the config built from OpenSSLVerifyMode.
TLSConfig *tls.Config
}
SMTP delivers a message over SMTP, mirroring the gem's `delivery_method :smtp` with its address/port/user_name/password/authentication/enable_starttls_auto/ openssl_verify_mode settings. Sending is done with the standard library's net/smtp over a connection obtained from SMTP.Dial (net.Dial by default), so tests can substitute an in-process server.
type Sendmail ¶
type Sendmail struct {
// Location is the sendmail binary path (default "/usr/sbin/sendmail").
Location string
// Arguments are the fixed arguments (default ["-i"]).
Arguments []string
// Run is the execution seam; nil runs the real binary.
Run func(path string, args []string, stdin []byte) error
}
Sendmail delivers a message by piping it to a local sendmail binary, mirroring `delivery_method :sendmail`. The command is built as `location arguments… -f <from> -- <recipients…>` and the LF-normalised message is written to its stdin.
type TestDelivery ¶
type TestDelivery struct {
// contains filtered or unexported fields
}
TestDelivery is a no-op delivery method that records every delivered message, mirroring Mail::TestMailer for use in tests. It still builds (and thus validates) the SMTP envelope, so envelope errors surface as they would over a real transport.
func (*TestDelivery) Clear ¶
func (t *TestDelivery) Clear()
Clear empties the recorded deliveries, mirroring TestMailer.deliveries.clear.
func (*TestDelivery) Deliver ¶
func (t *TestDelivery) Deliver(m *Message) error
Deliver records m after validating its envelope.
func (*TestDelivery) Deliveries ¶
func (t *TestDelivery) Deliveries() []*Message
Deliveries returns a snapshot of the recorded messages, mirroring Mail::TestMailer.deliveries.
