mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 16:33:47 +03:00
164 lines
7.3 KiB
Go
164 lines
7.3 KiB
Go
|
// 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 "<random@hostname>", 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
|
||
|
}
|