Help us shape the future of Go! Take the 2026 Go Developer Survey

mailparser

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: Apache-2.0, MIT Imports: 19 Imported by: 0

README

mailparser

CI Go Reference Go License

Fast and robust e-mail parsing library for Go.

[!NOTE] A Go port of stalwartlabs/mail-parser by Stalwart Labs. Known deviations are documented in DEVIATIONS.md.

Quick start

package main

import (
	"fmt"

	"github.com/yokohola/mailparser"
)

func main() {
	raw := []byte("From: Art Vandelay <art@vandelay.com>\n" +
		"Subject: Why not both?\n\nHello, world!\n")

	m := mailparser.NewMessageParser().Parse(raw)
	if m == nil {
		return // not a message
	}
	
	if subject, ok := m.Subject(); ok {
		fmt.Println("Subject:", subject)
	}
	
	if from := m.From(); from != nil {
		if addr := from.First(); addr != nil {
			fmt.Println("From:", addr.Address)
		}
	}
	
	if body, ok := m.BodyText(0); ok {
		fmt.Println(body)
	}
}

Selective parsing for hot paths - parse only the headers you need:

parser := mailparser.NewMessageParser().
	WithMinimalHeaders().          // MIME + Date/Subject/From/Reply-To/To/Cc/Bcc
	IgnoreHeader(mailparser.HeaderReceived)

headersOnly := parser.ParseHeaders(raw)

See example_test.go and the mailbox package for more, including custom per-header parser configuration.

License

Licensed under either of Apache-2.0 or MIT, at your option.

Derivative work of stalwartlabs/mail-parser, Copyright © 2020 Stalwart Labs LLC.

Documentation

Overview

Package mailparser is a fast and robust e-mail parsing library.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	HeaderSubject                  = HeaderName{/* contains filtered or unexported fields */}
	HeaderFrom                     = HeaderName{/* contains filtered or unexported fields */}
	HeaderTo                       = HeaderName{/* contains filtered or unexported fields */}
	HeaderCc                       = HeaderName{/* contains filtered or unexported fields */}
	HeaderDate                     = HeaderName{/* contains filtered or unexported fields */}
	HeaderBcc                      = HeaderName{/* contains filtered or unexported fields */}
	HeaderReplyTo                  = HeaderName{/* contains filtered or unexported fields */}
	HeaderSender                   = HeaderName{/* contains filtered or unexported fields */}
	HeaderComments                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderInReplyTo                = HeaderName{/* contains filtered or unexported fields */}
	HeaderKeywords                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderReceived                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderMessageID                = HeaderName{/* contains filtered or unexported fields */}
	HeaderReferences               = HeaderName{/* contains filtered or unexported fields */}
	HeaderReturnPath               = HeaderName{/* contains filtered or unexported fields */}
	HeaderMimeVersion              = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentDescription       = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentID                = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentLanguage          = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentLocation          = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentTransferEncoding  = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentType              = HeaderName{/* contains filtered or unexported fields */}
	HeaderContentDisposition       = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentTo                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentFrom               = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentBcc                = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentCc                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentSender             = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentDate               = HeaderName{/* contains filtered or unexported fields */}
	HeaderResentMessageID          = HeaderName{/* contains filtered or unexported fields */}
	HeaderListArchive              = HeaderName{/* contains filtered or unexported fields */}
	HeaderListHelp                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderListID                   = HeaderName{/* contains filtered or unexported fields */}
	HeaderListOwner                = HeaderName{/* contains filtered or unexported fields */}
	HeaderListPost                 = HeaderName{/* contains filtered or unexported fields */}
	HeaderListSubscribe            = HeaderName{/* contains filtered or unexported fields */}
	HeaderListUnsubscribe          = HeaderName{/* contains filtered or unexported fields */}
	HeaderArcAuthenticationResults = HeaderName{/* contains filtered or unexported fields */}
	HeaderArcMessageSignature      = HeaderName{/* contains filtered or unexported fields */}
	HeaderArcSeal                  = HeaderName{/* contains filtered or unexported fields */}
	HeaderDkimSignature            = HeaderName{/* contains filtered or unexported fields */}
	HeaderDkim2Signature           = HeaderName{/* contains filtered or unexported fields */}
	HeaderMessageInstance          = HeaderName{/* contains filtered or unexported fields */}
)

Known header names.

Functions

func AddHTMLToken

func AddHTMLToken(result *strings.Builder, token []byte, addSpace bool)

AddHTMLToken appends token to result, optionally preceded by a space. A token of the form "&name;", "&#nnn;" or "&#xhh;" is decoded as an HTML entity; unknown or malformed entities are appended verbatim.

func Base64Decode

func Base64Decode(bytes []byte) ([]byte, bool)

Base64Decode decodes a base64 encoded value, ignoring ASCII whitespace. It reports false on invalid input.

func Base64DecodeStream

func Base64DecodeStream(stream iter.Seq[byte], streamLen int, stopChar byte) ([]byte, bool)

Base64DecodeStream decodes a base64 encoded value from a byte sequence, stopping successfully when stopChar is found outside of the base64 alphabet.

func DecodeHex

func DecodeHex(src []byte) (bool, []byte)

DecodeHex decodes a %XX percent-encoded byte string (RFC2231 extended parameter values), reporting success and the decoded bytes.

func HTMLToText

func HTMLToText(input string) string

HTMLToText extracts the readable text of an HTML document: tags, comments and head/style/script/template contents are dropped, entities are decoded, whitespace runs collapse to a single space, and <br> and closing </p> tags become newlines.

func ParseAddressDetailPart

func ParseAddressDetailPart(addr string) (string, bool)

ParseAddressDetailPart returns the text between the last '+' before the '@' of an e-mail address and the '@' itself (possibly empty), reporting false when there is no such '+'.

func ParseAddressDomain

func ParseAddressDomain(addr string) (string, bool)

ParseAddressDomain returns the text after the first '@' of an e-mail address, reporting false when either side of the '@' is empty.

func ParseAddressLocalPart

func ParseAddressLocalPart(addr string) (string, bool)

ParseAddressLocalPart returns the text before the first '@' of an e-mail address, reporting false when the address has no non-empty local part followed by a non-empty remainder.

func ParseAddressUserPart

func ParseAddressUserPart(addr string) (string, bool)

ParseAddressUserPart returns the text before the first '+' of an e-mail address when that '+' precedes the '@', and otherwise the local part.

func PreviewHTML

func PreviewHTML(html string, maxLen int) string

PreviewHTML converts HTML to text and truncates it to at most maxLen bytes, appending "..." when maxLen > 6.

func PreviewText

func PreviewText(text string, maxLen int) string

PreviewText truncates text at a UTF-8 character boundary so the result is at most maxLen bytes, appending "..." when maxLen > 6 (the dots count toward the limit).

func QuotedPrintableDecode

func QuotedPrintableDecode(bytes []byte) ([]byte, bool)

QuotedPrintableDecode decodes a quoted-printable encoded value, reporting false on invalid input.

func QuotedPrintableDecodeChar

func QuotedPrintableDecodeChar(hex1, hex2 byte) (byte, bool)

QuotedPrintableDecodeChar decodes a single =XX escape from its two hex digit characters, reporting false when either digit is invalid.

func TextToHTML

func TextToHTML(input string) string

TextToHTML wraps plain text in a minimal HTML document, converting newlines to <br/> and escaping '<'.

func ThreadName

func ThreadName(text string) string

ThreadName returns the base thread name of a Subject value: it strips reply/forward prefixes ("re:", "fwd[5]:", localized variants), leading mailing-list "[...]" blobs, "[fwd: ...]" wrappers and trailing "(fwd)" suffixes, returning a substring of text.

func TrimTrailingFwd

func TrimTrailingFwd(text string) string

TrimTrailingFwd trims leading/trailing whitespace and any number of trailing "(fwd)"-style groups from text, returning a substring.

It scans in reverse. The whitespace arm's continue skips the textStart update, which is how leading trim happens, and a stripped group re-arms trimEnd.

func TruncateHTML

func TruncateHTML(html string, maxLen int) string

TruncateHTML truncates HTML markup to at most maxLen bytes, appending "..." when maxLen > 6. The cut never lands inside a tag or comment: when it would, the result is rolled back to the end of the last complete tag (or to just before an unfinished one).

func TruncateText

func TruncateText(text string, maxLen int) string

TruncateText truncates text to at most maxLen bytes at a UTF-8 character boundary. It is an alias of PreviewText.

Types

type Addr

type Addr struct {
	// Name is the address display name including comments; "" when absent.
	Name string
	// Address is an e-mail address (RFC5322/RFC2369) or URL (RFC2369);
	// "" when absent.
	Address string
}

Addr is a single RFC5322 or RFC2369 internet address.

Both fields are plain strings and "" means absent, the shape used by net/mail.Address: callers read a.Name and a.Address directly, with no nil check and no dereference. Absent and present-but-empty are therefore one state, not two. The wire form maps "" to null and back, so a JSON round trip is stable; the one input class that told the two apart in the Rust original is recorded in DEVIATIONS.md 11.

func NewAddr

func NewAddr(name, address string) Addr

NewAddr returns an Addr with the given display name and address. An empty string means that field is absent.

func (Addr) MarshalJSON

func (a Addr) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form, encoding an absent name or address as null.

func (*Addr) UnmarshalJSON

func (a *Addr) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form, decoding null as "".

type Address

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

Address is an RFC5322 address header value: either a flat address list or a list of named groups.

