msgs

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2024 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package msgs provides the Brief system's mail message abstraction including the Docket together with mail message parsing.

Index

Constants

View Source
const DocketTimeLayout = time.RFC3339Nano

Variables

View Source
var ErrDuplicateMessageId = errors.New("duplicate Message-Id")
View Source
var ErrInvalidDocket = errors.New("invalid docket")
View Source
var ErrNoUniqueMessageId = errors.New("no unique Message-Id")
View Source
var ErrNonUniqueMessageId = errors.New("non-unique message id")
View Source
var ErrUnexpectedMessageId = errors.New("unexpected Message-Id")

Functions

func ModTime

func ModTime(docket Docket, date time.Time) time.Time

func ParseAddressList

func ParseAddressList(text string) ([]*netmail.Address, error)

does no partial parse: err==nil iff []*netmail.Address!=nil

func ParseDate

func ParseDate(text string) (time.Time, error)

Expands (i.e. allows incompetent syntactical constructs found in the wild) on netmail.ParseDate (which claims to implement RFC 5322).

func SetMailbox

func SetMailbox(setdocketer interface {
	SetDocket(MessageId, time.Time, string, []byte) error
}, t time.Time, mid MessageId, mailbox string) error

func SetMailboxRubbish

func SetMailboxRubbish(setdocketer interface {
	SetDocket(MessageId, time.Time, string, []byte) error
}, t time.Time, mid MessageId) error

Types

type Docket

type Docket []TKV

Docket represents brief's locally-added message metadata. Unlike e.g. a raw RFC 822 mail's "Date:" header field, all information inside the Docket can be trusted. Using sacks/fssack, the docket is stored *alongside the original message*. To accurately transmit the above described trust, "X-Brief" and "X-Brief-*" headers are sanitised (see WithDocket). A docket is appended to, meaning with increasing index, (generally) tkv.T increases.

func WithoutDocket

func WithoutDocket(r io.Reader) (Docket, io.Reader, error)

func (Docket) Clone

func (docket Docket) Clone() Docket

Clone creates a deep clone.

func (Docket) Equal

func (docket Docket) Equal(other Docket) bool

func (Docket) Get

func (docket Docket) Get(k string) string

Get gets the most recent k-keyed docket entry.

func (Docket) IsHot

func (docket Docket) IsHot() bool

TODO "hot" is a bad name?

INBOX/ or OUTBOX/ residents are hot. Messages of indeterminate transfer-status are hot.

func (Docket) Mailbox

func (docket Docket) Mailbox() string

func (Docket) TransferStatus

func (docket Docket) TransferStatus() (string, *url.URL)

*url.URL may be nil

TODO think about a function like text.SplitTrimmed

type Docketed

type Docketed []byte

func WithDocket

func WithDocket(docket Docket, undocketed Undocketed) Docketed

type Message

type Message struct {
	// Id MUST correspond to the unique "Message-Id" field in Message.
	// Cf. Message.Contract
	Id MessageId

	// Docket holds is the Message's brief-specific associated metadata. This
	// is distinc from Message.
	Docket Docket

	// Message is this Message's byte-for-byte exact raw message, not including
	// the docket. This raw message may also contain un-sanitised, potentially
	// not trustworthy "X-Brief" and "X-Brief-*" headers.
	// Since a syntactically valid RFC 822 message contains a CRLF separating a
	// potentially empty header from a potentially empty body, a valid
	// Message's Message always has positive len.
	Message []byte
}

Message represents a Sack's atomic unit of content. A Message's fields MUST NOT be retained by a Sack to ensure value semantics.

Often, *Message is used. Nonetheless, read-only value semantics should always be applied.

func (*Message) Clone

func (msg *Message) Clone() *Message

func (*Message) Contract

func (msg *Message) Contract() error

Contract asserts internal type consistencies.

Most notably, Message.Id is asserted to be the unique Message-Id found in Message.Message.

func (*Message) Date

func (message *Message) Date() time.Time

When the message contains a unique, parsable "Date:" header, said field is parsed and returned. A message's "Date:" may be parsed to the zero time value. Else, the zero time value is returned. RFC 822 does not force messages to employ the "Date:" field, though the vast majority probably does.

func (*Message) MarshalBinary

func (msg *Message) MarshalBinary() ([]byte, error)

MarshalBinary builds a docketed message form. All information is preserved.

func (*Message) ModTime deprecated

func (message *Message) ModTime() time.Time

Deprecated: TODO Who would use this method? TODO Should not any docket's date ALWAYS be after the mail's "Date:"?

func (*Message) UnmarshalBinary

func (msg *Message) UnmarshalBinary() error

TODO

type MessageId

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

A Message-Id's e-mail-address-like appearance should not tempt to think its display name had any meaning. A Message-Id does not have a display name. MessageId.raw is in net/mail's eyes (*"net/mail".Address).Address.

MessageId has fully non-retaining value semantics and thus does not implement a "Clone()" method.

func GenerateMessageId

func GenerateMessageId(source *netmail.Address) (MessageId, error)

func ParseMessageId

func ParseMessageId(text string) (MessageId, error)

ParseMessageId parses a Message-Id field's body.

