// Package webhook has data types used for webhooks about incoming and outgoing deliveries. // // See package webapi for details about the webapi and webhooks. // // Types [Incoming] and [Outgoing] represent the JSON bodies sent in the webhooks. // New fields may be added in the future, unrecognized fields should be ignored // when parsing for forward compatibility. package webhook import ( "strings" "time" "github.com/mjl-/mox/message" ) // OutgoingEvent is an activity for an outgoing delivery. Either generated by the // queue, or through an incoming DSN (delivery status notification) message. type OutgoingEvent string // note: outgoing hook events are in ../queue/hooks.go, ../mox-/config.go, ../queue.go and ../webapi/gendoc.sh. keep in sync. // todo: in future have more events: for spam complaints, perhaps mdn's. const ( // Message was accepted by a next-hop server. This does not necessarily mean the // message has been delivered in the mailbox of the user. EventDelivered OutgoingEvent = "delivered" // Outbound delivery was suppressed because the recipient address is on the // suppression list of the account, or a simplified/base variant of the address is. EventSuppressed OutgoingEvent = "suppressed" // A delivery attempt failed but delivery will be retried again later. EventDelayed OutgoingEvent = "delayed" // Delivery of the message failed and will not be tried again. Also see the // "Suppressing" field of [Outgoing]. EventFailed OutgoingEvent = "failed" // Message was relayed into a system that does not generate DSNs. Should only // happen when explicitly requested. EventRelayed OutgoingEvent = "relayed" // Message was accepted and is being delivered to multiple recipients (e.g. the // address was an alias/list), which may generate more DSNs. EventExpanded OutgoingEvent = "expanded" // Message was removed from the queue, e.g. canceled by admin/user. EventCanceled OutgoingEvent = "canceled" // An incoming message was received that was either a DSN with an unknown event // type ("action"), or an incoming non-DSN-message was received for the unique // per-outgoing-message address used for sending. EventUnrecognized OutgoingEvent = "unrecognized" ) // Outgoing is the payload sent to webhook URLs for events about outgoing deliveries. type Outgoing struct { Version int // Format of hook, currently 0. Event OutgoingEvent // Type of outgoing delivery event. DSN bool // If this event was triggered by a delivery status notification message (DSN). Suppressing bool // If true, this failure caused the address to be added to the suppression list. QueueMsgID int64 // ID of message in queue. FromID string // As used in MAIL FROM, can be empty, for incoming messages. MessageID string // From Message-Id header, as set by submitter or us, with enclosing <>. Subject string // Of original message. WebhookQueued time.Time // When webhook was first queued for delivery. SMTPCode int // Optional, for errors only, e.g. 451, 550. See package smtp for definitions. SMTPEnhancedCode string // Optional, for errors only, e.g. 5.1.1. Error string // Error message while delivering, or from DSN from remote, if any. Extra map[string]string // Extra fields set for message during submit, through webapi call or through X-Mox-Extra-* headers during SMTP submission. } // Incoming is the data sent to a webhook for incoming deliveries over SMTP. type Incoming struct { Version int // Format of hook, currently 0. // Message "From" header, typically has one address. From []NameAddress To []NameAddress CC []NameAddress BCC []NameAddress // Often empty, even if you were a BCC recipient. // Optional Reply-To header, typically absent or with one address. ReplyTo []NameAddress Subject string // Of Message-Id header, typically of the form "", includes <>. MessageID string // Optional, the message-id this message is a reply to. Includes <>. InReplyTo string // Optional, zero or more message-ids this message is a reply/forward/related to. // The last entry is the most recent/immediate message this is a reply to. Earlier // entries are the parents in a thread. Values include <>. References []string // Time in "Date" message header, can be different from time received. Date *time.Time // Contents of text/plain and/or text/html part (if any), with "\n" line-endings, // converted from "\r\n". Values are truncated to 1MB (1024*1024 bytes). Use webapi // MessagePartGet to retrieve the full part data. Text string HTML string // No files, can be large. Structure Structure // Parsed form of MIME message. Meta IncomingMeta // Details about message in storage, and SMTP transaction details. } type IncomingMeta struct { MsgID int64 // ID of message in storage, and to use in webapi calls like MessageGet. MailFrom string // Address used during SMTP "MAIL FROM" command. MailFromValidated bool // Whether SMTP MAIL FROM address was SPF-validated. MsgFromValidated bool // Whether address in message "From"-header was DMARC(-like) validated. RcptTo string // SMTP RCPT TO address used in SMTP. DKIMVerifiedDomains []string // Verified domains from DKIM-signature in message. Can be different domain than used in addresses. RemoteIP string // Where the message was delivered from. Received time.Time // When message was received, may be different from the Date header. MailboxName string // Mailbox where message was delivered to, based on configured rules. Defaults to "Inbox". // Whether this message was automated and should not receive automated replies. // E.g. out of office or mailing list messages. Automated bool } type NameAddress struct { Name string // Optional, human-readable "display name" of the addressee. Address string // Required, email address. } type Structure struct { ContentType string // Lower case, e.g. text/plain. ContentTypeParams map[string]string // Lower case keys, original case values, e.g. {"charset": "UTF-8"}. ContentID string // Can be empty. Otherwise, should be a value wrapped in <>'s. For use in HTML, referenced as URI `cid:...`. DecodedSize int64 // Size of content after decoding content-transfer-encoding. For text and HTML parts, this can be larger than the data returned since this size includes \r\n line endings. Parts []Structure // Subparts of a multipart message, possibly recursive. } // PartStructure returns a Structure for a parsed message part. func PartStructure(p *message.Part) Structure { parts := make([]Structure, len(p.Parts)) for i := range p.Parts { parts[i] = PartStructure(&p.Parts[i]) } s := Structure{ ContentType: strings.ToLower(p.MediaType + "/" + p.MediaSubType), ContentTypeParams: p.ContentTypeParams, ContentID: p.ContentID, DecodedSize: p.DecodedSize, Parts: parts, } // Replace nil map with empty map, for easier to use JSON. if s.ContentTypeParams == nil { s.ContentTypeParams = map[string]string{} } return s }