func AddressGroups

func AddressGroups(groups []Group) Address

AddressGroups returns an Address holding grouped addresses.

func AddressList

func AddressList(list []Addr) Address

AddressList returns an Address holding a flat list of addresses.

func (*Address) All

func (a *Address) All() iter.Seq[*Addr]

All iterates over every address: the list elements, or all groups' addresses flattened in order.

func (*Address) AsGroup

func (a *Address) AsGroup() ([]Group, bool)

AsGroup returns the groups; ok is false when the address holds a flat list.

func (*Address) AsList

func (a *Address) AsList() ([]Addr, bool)

AsList returns the flat list of addresses; ok is false when the address holds groups.

func (*Address) Contains

func (a *Address) Contains(addr string) bool

Contains reports whether any address field equals addr ASCII-case-insensitively. Display names are ignored, and an Addr with no Address never matches.

func (*Address) First

func (a *Address) First() *Addr

First returns the first address in the list, or the first address across the groups (empty groups are skipped).

func (*Address) IsGroup

func (a *Address) IsGroup() bool

IsGroup reports whether this address value holds groups.

func (*Address) Last

func (a *Address) Last() *Addr

Last returns the last address in the list, or the last address across the groups (empty groups are skipped).

func (Address) MarshalJSON

func (a Address) MarshalJSON() ([]byte, error)

MarshalJSON serializes the externally tagged Address variants: {"List": [...]} or {"Group": [...]}.

func (*Address) ToGroups

func (a *Address) ToGroups() []Group

ToGroups converts the address into groups. A flat list becomes one singleton Group PER address, each with an empty name, not a single group holding all of them.

func (*Address) ToList

func (a *Address) ToList() []Addr

ToList converts the address into a flat list of Addr, flattening all groups' addresses in order.

func (*Address) UnmarshalJSON

func (a *Address) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the externally tagged Address variants.

type Attribute

type Attribute struct {
	Name  string
	Value string
}

Attribute is a single Content-Type/Content-Disposition attribute.

func (Attribute) MarshalJSON

func (a Attribute) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form.

func (*Attribute) UnmarshalJSON

func (a *Attribute) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form. Both shapes are accepted: the map {"name":..,"value":..} and the pair ["name","value"], which is what the content_type.json fixture uses.

type ContentType

type ContentType struct {
	Type    string
	Subtype *string // nil when absent
	// Attributes is nil when the header has no attributes; it serializes to
	// JSON null.
	Attributes []Attribute
}

ContentType is an RFC2047 Content-Type or RFC2183 Content-Disposition header value.

func (*ContentType) Attribute

func (c *ContentType) Attribute(name string) (string, bool)

Attribute returns the value of the first attribute with the given name. The match is case-SENSITIVE: the parser already lowercases attribute names.

func (*ContentType) HasAttribute

func (c *ContentType) HasAttribute(name string) bool

HasAttribute reports whether an attribute with the given name is present (case-sensitive).

func (*ContentType) IsAttachment

func (c *ContentType) IsAttachment() bool

IsAttachment reports whether the Content-Disposition type is "attachment", ASCII-case-insensitively.

func (*ContentType) IsInline

func (c *ContentType) IsInline() bool

IsInline reports whether the Content-Disposition type is "inline", ASCII-case-insensitively.

func (ContentType) MarshalJSON

func (c ContentType) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form.

func (*ContentType) RemoveAttribute

func (c *ContentType) RemoveAttribute(name string) (string, bool)

RemoveAttribute removes the first attribute with the given name (case-sensitive) and returns its value. The last attribute moves into the freed slot, so order is not preserved.

func (*ContentType) UnmarshalJSON

func (c *ContentType) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form.

type DateTime

type DateTime struct {
	Year        uint16
	Month       uint8
	Day         uint8
	Hour        uint8
	Minute      uint8
	Second      uint8
	TZBeforeGMT bool
	TZHour      uint8
	TZMinute    uint8
}

DateTime is an RFC5322 date and time. Malformed input is preserved verbatim, so fields may hold out-of-range values (e.g. Second == 232) and the narrow unsigned widths wrap on overflow.

func DateTimeFromTimestamp

func DateTimeFromTimestamp(timestamp int64) DateTime

DateTimeFromTimestamp builds a DateTime from a Unix timestamp. The result is always UTC: TZBeforeGMT false, zero offset.

func ParseRFC822

func ParseRFC822(value string) *DateTime

ParseRFC822 parses an RFC822 date, or returns nil if it does not parse.

func ParseRFC3339

func ParseRFC3339(value string) *DateTime

ParseRFC3339 parses an RFC3339 date, or returns nil if it does not parse.

func (DateTime) Compare

func (d DateTime) Compare(other DateTime) int

Compare returns -1, 0 or 1 as d is before, equal to, or after other by timestamp.

func (DateTime) DayOfWeek

func (d DateTime) DayOfWeek() uint8

DayOfWeek returns the day of week where [0, 6] represents [Sun, Sat].

func (DateTime) IsValid

func (d DateTime) IsValid() bool

IsValid returns true if the date is valid.

func (DateTime) JulianDay

func (d DateTime) JulianDay() int64

JulianDay returns the julian day.

func (DateTime) MarshalJSON

func (d DateTime) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form.

func (DateTime) String

func (d DateTime) String() string

String returns the RFC3339 representation.

func (DateTime) ToRFC822

func (d DateTime) ToRFC822() string

ToRFC822 returns an RFC822 representation of the date.

func (DateTime) ToRFC3339

func (d DateTime) ToRFC3339() string

ToRFC3339 returns an RFC3339 representation of the parsed RFC5322 datetime field.

func (DateTime) ToTimestamp

func (d DateTime) ToTimestamp() int64

ToTimestamp returns the number of seconds since 1970-01-01T00:00:00Z (Unix epoch).

func (DateTime) ToTimestampLocal

func (d DateTime) ToTimestampLocal() int64

ToTimestampLocal returns the number of seconds since 1970-01-01T00:00:00Z (Unix epoch) in local time.

func (DateTime) ToTimezone

func (d DateTime) ToTimezone(tz int64) DateTime

ToTimezone converts the DateTime to the given timezone (signed seconds offset from GMT).

func (*DateTime) UnmarshalJSON

func (d *DateTime) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form.

type DecodeFnc

type DecodeFnc func(stream *MessageStream, boundary []byte) (int, []byte)

DecodeFnc is a MIME body decoder: it decodes the stream while scanning for the given multipart boundary and returns the offset where the decoded body ends (math.MaxInt when a non-empty boundary was not found) together with the decoded bytes.

type DecodeWordFnc

type DecodeWordFnc func(stream *MessageStream) ([]byte, bool)

DecodeWordFnc is an RFC2047 encoded-word decoder: it decodes the stream up to the closing "?=" sequence and reports whether the word was valid.

type DecoderFnc

type DecoderFnc func(bytes []byte) string

DecoderFnc decodes bytes in a legacy character set into a UTF-8 string.

func CharsetDecoder

func CharsetDecoder(charset []byte) (DecoderFnc, bool)

CharsetDecoder returns the decoder function for a character set name. Names match ASCII case-insensitively with '-' treated as '_', and only the first 45 bytes are significant.

type Encoding

type Encoding uint8

Encoding is a MIME part transfer encoding.

const (
	EncodingNone            Encoding = 0
	EncodingQuotedPrintable Encoding = 1
	EncodingBase64          Encoding = 2
)

func EncodingFrom

func EncodingFrom(v uint8) Encoding

EncodingFrom converts a raw byte: 1 and 2 map to their encodings, anything else to EncodingNone.

func (Encoding) MarshalJSON

func (e Encoding) MarshalJSON() ([]byte, error)

MarshalJSON writes the encoding as its name string. MessagePart.Encoding is skipped, so this only matters when the type is used standalone.

func (*Encoding) UnmarshalJSON

func (e *Encoding) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form. Unknown names error loudly.

type Greeting

type Greeting uint8

Greeting is the SMTP greeting command recorded in a Received header.

const (
	GreetingHelo Greeting = iota
	GreetingEhlo
	GreetingLhlo
)

func (Greeting) AsStr

func (g Greeting) AsStr() string

AsStr returns the display name of the greeting.

func (Greeting) MarshalJSON

func (g Greeting) MarshalJSON() ([]byte, error)

MarshalJSON serializes the bare variant identifier string.

func (Greeting) String

func (g Greeting) String() string

String is the same as AsStr.

func (*Greeting) UnmarshalJSON

func (g *Greeting) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the bare variant identifier string.

type Group

type Group struct {
	// Name is the group display name; "" when absent.
	Name string
	// Addresses are the members of the group.
	Addresses []Addr
}

Group is an RFC5322 address group.

A Group is always held by value - no exported signature in this package returns a *Group - so callers never nil-check a Group itself. An unnamed group is one with Name == "", which is what "To: a@b.com;" and Address.ToGroups on a flat list produce.

func NewGroup

func NewGroup(name string, addresses []Addr) Group

NewGroup returns a Group with the given display name and addresses. An empty name means an unnamed group.

func (Group) MarshalJSON

func (g Group) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form, encoding an unnamed group as null.

func (*Group) UnmarshalJSON

func (g *Group) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form, decoding null as "".

type HdrParseFunc

type HdrParseFunc func(*MessageStream) HeaderValue

HdrParseFunc parses one header value from the stream, leaving it positioned after the field's terminating newline.