func ParseMessageIdList

func ParseMessageIdList(text string) ([]MessageId, error)

func UniqueMessageId

func UniqueMessageId(undocketed Undocketed) (MessageId, int64, int64, error)

UniqueMessageId extracts the unique (!) Message-Id.

func (MessageId) Chevroned

func (mid MessageId) Chevroned() *netmail.Address

func (MessageId) Equal

func (mid MessageId) Equal(other MessageId) bool

TODO think about domain case lowering?

func (MessageId) IsZero

func (mid MessageId) IsZero() bool

func (MessageId) MarshalBinary

func (mid MessageId) MarshalBinary() ([]byte, error)

interface { encoding.BinaryMarshaler; encoding.BinaryUnmarshaler } are required by sacks/fssacks.Cache.

func (MessageId) MarshalText

func (mid MessageId) MarshalText() ([]byte, error)

The chevrons are not an inherent part of the Message-Id but merely a common framing.

func (MessageId) RandomAlternate

func (mid MessageId) RandomAlternate() (MessageId, error)

func (MessageId) Unchevroned

func (mid MessageId) Unchevroned() string

TODO Is mid.raw guaranteed to be Stringer-suitable?

func (*MessageId) UnmarshalBinary

func (mid *MessageId) UnmarshalBinary(text []byte) error

func (*MessageId) UnmarshalText

func (mid *MessageId) UnmarshalText(text []byte) error

type ParsedMessage

type ParsedMessage struct {
	MessageId MessageId // may be .Zero()

	Date    time.Time // may be .Zero()
	Subject string
	XMailer string // includes other headers: "X-Mailer", "X-Mailman-Version", "X-Originating-Client", "X-Powered-By", etc. (TODO create a fieldnames category)

	From    []*netmail.Address // RFC 5322 3.6.2. allows multiple "From:"
	Sender  **netmail.Address  // RFC 5322 3.6.2. forbids multiple "Sender:"
	ReplyTo []*netmail.Address // RFC 5322 3.6.2. allows multiple "Reply-To:"

	InReplyTo  []MessageId // RFC 5322 3.6.4. allows multiple "In-Reply-To:"
	References []MessageId // RFC 5322 3.6.4. allows multiple "References:"

	DispositionNotificationTo []MessageId // TODO verify multiple values are permissible

	To  []*netmail.Address
	Cc  []*netmail.Address
	Bcc []*netmail.Address

	// syntactically invalid fields (i.e. unknown "charset")
	Undecodable []mailx.RawField
	// semantically invalid fields
	Unparseable []mailx.Field

	// ignored fields due to fieldnames.Category.Boring
	Boring []mailx.Field
	// extra, non-interpreted, non-boring fields
	Extra []mailx.Field

	// [2023-12-11, jfrech] TODO "Body" is not really a good name for the
	// parsed Body (for MIME multipart, the RFC 822 body is only an encoding).
	Body []*ParsedPart
}

*ParsedMessage holds a semantically broken open RFC 822/newer standards/MIME internet e-mail message. It is designed to admit its inabilities for ambiguous or incorrectly formatted values.

(*ParsedMessage).UnmarshalText: When a unique field is present more than once, all are put into Unparseable and the corresponding ParsedMessage struct field is set to its zero value.

*netmail.Address is never nil. **netmail.Address is used to fake a Maybe monad and thus may be nil.

TODO use for "brief compose"

func ParseMessage deprecated

func ParseMessage(rawmessage []byte) (*ParsedMessage, error)

Deprecated: TODO remove Convenience wrapper for (*ParsedMessage).UnmarshalText.

func (*ParsedMessage) UnmarshalText

func (parsedmessage *ParsedMessage) UnmarshalText(rawmessage []byte) error

A RFC 822-style message. No concepts like "Brief dockets" exist here.

type ParsedPart

type ParsedPart struct {
	Name string

	Mediatype string
	Params    map[string]string

	Header textproto.MIMEHeader

	// if text data, in UTF-8
	Body []byte
}

type TKV

type TKV struct {
	T time.Time
	K string
	V []byte
}

NOTE: Contrary to RFC-822-style message headers, dockets have in-built versioning requiring the most recent entry to be interpreted as the sole value: where some mail headers (e.g. "From: x\r\nFrom: y\r\n") can be catenated (e.g. "From: x, y\r\n"), docket values MUST NOT be catenated.

func MakeTKV

func MakeTKV(k string, v []byte) TKV

func TransferStatusOutgoing

func TransferStatusOutgoing(t time.Time) TKV

func TransferStatusRetrieved

func TransferStatusRetrieved(t time.Time, u *url.URL) TKV

func TransferStatusRetrieving

func TransferStatusRetrieving(t time.Time, u *url.URL) TKV

func (TKV) Clone

func (tkv TKV) Clone() TKV

func (TKV) Equal

func (tkv TKV) Equal(other TKV) bool

func (TKV) MarshalText

func (tkv TKV) MarshalText() ([]byte, error)

func (*TKV) UnmarshalText

func (tkv *TKV) UnmarshalText(text []byte) error

type Undocketed

type Undocketed []byte

Jump to

Keyboard shortcuts

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