type Header struct {
	Name  HeaderName
	Value HeaderValue
	// OffsetField is the offset of the header name in the raw message;
	// OffsetStart/OffsetEnd delimit the raw value.
	OffsetField int
	OffsetStart int
	OffsetEnd   int
}

Header is a single message header field.

func (Header) MarshalJSON

func (h Header) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form.

func (*Header) UnmarshalJSON

func (h *Header) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form.

type HeaderForm

type HeaderForm uint8

HeaderForm selects how HeaderAs re-parses a raw header value.

const (
	// HeaderFormRaw yields the raw UTF-8 text, whitespace-trimmed.
	HeaderFormRaw HeaderForm = iota
	// HeaderFormText re-parses as an unstructured text header.
	HeaderFormText
	// HeaderFormAddresses re-parses as an address header.
	HeaderFormAddresses
	// HeaderFormGroupedAddresses re-parses as an address header. Identical
	// to HeaderFormAddresses at this layer.
	HeaderFormGroupedAddresses
	// HeaderFormMessageIDs re-parses as a Message-ID/References header.
	HeaderFormMessageIDs
	// HeaderFormDate re-parses as an RFC5322 date header.
	HeaderFormDate
	// HeaderFormURLs re-parses as a list of URLs. It routes to the address
	// parser: RFC2369 List-* URLs parse as addresses.
	HeaderFormURLs
)

type HeaderID

type HeaderID uint8

HeaderID identifies a known header name.

const (
	HeaderIDSubject                  HeaderID = 0
	HeaderIDFrom                     HeaderID = 1
	HeaderIDTo                       HeaderID = 2
	HeaderIDCc                       HeaderID = 3
	HeaderIDDate                     HeaderID = 4
	HeaderIDBcc                      HeaderID = 5
	HeaderIDReplyTo                  HeaderID = 6
	HeaderIDSender                   HeaderID = 7
	HeaderIDComments                 HeaderID = 8
	HeaderIDInReplyTo                HeaderID = 9
	HeaderIDKeywords                 HeaderID = 10
	HeaderIDReceived                 HeaderID = 11
	HeaderIDMessageID                HeaderID = 12
	HeaderIDReferences               HeaderID = 13
	HeaderIDReturnPath               HeaderID = 14
	HeaderIDMimeVersion              HeaderID = 15
	HeaderIDContentDescription       HeaderID = 16
	HeaderIDContentID                HeaderID = 17
	HeaderIDContentLanguage          HeaderID = 18
	HeaderIDContentLocation          HeaderID = 19
	HeaderIDContentTransferEncoding  HeaderID = 20
	HeaderIDContentType              HeaderID = 21
	HeaderIDContentDisposition       HeaderID = 22
	HeaderIDResentTo                 HeaderID = 23
	HeaderIDResentFrom               HeaderID = 24
	HeaderIDResentBcc                HeaderID = 25
	HeaderIDResentCc                 HeaderID = 26
	HeaderIDResentSender             HeaderID = 27
	HeaderIDResentDate               HeaderID = 28
	HeaderIDResentMessageID          HeaderID = 29
	HeaderIDListArchive              HeaderID = 30
	HeaderIDListHelp                 HeaderID = 31
	HeaderIDListID                   HeaderID = 32
	HeaderIDListOwner                HeaderID = 33
	HeaderIDListPost                 HeaderID = 34
	HeaderIDListSubscribe            HeaderID = 35
	HeaderIDListUnsubscribe          HeaderID = 36
	HeaderIDOther                    HeaderID = 37
	HeaderIDArcAuthenticationResults HeaderID = 38
	HeaderIDArcMessageSignature      HeaderID = 39
	HeaderIDArcSeal                  HeaderID = 40
	HeaderIDDkimSignature            HeaderID = 41
	HeaderIDDkim2Signature           HeaderID = 42
	HeaderIDMessageInstance          HeaderID = 43
)

type HeaderName

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

HeaderName is a header field name: either one of the known names or an arbitrary ("Other") name. Other names compare ASCII case-insensitively.

func HeaderNameFrom

func HeaderNameFrom(s string) HeaderName

HeaderNameFrom converts a string into a HeaderName. Known names match ASCII-case-insensitively; any other valid name is kept verbatim as an Other name. Invalid input (empty, or a character outside [A-Za-z0-9_-]) gives Other("").

func HeaderNameFromID

func HeaderNameFromID(id HeaderID) HeaderName

HeaderNameFromID maps an id back to a name. Ids with no reverse mapping (HeaderIDOther, Dkim2Signature, MessageInstance, out of range) yield HeaderOther("").

func HeaderOther

func HeaderOther(name string) HeaderName

HeaderOther returns an arbitrary header name.

func ParseHeaderName

func ParseHeaderName(data string) (HeaderName, bool)

ParseHeaderName parses and validates a standalone header-name string, returning the matched well-known HeaderName or an Other name holding the input unchanged. It reports false when the input is empty or contains a character outside [A-Za-z0-9_-].

func (HeaderName) Equal

func (h HeaderName) Equal(o HeaderName) bool

Equal compares two header names. Known names match only the same known name; Other names compare ASCII-case-insensitively. A known name never equals an Other name even when the text matches: Other("From") != From.

func (HeaderName) ID

func (h HeaderName) ID() HeaderID

ID returns the header's numeric id (HeaderIDOther for arbitrary names).

func (HeaderName) IsMimeHeader

func (h HeaderName) IsMimeHeader() bool

IsMimeHeader reports whether this is one of the Content-* MIME headers.

func (HeaderName) IsOther

func (h HeaderName) IsOther() bool

IsOther reports whether this is an arbitrary (non-registered) header name.

func (HeaderName) Len

func (h HeaderName) Len() int

Len returns the byte length of the display name.

func (HeaderName) MapKey

func (h HeaderName) MapKey() HeaderName

MapKey normalizes the name for use as a map key: Other names are ASCII-lowercased so lookups agree with Equal.

func (HeaderName) MarshalJSON

func (h HeaderName) MarshalJSON() ([]byte, error)

MarshalJSON writes known names as their snake_case string and Other names as {"other": name}, case preserved.

func (HeaderName) Other

func (h HeaderName) Other() string

Other returns the arbitrary header name ("" for known names).

func (HeaderName) StaticString

func (h HeaderName) StaticString() string

StaticString is like String but returns "" for Other names.

func (HeaderName) String

func (h HeaderName) String() string

String returns the display name: the canonical spelling for known headers ("Message-ID", "MIME-Version", ...), or the Other name unchanged.

func (*HeaderName) UnmarshalJSON

func (h *HeaderName) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes either form of HeaderName.

type HeaderSlice

type HeaderSlice []Header

HeaderSlice adds lookups to a []Header such as MessagePart.Headers. It scans in reverse, so a repeated header resolves to its LAST occurrence.

func (HeaderSlice) Header

func (hs HeaderSlice) Header(name HeaderName) *Header

Header returns the last header matching name, or nil. Matching uses HeaderName.Equal (ASCII-case-insensitive for Other names).

func (HeaderSlice) HeaderValue

func (hs HeaderSlice) HeaderValue(name HeaderName) *HeaderValue

HeaderValue returns the value of the last header matching name, or nil.

type HeaderValue

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

HeaderValue is a parsed header value. The zero value is Empty.

func AddressValue

func AddressValue(a Address) HeaderValue

AddressValue returns an Address header value.

func ContentTypeValue

func ContentTypeValue(ct ContentType) HeaderValue

ContentTypeValue returns a ContentType header value.

func DateTimeValue

func DateTimeValue(dt DateTime) HeaderValue

DateTimeValue returns a DateTime header value.

func EmptyValue

func EmptyValue() HeaderValue

EmptyValue returns an empty header value.

func ReceivedValue

func ReceivedValue(r Received) HeaderValue

ReceivedValue returns a Received header value.

func TextListValue

func TextListValue(l []string) HeaderValue

TextListValue returns a TextList header value.

func TextValue

func TextValue(s string) HeaderValue

TextValue returns a Text header value.

func (*HeaderValue) AsAddress

func (v *HeaderValue) AsAddress() *Address

AsAddress returns the Address value, or nil for any other variant.

func (*HeaderValue) AsContentType

func (v *HeaderValue) AsContentType() *ContentType

AsContentType returns the ContentType value, or nil for any other variant.

func (*HeaderValue) AsDateTime

func (v *HeaderValue) AsDateTime() *DateTime

AsDateTime returns the DateTime value, or nil for any other variant.

func (*HeaderValue) AsReceived

func (v *HeaderValue) AsReceived() *Received

AsReceived returns the Received value, or nil for any other variant.

func (*HeaderValue) AsText

func (v *HeaderValue) AsText() (string, bool)

AsText returns the text of a Text value, or the LAST element of a TextList. Last, not first: the header getters rely on it.

func (*HeaderValue) AsTextList

func (v *HeaderValue) AsTextList() ([]string, bool)

AsTextList returns the TextList value, promoting a Text value to a single-element list.

func (*HeaderValue) IsEmpty

func (v *HeaderValue) IsEmpty() bool

IsEmpty reports whether the value is the Empty variant.

func (*HeaderValue) Kind

func (v *HeaderValue) Kind() HeaderValueKind

Kind returns the value variant.

func (*HeaderValue) Len

func (v *HeaderValue) Len() int

Len is a heuristic content length. DateTime is always 24 and Received always 1. Address sums the name and address bytes of every flattened Addr; group display names do not count.

func (HeaderValue) MarshalJSON

func (v HeaderValue) MarshalJSON() ([]byte, error)

MarshalJSON serializes the externally tagged HeaderValue variants; the unit variant Empty is the bare string "Empty".

func (*HeaderValue) ToText

func (v *HeaderValue) ToText() (string, bool)

ToText returns the text of a Text value only. Unlike AsText, a TextList yields false.

func (*HeaderValue) UnmarshalJSON

func (v *HeaderValue) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the externally tagged HeaderValue variants.

func (*HeaderValue) UnwrapAddress

func (v *HeaderValue) UnwrapAddress() Address

UnwrapAddress returns the Address value, or panics.

func (*HeaderValue) UnwrapContentType

func (v *HeaderValue) UnwrapContentType() ContentType

UnwrapContentType returns the ContentType value, or panics.

func (*HeaderValue) UnwrapDateTime

func (v *HeaderValue) UnwrapDateTime() DateTime

UnwrapDateTime returns the DateTime value, or panics.

func (*HeaderValue) UnwrapReceived

func (v *HeaderValue) UnwrapReceived() Received

UnwrapReceived returns the Received value, or panics.

func (*HeaderValue) UnwrapText

func (v *HeaderValue) UnwrapText() string

UnwrapText returns the Text value, or panics.

func (*HeaderValue) UnwrapTextList

func (v *HeaderValue) UnwrapTextList() []string

UnwrapTextList returns the TextList value, promoting a Text value to a single-element list, or panics.

type HeaderValueKind

type HeaderValueKind uint8

HeaderValueKind discriminates the variants of a HeaderValue.

const (
	// HeaderValueKindEmpty is the zero value.
	HeaderValueKindEmpty HeaderValueKind = iota
	HeaderValueKindText
	HeaderValueKindTextList
	HeaderValueKindAddress
	HeaderValueKindDateTime
	HeaderValueKindContentType
	HeaderValueKindReceived
)

type Host

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

Host is a hostname or IP address in a Received header.

func HostIP

func HostIP(ip netip.Addr) Host

HostIP returns an IP address Host.

func HostName

func HostName(name string) Host

HostName returns a hostname Host.

func (*Host) IP

func (h *Host) IP() netip.Addr

IP returns the IP address (the zero Addr for name hosts).

func (*Host) IsIP

func (h *Host) IsIP() bool

IsIP reports whether the host is an IP address.

func (Host) MarshalJSON

func (h Host) MarshalJSON() ([]byte, error)

MarshalJSON serializes the externally tagged Host variants: {"Name": "host"} or {"IpAddr": "1.2.3.4"}.

func (*Host) Name

func (h *Host) Name() string

Name returns the hostname ("" for IP hosts).

func (Host) String

func (h Host) String() string

String returns the hostname, or the IP address in canonical form.

func (*Host) UnmarshalJSON

func (h *Host) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the externally tagged Host variants.

type Message

type Message struct {
	// HTMLBody, TextBody and Attachments hold indexes into Parts.
	HTMLBody    []int
	TextBody    []int
	Attachments []int

	Parts []MessagePart

	// RawMessage is the raw message bytes this Message was parsed from.
	// Parts reference it by offset. It is not serialized to JSON.
	RawMessage []byte
}

Message is a parsed RFC5322/RFC822 message.

Example (Attachments)

ExampleMessage_attachments walks every attachment, descending into nested messages. It prints names rather than writing files, to keep the example free of file-system side effects.

package main

import (
	"fmt"

	"github.com/yokohola/mailparser"
)

func main() {
	input := []byte(`From: Art Vandelay <art@vandelay.com> (Vandelay Industries)
To: "Colleagues": "James Smythe" <james@vandelay.com>; Friends:
    jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>;
Date: Sat, 20 Nov 2021 14:22:01 -0800
Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
Content-Type: multipart/mixed; boundary="festivus";

--festivus
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: base64

PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle
HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm
cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8
gJiN4MjYzQTs8L3A+PC9odG1sPg==
--festivus
Content-Type: message/rfc822; name="Exporting my book about coffee tables.eml"
Content-Disposition: inline; filename="Exporting my book about coffee tables.eml"  
Content-Transfer-Encoding: 7bit

From: "Cosmo Kramer" <kramer@kramerica.com>
Subject: Exporting my book about coffee tables
Content-Type: multipart/mixed; boundary="giddyup";

--giddyup
Content-Type: text/plain; charset="utf-16"
Content-Transfer-Encoding: quoted-printable

=FF=FE=0C!5=D8"=DD5=D8)=DD5=D8-=DD =005=D8*=DD5=D8"=DD =005=D8"=
=DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD =
=005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"=
=DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00
--giddyup
Content-Type: image/gif; name*1="about "; name*0="Book ";
              name*2*=utf-8''%e2%98%95 tables.gif
Content-Transfer-Encoding: Base64
Content-Disposition: attachment

R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
--giddyup--
--festivus--
`)

	printAttachments(mailparser.NewMessageParser().Parse(input))
}

// printAttachments prints the name of every attachment it finds.
func printAttachments(message *mailparser.Message) {
	for attachment := range message.AllAttachments() {
		if !attachment.IsMessage() {
			name, ok := attachment.AttachmentName()
			if !ok {
				name = "Untitled"
			}
			fmt.Println(name)
		} else {
			printAttachments(attachment.Message())
		}
	}
}

func (*Message) AllAttachments

func (m *Message) AllAttachments() iter.Seq[*MessagePart]

AllAttachments iterates over the attachment parts, stopping early on a bad part id. Named All* because Attachments is already the index field.

func (*Message) AllBcc

func (m *Message) AllBcc() iter.Seq[*Address]

AllBcc iterates over all Bcc headers as Addresses in message order. RFC5322 section 4.5.3 allows the header to repeat.

func (*Message) AllCc

func (m *Message) AllCc() iter.Seq[*Address]

AllCc iterates over all Cc headers as Addresses in message order. RFC5322 section 4.5.3 allows the header to repeat.

func (*Message) AllTo

func (m *Message) AllTo() iter.Seq[*Address]

AllTo iterates over all To headers as Addresses in message order. RFC5322 section 4.5.3 allows the header to repeat.

func (*Message) Attachment

func (m *Message) Attachment(pos int) *MessagePart

Attachment returns the pos-th attachment part, or nil.

func (*Message) AttachmentCount

func (m *Message) AttachmentCount() int

AttachmentCount returns the number of attachments.

func (*Message) AttachmentName

func (m *Message) AttachmentName() (string, bool)

AttachmentName returns the attachment file name, if any.

func (*Message) Bcc

func (m *Message) Bcc() *Address

Bcc returns the last Bcc header as an Address.

func (*Message) BodyHTML

func (m *Message) BodyHTML(pos int) (string, bool)

BodyHTML returns the pos-th HTML body part as text/html. An HTML part comes back as-is, a Text part is converted with TextToHTML, and any other part type yields false.

func (*Message) BodyPreview

func (m *Message) BodyPreview(previewLen int) (string, bool)

BodyPreview returns a preview of the message body of at most previewLen bytes. Text bodies take absolute priority: if TextBody is non-empty but its first part is unusable, the result is false without falling back to HTML.

func (*Message) BodyText

func (m *Message) BodyText(pos int) (string, bool)

BodyText returns the pos-th text body part as text/plain. A Text part comes back as-is, an HTML part is converted with HTMLToText, and any other part type yields false.

func (*Message) Cc

func (m *Message) Cc() *Address

Cc returns the last Cc header as an Address.

func (*Message) Comments

func (m *Message) Comments() *HeaderValue

Comments returns the last Comments value, or Empty.

func (*Message) ContentDescription

func (m *Message) ContentDescription() (string, bool)

ContentDescription returns the root part's Content-Description field.

func (*Message) ContentDisposition

func (m *Message) ContentDisposition() *ContentType

ContentDisposition returns the root part's Content-Disposition field.

func (*Message) ContentID

func (m *Message) ContentID() (string, bool)

ContentID returns the root part's Content-ID field.

func (*Message) ContentLanguage

func (m *Message) ContentLanguage() *HeaderValue

ContentLanguage returns the root part's Content-Language value, or the Empty value (never nil).

func (*Message) ContentLocation

func (m *Message) ContentLocation() (string, bool)

ContentLocation returns the root part's Content-Location field.

func (*Message) ContentTransferEncoding

func (m *Message) ContentTransferEncoding() (string, bool)

ContentTransferEncoding returns the root part's Content-Transfer-Encoding field.

func (*Message) ContentType

func (m *Message) ContentType() *ContentType

ContentType returns the root part's Content-Type field.

func (*Message) Date

func (m *Message) Date() *DateTime

Date returns the DateTime of the last Date header, or nil.

func (*Message) From

func (m *Message) From() *Address

From returns the last From header as an Address.

func (*Message) HTMLBodies

func (m *Message) HTMLBodies() iter.Seq[*MessagePart]

HTMLBodies iterates over the HTML body parts.

func (*Message) HTMLBodyCount

func (m *Message) HTMLBodyCount() int

HTMLBodyCount returns the number of HTML body parts.

func (*Message) HTMLPart

func (m *Message) HTMLPart(pos int) *MessagePart

HTMLPart returns the pos-th inline HTML body part, or nil.

func (*Message) Header

func (m *Message) Header(name HeaderName) *HeaderValue

Header returns the value of the last header matching name, or nil.

func (*Message) HeaderAs

func (m *Message) HeaderAs(name HeaderName, form HeaderForm) []HeaderValue

HeaderAs re-parses EVERY header matching name from its raw bytes according to form, in message order. A header whose offsets fall outside RawMessage contributes an Empty value, so there is always one entry per match.

func (*Message) HeaderRaw

func (m *Message) HeaderRaw(name HeaderName) (string, bool)

HeaderRaw returns the raw text of the last header matching name. ok is false when the header is absent or its raw bytes are not valid UTF-8.

func (*Message) HeaderValues

func (m *Message) HeaderValues(name HeaderName) iter.Seq[*HeaderValue]

HeaderValues iterates over the values of ALL headers matching name, in message order.

func (*Message) Headers

func (m *Message) Headers() []Header

Headers returns all root-part headers in message order.

func (*Message) HeadersRaw

func (m *Message) HeadersRaw() iter.Seq2[string, string]

HeadersRaw iterates over (display name, raw text) pairs for every header in message order. Headers whose raw bytes are not valid UTF-8 are silently skipped.

func (*Message) InReplyTo

func (m *Message) InReplyTo() *HeaderValue

InReplyTo returns the last In-Reply-To value, or Empty.

func (*Message) IsContentType

func (m *Message) IsContentType(ctype, subtype string) bool

IsContentType reports whether the Content-Type matches type and subtype.

func (*Message) IsEmpty

func (m *Message) IsEmpty() bool

IsEmpty reports whether no header field was successfully parsed (the message has no parts).

func (*Message) Keywords

func (m *Message) Keywords() *HeaderValue

Keywords returns the last Keywords value, or Empty.

func (*Message) ListArchive

func (m *Message) ListArchive() *HeaderValue

ListArchive returns the last List-Archive value, or the Empty value (never nil).

func (*Message) ListHelp

func (m *Message) ListHelp() *HeaderValue

ListHelp returns the last List-Help value, or Empty.

func (*Message) ListID

func (m *Message) ListID() *HeaderValue

ListID returns the last List-ID value, or Empty.

func (*Message) ListOwner

func (m *Message) ListOwner() *HeaderValue

ListOwner returns the last List-Owner value, or Empty.

func (*Message) ListPost

func (m *Message) ListPost() *HeaderValue

ListPost returns the last List-Post value, or Empty.

func (*Message) ListSubscribe

func (m *Message) ListSubscribe() *HeaderValue

ListSubscribe returns the last List-Subscribe value, or the Empty value (never nil).

func (*Message) ListUnsubscribe

func (m *Message) ListUnsubscribe() *HeaderValue

ListUnsubscribe returns the last List-Unsubscribe value, or Empty.

func (Message) MarshalJSON

func (m Message) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form; RawMessage is never emitted.

func (*Message) MessageID

func (m *Message) MessageID() (string, bool)

MessageID returns the text of the last Message-ID header.

func (*Message) MimeVersion

func (m *Message) MimeVersion() *HeaderValue

MimeVersion returns the last MIME-Version value, or the Empty value (never nil).

func (*Message) Part

func (m *Message) Part(pos int) *MessagePart

Part returns the message part at pos, or nil.

func (*Message) Received

func (m *Message) Received() *Received

Received returns the LAST Received header, or nil.

func (*Message) ReceivedAll

func (m *Message) ReceivedAll() iter.Seq[*Received]

ReceivedAll iterates over all Received headers in the order they appear in the message.

func (*Message) References

func (m *Message) References() *HeaderValue

References returns the last References value, or Empty.

func (*Message) RemoveHeader

func (m *Message) RemoveHeader(name HeaderName) (HeaderValue, bool)

RemoveHeader removes the FIRST header matching name and returns its value. The LAST header moves into the freed slot, so order is not preserved.

func (*Message) ReplyTo

func (m *Message) ReplyTo() *Address

ReplyTo returns the last Reply-To header as an Address.

func (*Message) ResentBcc

func (m *Message) ResentBcc() *Address

ResentBcc returns the last Resent-Bcc header as an Address.

func (*Message) ResentCc

func (m *Message) ResentCc() *Address

ResentCc looks up the Resent-To header, NOT Resent-Cc. That is a long-standing bug kept for compatibility; Resent-Cc is unreachable here.

func (*Message) ResentDate

func (m *Message) ResentDate() *HeaderValue

ResentDate returns the last Resent-Date value, or Empty.

func (*Message) ResentFrom

func (m *Message) ResentFrom() *Address

ResentFrom returns the last Resent-From header as an Address.

func (*Message) ResentMessageID

func (m *Message) ResentMessageID() *HeaderValue

ResentMessageID returns the last Resent-Message-ID value, or Empty.

func (*Message) ResentSender

func (m *Message) ResentSender() *Address

ResentSender returns the last Resent-Sender header as an Address.

func (*Message) ResentTo

func (m *Message) ResentTo() *Address

ResentTo returns the last Resent-To header as an Address.

func (*Message) ReturnAddress

func (m *Message) ReturnAddress() (string, bool)

ReturnAddress returns the return address from Return-Path or From. A Text Return-Path wins. A TextList Return-Path yields its LAST element, or nothing at all if the list is empty; it does not fall through. Otherwise the first From address's Address field is used, and if that Addr has no address there is no further fallback.

func (*Message) ReturnPath

func (m *Message) ReturnPath() *HeaderValue

ReturnPath returns the last Return-Path value, or Empty.

func (*Message) RootPart

func (m *Message) RootPart() *MessagePart

RootPart returns the root message part.

func (*Message) RootRawMessage

func (m *Message) RootRawMessage() []byte

RootRawMessage returns RawMessage[OffsetHeader:OffsetEnd] of Parts[0], or nil when out of range. Not the RawMessage field, which is the whole input.

func (*Message) Sender

func (m *Message) Sender() *Address

Sender returns the last Sender header as an Address.

func (*Message) Subject

func (m *Message) Subject() (string, bool)

Subject returns the text of the last Subject header.

func (*Message) TextBodies

func (m *Message) TextBodies() iter.Seq[*MessagePart]

TextBodies iterates over the text body parts.

func (*Message) TextBodyCount

func (m *Message) TextBodyCount() int

TextBodyCount returns the number of plain text body parts.

func (*Message) TextPart

func (m *Message) TextPart(pos int) *MessagePart

TextPart returns the pos-th inline text body part, or nil.

func (*Message) ThreadName

func (m *Message) ThreadName() (string, bool)

ThreadName returns the message thread name or "base subject" as defined in RFC5256 section 2.1; ok is false only when the message has no Subject.

func (*Message) To

func (m *Message) To() *Address

To returns the last To header as an Address.

func (*Message) UnmarshalJSON

func (m *Message) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form. Missing keys take their zero value and RawMessage is left nil.

type MessageParser

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

MessageParser is a configurable RFC5322/RFC822 message parser.

With no custom header parsers registered, every IANA-registered header defined in HeaderName is parsed with its corresponding parser and other headers are parsed raw. Registering even one custom parser replaces that built-in dispatch entirely: headers are then looked up in the custom map, falling back to the default parser.

Example (Custom)

ExampleMessageParser_custom builds parsers that handle only a chosen subset of headers.

package main

import (
	"github.com/yokohola/mailparser"
)

func main() {
	input := []byte(`From: Art Vandelay <art@vandelay.com> (Vandelay Industries)
To: "Colleagues": "James Smythe" <james@vandelay.com>; Friends:
    jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>;
Date: Sat, 20 Nov 2021 14:22:01 -0800
Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
Content-Type: multipart/mixed; boundary="festivus";

--festivus
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: base64

PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle
HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm
cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8
gJiN4MjYzQTs8L3A+PC9odG1sPg==
--festivus
Content-Type: message/rfc822

From: "Cosmo Kramer" <kramer@kramerica.com>
Subject: Exporting my book about coffee tables
Content-Type: multipart/mixed; boundary="giddyup";

--giddyup
Content-Type: text/plain; charset="utf-16"
Content-Transfer-Encoding: quoted-printable

=FF=FE=0C!5=D8"=DD5=D8)=DD5=D8-=DD =005=D8*=DD5=D8"=DD =005=D8"=
=DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD =
=005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"=
=DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00
--giddyup
Content-Type: image/gif; name*1="about "; name*0="Book ";
              name*2*=utf-8''%e2%98%95 tables.gif
Content-Transfer-Encoding: Base64
Content-Disposition: attachment

R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
--giddyup--
--festivus--
`)

	// Parse only the message headers
	_ = mailparser.NewMessageParser().ParseHeaders(input).Headers()

	// Parse only the message body, ignoring all headers (except MIME
	// headers, which are required to parse the body)
	_ = mailparser.NewMessageParser().
		WithMimeHeaders().
		DefaultHeaderIgnore().
		Parse(input)

	// Parse only To, From, Date, and Subject headers. All other headers
	// are parsed as raw.
	_ = mailparser.NewMessageParser().
		WithMimeHeaders().
		HeaderText(mailparser.HeaderSubject).
		HeaderAddress(mailparser.HeaderFrom).
		HeaderAddress(mailparser.HeaderTo).
		HeaderDate(mailparser.HeaderDate).
		DefaultHeaderRaw().
		Parse(input)
}

func NewMessageParser

func NewMessageParser() *MessageParser

NewMessageParser creates a message parser with the default settings:

  • IANA-registered headers defined in HeaderName are parsed with their corresponding parser.
  • Other headers (HeaderName Other) are parsed as raw.

func (*MessageParser) DefaultHeaderIgnore

func (p *MessageParser) DefaultHeaderIgnore() *MessageParser

DefaultHeaderIgnore ignores and skips parsing all other headers.

func (*MessageParser) DefaultHeaderRaw

func (p *MessageParser) DefaultHeaderRaw() *MessageParser

DefaultHeaderRaw parses all other headers as raw strings, no RFC 2047 decoding is done.

func (*MessageParser) DefaultHeaderText

func (p *MessageParser) DefaultHeaderText() *MessageParser

DefaultHeaderText parses all other headers as text decoding RFC 2047 encoded words.

func (*MessageParser) HeaderAddress

func (p *MessageParser) HeaderAddress(header HeaderName) *MessageParser

HeaderAddress parses a header as an address.

func (*MessageParser) HeaderCommaSeparated

func (p *MessageParser) HeaderCommaSeparated(header HeaderName) *MessageParser

HeaderCommaSeparated parses a header as a comma-separated list of values.

func (*MessageParser) HeaderContentType

func (p *MessageParser) HeaderContentType(header HeaderName) *MessageParser

HeaderContentType parses a header as a MIME Content-Type or Content-Disposition type.

func (*MessageParser) HeaderDate

func (p *MessageParser) HeaderDate(header HeaderName) *MessageParser

HeaderDate parses a header as a RFC 5322 date.

func (*MessageParser) HeaderID

func (p *MessageParser) HeaderID(header HeaderName) *MessageParser

HeaderID parses a header as an ID.

func (*MessageParser) HeaderRaw

func (p *MessageParser) HeaderRaw(header HeaderName) *MessageParser

HeaderRaw parses a header as a raw string, no RFC 2047 decoding is done.

func (*MessageParser) HeaderReceived

func (p *MessageParser) HeaderReceived(header HeaderName) *MessageParser

HeaderReceived parses a header as a received header.

func (*MessageParser) HeaderText

func (p *MessageParser) HeaderText(header HeaderName) *MessageParser

HeaderText parses a header as text decoding RFC 2047 encoded words.

func (*MessageParser) IgnoreHeader

func (p *MessageParser) IgnoreHeader(header HeaderName) *MessageParser

IgnoreHeader ignores and skips parsing a header. The header bytes are still consumed from the stream so subsequent offsets stay correct.

func (*MessageParser) Parse

func (p *MessageParser) Parse(rawMessage []byte) *Message

Parse parses a byte slice containing the RFC5322 raw message and returns a Message.

This function never panics, a best-effort is made to parse the message and if no headers are found nil is returned.

Example

ExampleMessageParser_Parse walks a parsed message's headers and bodies.

package main

import (
	"encoding/json"
	"fmt"

	"github.com/yokohola/mailparser"
)

func main() {
	input := []byte(`From: Art Vandelay <art@vandelay.com> (Vandelay Industries)
To: "Colleagues": "James Smythe" <james@vandelay.com>; Friends:
    jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>;
Date: Sat, 20 Nov 2021 14:22:01 -0800
Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
Content-Type: multipart/mixed; boundary="festivus";

--festivus
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: base64

PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle
HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm
cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8
gJiN4MjYzQTs8L3A+PC9odG1sPg==
--festivus
Content-Type: message/rfc822

From: "Cosmo Kramer" <kramer@kramerica.com>
Subject: Exporting my book about coffee tables
Content-Type: multipart/mixed; boundary="giddyup";

--giddyup
Content-Type: text/plain; charset="utf-16"
Content-Transfer-Encoding: quoted-printable

=FF=FE=0C!5=D8"=DD5=D8)=DD5=D8-=DD =005=D8*=DD5=D8"=DD =005=D8"=
=DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD =
=005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"=
=DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00
--giddyup
Content-Type: image/gif; name*1="about "; name*0="Book ";
              name*2*=utf-8''%e2%98%95 tables.gif
Content-Transfer-Encoding: Base64
Content-Disposition: attachment

R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
--giddyup--
--festivus--
`)

	message := mailparser.NewMessageParser().Parse(input)

	// Parses addresses (including comments), lists and groups
	from := message.From().First()
	fmt.Printf("%s <%s>\n", from.Name, from.Address)

	groups, _ := message.To().AsGroup()
	for _, group := range groups {
		for _, addr := range group.Addresses {
			fmt.Printf("%s: %s\n", group.Name, addr.Address)
		}
	}

	fmt.Println(message.Date().ToRFC3339())

	// RFC2047 support for encoded text in message readers
	subject, _ := message.Subject()
	fmt.Println(subject)

	// HTML and text body parts are returned conforming to RFC8621,
	// Section 4.1.4
	bodyHTML, _ := message.BodyHTML(0)
	fmt.Println(bodyHTML)

	// HTML parts are converted to plain text (and viceversa) when missing
	bodyText, _ := message.BodyText(0)
	fmt.Println(bodyText)

	// Supports nested messages as well as multipart/digest
	nestedMessage := message.Attachment(0).Message()

	nestedSubject, _ := nestedMessage.Subject()
	fmt.Println(nestedSubject)

	// Handles UTF-* as well as many legacy encodings
	nestedText, _ := nestedMessage.BodyText(0)
	fmt.Println(nestedText)

	nestedHTML, _ := nestedMessage.BodyHTML(0)
	fmt.Println(nestedHTML)

	nestedAttachment := nestedMessage.Attachment(0)

	fmt.Println(nestedAttachment.Len())

	// Full RFC2231 support for continuations and character sets
	attachmentName, _ := nestedAttachment.AttachmentName()
	fmt.Println(attachmentName)

	// Integrates with encoding/json
	pretty, _ := json.MarshalIndent(message, "", "  ")
	fmt.Println(string(pretty))
}

func (*MessageParser) ParseHeaders

func (p *MessageParser) ParseHeaders(rawMessage []byte) *Message

ParseHeaders parses a byte slice containing the RFC5322 raw message and returns a Message containing only the headers, or nil if no headers were parsed.

func (*MessageParser) WithAddressHeaders

func (p *MessageParser) WithAddressHeaders() *MessageParser

WithAddressHeaders registers parsers for all address headers:

  • From
  • Sender
  • Reply-To
  • To
  • Cc
  • Bcc
  • Resent-From
  • Resent-Sender
  • Resent-To
  • Resent-Cc
  • Resent-Bcc

func (*MessageParser) WithDateHeaders

func (p *MessageParser) WithDateHeaders() *MessageParser

WithDateHeaders registers parsers for all Date headers:

  • Date
  • Resent-Date

func (*MessageParser) WithMessageIDs

func (p *MessageParser) WithMessageIDs() *MessageParser

WithMessageIDs registers parsers for all ID headers:

  • Message-Id
  • In-Reply-To
  • References
  • Resent-Message-Id

func (*MessageParser) WithMimeHeaders

func (p *MessageParser) WithMimeHeaders() *MessageParser

WithMimeHeaders registers parsers for all MIME headers:

  • Content-Type
  • Content-Disposition
  • Content-Id
  • Content-Description
  • Content-Location
  • Content-Transfer-Encoding

Adding these MIME headers is required in order to parse message bodies.

func (*MessageParser) WithMinimalHeaders

func (p *MessageParser) WithMinimalHeaders() *MessageParser

WithMinimalHeaders registers parsers for all MIME headers plus:

  • Date
  • Subject
  • From
  • Reply-To
  • To
  • Cc
  • Bcc

func (*MessageParser) WithoutHeader

func (p *MessageParser) WithoutHeader(header HeaderName) *MessageParser

WithoutHeader removes a custom header parser.

type MessagePart

type MessagePart struct {
	Headers           []Header
	IsEncodingProblem bool
	Body              PartType
	// Encoding is the transfer encoding used by the raw body.
	// It is not serialized to JSON.
	Encoding     Encoding
	OffsetHeader int
	OffsetBody   int
	OffsetEnd    int
}

MessagePart is a single MIME part of a message.

func (*MessagePart) AttachmentName

func (p *MessagePart) AttachmentName() (string, bool)

AttachmentName returns the attachment file name, if any.

func (*MessagePart) ContentDescription

func (p *MessagePart) ContentDescription() (string, bool)

ContentDescription returns the part's Content-Description field.

func (*MessagePart) ContentDisposition

func (p *MessagePart) ContentDisposition() *ContentType

ContentDisposition returns the part's Content-Disposition field.

func (*MessagePart) ContentID

func (p *MessagePart) ContentID() (string, bool)

ContentID returns the part's Content-ID field.

func (*MessagePart) ContentLanguage

func (p *MessagePart) ContentLanguage() *HeaderValue

ContentLanguage returns the part's Content-Language value, or the Empty value (never nil).

func (*MessagePart) ContentLocation

func (p *MessagePart) ContentLocation() (string, bool)

ContentLocation returns the part's Content-Location field.

func (*MessagePart) ContentTransferEncoding

func (p *MessagePart) ContentTransferEncoding() (string, bool)

ContentTransferEncoding returns the part's Content-Transfer-Encoding field.

func (*MessagePart) ContentType

func (p *MessagePart) ContentType() *ContentType

ContentType returns the part's Content-Type field.

func (*MessagePart) Contents

func (p *MessagePart) Contents() []byte

Contents returns the body as bytes: text bytes for Text/HTML, decoded bytes for binary, the nested raw message for message parts, nil for multipart. Text/HTML callers get a copy, since Go cannot alias a string's bytes.

func (*MessagePart) IsBinary

func (p *MessagePart) IsBinary() bool

IsBinary reports whether the part is binary (Binary or InlineBinary).

func (*MessagePart) IsContentType

func (p *MessagePart) IsContentType(ctype, subtype string) bool

IsContentType reports whether the Content-Type matches type and subtype.

func (*MessagePart) IsEmpty

func (p *MessagePart) IsEmpty() bool

IsEmpty reports whether the body part is empty.

func (*MessagePart) IsMessage

func (p *MessagePart) IsMessage() bool

IsMessage reports whether the part is a nested message.

func (*MessagePart) IsMultipart

func (p *MessagePart) IsMultipart() bool

IsMultipart reports whether the part is multipart.

func (*MessagePart) IsText

func (p *MessagePart) IsText() bool

IsText reports whether the part is a text/* part (Text or HTML).

func (*MessagePart) IsTextHTML

func (p *MessagePart) IsTextHTML() bool

IsTextHTML reports whether the part is a text/html part.

func (*MessagePart) Len

func (p *MessagePart) Len() int

Len returns the body content length in bytes, 0 for multipart parts.

func (MessagePart) MarshalJSON

func (p MessagePart) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form; Encoding is never emitted.

func (*MessagePart) Message

func (p *MessagePart) Message() *Message

Message returns the nested message, or nil.

func (*MessagePart) RawBodyOffset

func (p *MessagePart) RawBodyOffset() int

RawBodyOffset returns the raw body offset of this part.

func (*MessagePart) RawEndOffset

func (p *MessagePart) RawEndOffset() int

RawEndOffset returns the raw end offset of this part.

func (*MessagePart) RawHeaderOffset

func (p *MessagePart) RawHeaderOffset() int

RawHeaderOffset returns the raw header offset of this part.

func (*MessagePart) RawLen

func (p *MessagePart) RawLen() int

RawLen returns OffsetEnd - OffsetHeader, saturating at 0 so the result is never negative.

func (*MessagePart) String

func (p *MessagePart) String() string

String returns the text contents, or "[no contents]" when there are none.

func (*MessagePart) SubParts

func (p *MessagePart) SubParts() ([]int, bool)

SubParts returns the child part indexes of a multipart part; ok is false for any other part type.

func (*MessagePart) TextContents

func (p *MessagePart) TextContents() (string, bool)

TextContents returns the body as a string. Binary and message parts convert only when their bytes are valid UTF-8, with no replacement characters. Multipart parts have no contents.

func (*MessagePart) UnmarshalJSON

func (p *MessagePart) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form; Encoding is left as EncodingNone.

type MessageStream

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

MessageStream is a cursor over a raw message, used by every parsing routine. The position may run past the end of the data, which Offset clamps. There is a single checkpoint/restore slot.

func NewMessageStream

func NewMessageStream(data []byte) *MessageStream

NewMessageStream returns a stream over data.

func (*MessageStream) Bytes

func (s *MessageStream) Bytes(start, end int) []byte

Bytes returns the data in [start, end).

func (*MessageStream) Checkpoint

func (s *MessageStream) Checkpoint()

Checkpoint records the current position for a later Restore.

func (*MessageStream) DecodeBase64MIME

func (s *MessageStream) DecodeBase64MIME(boundary []byte) (int, []byte)

DecodeBase64MIME decodes a base64 MIME body while scanning for the multipart boundary. It returns the offset where the encoded body ends (math.MaxInt when a non-empty boundary was not found) and the decoded bytes.

func (*MessageStream) DecodeBase64Word

func (s *MessageStream) DecodeBase64Word() ([]byte, bool)

DecodeBase64Word decodes an RFC2047 base64 encoded word up to the closing "?=" sequence, reporting false on invalid input.

func (*MessageStream) DecodeQuotedPrintableMIME

func (s *MessageStream) DecodeQuotedPrintableMIME(boundary []byte) (int, []byte)

DecodeQuotedPrintableMIME decodes a quoted-printable MIME body while scanning for the multipart boundary. It returns the offset where the encoded body ends (math.MaxInt when a non-empty boundary was not found) and the decoded bytes.

func (*MessageStream) DecodeQuotedPrintableWord

func (s *MessageStream) DecodeQuotedPrintableWord() ([]byte, bool)

DecodeQuotedPrintableWord decodes an RFC2047 quoted-printable encoded word up to the closing "?=" sequence, reporting false on invalid input.

func (*MessageStream) DecodeRFC2047

func (s *MessageStream) DecodeRFC2047() (string, bool)

DecodeRFC2047 decodes an RFC2047 encoded word ("charset?encoding?data?=", positioned after the leading "=?"), reporting false when the stream does not hold a valid encoded word.

func (*MessageStream) IsEOF

func (s *MessageStream) IsEOF() bool

IsEOF reports whether all data has been consumed.

func (*MessageStream) IsMultipartEnd

func (s *MessageStream) IsMultipartEnd() bool

IsMultipartEnd reports whether the bytes following a matched boundary mark the final "--boundary--" terminator. It consumes "\r\n", "--", "\n", or a whitespace run (via SkipCRLF) on recognized input, and restores the position on unrecognized input.

func (*MessageStream) Len

func (s *MessageStream) Len() int

Len returns the total data length.

func (*MessageStream) MIMEPart

func (s *MessageStream) MIMEPart(boundary []byte) (int, []byte)

MIMEPart reads one part body up to "--" followed by boundary, returning the offset where the content ends and the raw content bytes (a subslice of the stream data, never a copy). When a non-empty boundary is not found the position is restored to the part start and the returned offset is math.MaxInt (the "boundary not found" sentinel), with the bytes still holding the whole tail. An empty boundary consumes to EOF and returns the end offset.

func (*MessageStream) Next

func (s *MessageStream) Next() (byte, bool)

Next consumes and returns the next byte. The position keeps advancing even when reading past the end.

func (*MessageStream) NextIsSpace

func (s *MessageStream) NextIsSpace() bool

NextIsSpace consumes the next byte and reports whether it was a space or tab.

func (*MessageStream) Offset

func (s *MessageStream) Offset() int

Offset returns the current position, clamped to the data length.

func (*MessageStream) ParseAddress

func (s *MessageStream) ParseAddress() HeaderValue

ParseAddress parses one folded address header body into an Address value (a flat list or grouped addresses), or an Empty value when nothing was captured. It consumes the stream up to and including the header-terminating newline.

func (*MessageStream) ParseAndIgnore

func (s *MessageStream) ParseAndIgnore()

ParseAndIgnore consumes a header value without parsing it, advancing the stream past the unfolded header end.

func (*MessageStream) ParseCommaSeparated

func (s *MessageStream) ParseCommaSeparated() HeaderValue

ParseCommaSeparated parses a comma-separated header value (e.g. Keywords, Content-Language) with RFC2047 encoded-word support into Text, TextList or Empty.

func (*MessageStream) ParseContentType

func (s *MessageStream) ParseContentType() HeaderValue

ParseContentType parses an RFC2045 Content-Type or RFC2183 Content-Disposition header body into a ContentType, or Empty when no type is present. It handles RFC2231 parameter continuations and charset encoding, RFC2047 encoded words in parameter values, and comments.

func (*MessageStream) ParseDate

func (s *MessageStream) ParseDate() HeaderValue

ParseDate parses an RFC5322 Date header field, returning a DateTime value or Empty.

func (*MessageStream) ParseHeaders

func (s *MessageStream) ParseHeaders(conf *MessageParser, headers *[]Header) bool

ParseHeaders parses all headers of the current MIME part into headers, dispatching each value to conf's parser for that header name. It returns true when the blank line ending the header block was reached (a body follows) and false on EOF.

func (*MessageStream) ParseID

func (s *MessageStream) ParseID() HeaderValue

ParseID parses a Message-ID/References/In-Reply-To style header into one (Text) or many (TextList) angle-bracket-delimited ids, with a fallback for broken clients that omit the brackets.

func (*MessageStream) ParseRaw

func (s *MessageStream) ParseRaw() HeaderValue

ParseRaw captures the raw header value up to the unfolded header end as a single Text (edge whitespace trimmed, interior folding bytes preserved), or Empty. Unlike the other field parsers, text pending at end of input is still returned.

func (*MessageStream) ParseReceived

func (s *MessageStream) ParseReceived() HeaderValue

ParseReceived parses a Received header field, returning a Received value or Empty when not a single field could be extracted.

func (*MessageStream) ParseUnstructured

func (s *MessageStream) ParseUnstructured() HeaderValue

ParseUnstructured parses an unstructured header (Subject, Comments, ...) into a single Text with folding removed and RFC2047 encoded words decoded, or Empty.

func (*MessageStream) Peek

func (s *MessageStream) Peek() (byte, bool)

Peek returns the next byte without consuming it.

func (*MessageStream) PeekBytes

func (s *MessageStream) PeekBytes(n int) []byte

PeekBytes returns the next n bytes without consuming them, or nil if fewer than n remain.

func (*MessageStream) PeekChar

func (s *MessageStream) PeekChar(ch byte) bool

PeekChar reports whether the next byte equals ch.

func (*MessageStream) PeekNextIsSpace

func (s *MessageStream) PeekNextIsSpace() bool

PeekNextIsSpace reports whether the next byte is a space or tab.

func (*MessageStream) ReadHeaderName

func (s *MessageStream) ReadHeaderName() (HeaderName, bool)

ReadHeaderName consumes one header name from the stream, up to and including the ':' separator (or EOF), and returns the matched well-known HeaderName or an Other name holding the original text. It reports false on a '\n' before any usable colon or when no name characters were seen.

func (*MessageStream) Remaining

func (s *MessageStream) Remaining() int

Remaining returns the number of unread bytes.

func (*MessageStream) Reset

func (s *MessageStream) Reset()

Reset clears the checkpoint without moving.

func (*MessageStream) Restore

func (s *MessageStream) Restore()

Restore rewinds to the last Checkpoint and clears it.

func (*MessageStream) SeekEnd

func (s *MessageStream) SeekEnd()

SeekEnd moves the position to the end of the data.

func (*MessageStream) SeekNextPart

func (s *MessageStream) SeekNextPart(boundary []byte) bool

SeekNextPart scans forward for "--" followed by boundary. On a match the stream is left positioned right after the boundary text and it returns true; otherwise the original position is restored and it returns false. An empty boundary never matches. The match is not anchored to line starts.

func (*MessageStream) SeekNextPartOffset

func (s *MessageStream) SeekNextPartOffset(boundary []byte) (offset int, ok bool)

SeekNextPartOffset scans forward for "--" followed by boundary and, on a match, returns the offset where the content before the boundary line ends (the start of its terminating newline) with ok true, leaving the stream positioned after the boundary text. When no match is found the original position is restored and ok is false.

func (*MessageStream) SeekPartEnd

func (s *MessageStream) SeekPartEnd(boundary []byte) (int, bool)

SeekPartEnd advances past the current part, returning the offset where its content ends and whether the boundary was found. A nil boundary seeks to EOF and reports true. A non-nil boundary, empty or not, scans for "--" followed by the boundary; on EOF without a match it reports (Offset(), false) and leaves the stream at EOF without restoring.

func (*MessageStream) SkipBytes

func (s *MessageStream) SkipBytes(n int)

SkipBytes advances the position by n.

func (*MessageStream) SkipCRLF

func (s *MessageStream) SkipCRLF()

SkipCRLF consumes any run of '\r', space and '\t' bytes, stops after consuming a single '\n', and stops before any other byte.

func (*MessageStream) TryNextIsSpace

func (s *MessageStream) TryNextIsSpace() bool

TryNextIsSpace consumes the next byte only if it is a space or tab.

func (*MessageStream) TrySkip

func (s *MessageStream) TrySkip(bytes []byte) bool

TrySkip consumes bytes if the stream starts with them.

func (*MessageStream) TrySkipChar

func (s *MessageStream) TrySkipChar(ch byte) bool

TrySkipChar consumes the next byte if it equals ch.

type MimeHeaders

type MimeHeaders interface {
	// ContentDescription returns the Content-Description field.
	ContentDescription() (string, bool)
	// ContentDisposition returns the Content-Disposition field, or nil.
	ContentDisposition() *ContentType
	// ContentID returns the Content-ID field.
	ContentID() (string, bool)
	// ContentTransferEncoding returns the Content-Transfer-Encoding field.
	ContentTransferEncoding() (string, bool)
	// ContentType returns the Content-Type field, or nil.
	ContentType() *ContentType
	// ContentLanguage returns the Content-Language value, or the Empty
	// value (never nil).
	ContentLanguage() *HeaderValue
	// ContentLocation returns the Content-Location field.
	ContentLocation() (string, bool)
	// AttachmentName returns the attachment file name, if any: the
	// Content-Disposition "filename" attribute, falling back to the
	// Content-Type "name" attribute.
	AttachmentName() (string, bool)
	// IsContentType reports whether the Content-Type matches the given
	// type and subtype, ASCII-case-insensitively.
	IsContentType(ctype, subtype string) bool
}

MimeHeaders gives access to the MIME header fields. Implemented by *Message (via its root part) and *MessagePart.

type PartKind

type PartKind uint8

PartKind discriminates the body variants of a PartType.

const (
	// PartKindMultipart is the zero value: an empty multipart.
	PartKindMultipart PartKind = iota
	PartKindText
	PartKindHTML
	PartKindBinary
	PartKindInlineBinary
	PartKindMessage
)

type PartType

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

PartType is the decoded body of a message part: a text/* part, a text/html part, binary content, inline binary content, a nested RFC5322 message, or the list of child part indexes of a multipart part.

The zero value is an empty multipart.

func PartTypeBinary

func PartTypeBinary(b []byte) PartType

PartTypeBinary returns a binary body.

func PartTypeHTML

func PartTypeHTML(s string) PartType

PartTypeHTML returns a text/html body.

func PartTypeInlineBinary

func PartTypeInlineBinary(b []byte) PartType

PartTypeInlineBinary returns an inline binary body.

func PartTypeMessage

func PartTypeMessage(m *Message) PartType

PartTypeMessage returns a nested message body.

func PartTypeMultipart

func PartTypeMultipart(subparts []int) PartType

PartTypeMultipart returns a multipart body holding child part indexes.

func PartTypeText

func PartTypeText(s string) PartType

PartTypeText returns a text/* body.

func (*PartType) Binary

func (p *PartType) Binary() []byte

Binary returns the decoded bytes for Binary and InlineBinary parts.

func (*PartType) Kind

func (p *PartType) Kind() PartKind

Kind returns the body variant.

func (*PartType) Len

func (p *PartType) Len() int

Len returns the byte length of the body: text length for Text/HTML, byte length for the binary variants, 0 for Multipart. For a Message part it is the nested message's ENTIRE RawMessage buffer, unlike MessagePart.Len, which measures only the root-part slice.

func (PartType) MarshalJSON

func (p PartType) MarshalJSON() ([]byte, error)

MarshalJSON serializes the externally tagged PartType variants: {"Text": "..."}, {"Html": "..."}, {"Binary": [n, ...]}, {"InlineBinary": [n, ...]}, {"Message": {...}} and {"Multipart": [id, ...]}.

func (*PartType) Message

func (p *PartType) Message() *Message

Message returns the nested message, or nil.

func (*PartType) SubParts

func (p *PartType) SubParts() []int

SubParts returns the child part indexes of a multipart part, or nil.

func (*PartType) Text

func (p *PartType) Text() string

Text returns the decoded text for Text and HTML parts ("" otherwise).

func (*PartType) UnmarshalJSON

func (p *PartType) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the externally tagged PartType variants.

type Protocol

type Protocol uint8

Protocol is an IANA mail transmission type from a Received "with" clause.

const (
	// IANA Mail Transmission Types
	ProtocolSMTP Protocol = iota
	ProtocolESMTP
	ProtocolESMTPA
	ProtocolESMTPS
	ProtocolESMTPSA
	ProtocolLMTP
	ProtocolLMTPA
	ProtocolLMTPS
	ProtocolLMTPSA
	ProtocolMMS
	ProtocolUTF8SMTP
	ProtocolUTF8SMTPA
	ProtocolUTF8SMTPS
	ProtocolUTF8SMTPSA
	ProtocolUTF8LMTP
	ProtocolUTF8LMTPA
	ProtocolUTF8LMTPS
	ProtocolUTF8LMTPSA

	// Non-Standard Mail Transmission Types
	ProtocolHTTP
	ProtocolHTTPS
	ProtocolIMAP
	ProtocolPOP3
	ProtocolLocal // includes stdin, socket, etc.
)

func (Protocol) AsStr

func (p Protocol) AsStr() string

AsStr returns the display name of the protocol. It matches the JSON name, so that table is reused.

func (Protocol) MarshalJSON

func (p Protocol) MarshalJSON() ([]byte, error)

MarshalJSON serializes the bare variant identifier string.

func (Protocol) String

func (p Protocol) String() string

String is the same as AsStr.

func (*Protocol) UnmarshalJSON

func (p *Protocol) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the bare variant identifier string.

type Received

type Received struct {
	From       *Host
	FromIP     *netip.Addr
	FromIPRev  *string
	By         *Host
	For        *string
	With       *Protocol
	TLSVersion *TLSVersion
	TLSCipher  *string
	ID         *string
	Ident      *string
	Helo       *Host
	HeloCmd    *Greeting
	Via        *string
	Date       *DateTime
}

Received is a parsed Received header (RFC5321/RFC5322 trace field).

func (Received) MarshalJSON

func (r Received) MarshalJSON() ([]byte, error)

MarshalJSON writes the wire form.

func (*Received) UnmarshalJSON

func (r *Received) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the wire form.

type TLSVersion

type TLSVersion uint8

TLSVersion is a TLS protocol version from a Received header.

const (
	SSLv2 TLSVersion = iota
	SSLv3
	TLSv1_0
	TLSv1_1
	TLSv1_2
	TLSv1_3
	DTLSv1_0
	DTLSv1_2
	DTLSv1_3
)

The constant spellings are load-bearing: the JSON form ("TLSv1_2") and the display form ("TLSv1.2") both derive from them.

func (TLSVersion) AsStr

func (t TLSVersion) AsStr() string

AsStr returns the display name of the TLS version.

func (TLSVersion) MarshalJSON

func (t TLSVersion) MarshalJSON() ([]byte, error)

MarshalJSON serializes the bare variant identifier string.

func (TLSVersion) String

func (t TLSVersion) String() string

String is the same as AsStr.

func (*TLSVersion) UnmarshalJSON

func (t *TLSVersion) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the bare variant identifier string.

Directories

Path Synopsis
Package mailbox iterates over the two common on-disk mailbox formats.
Package mailbox iterates over the two common on-disk mailbox formats.

Jump to

Keyboard shortcuts

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