1package webapi
2
3import (
4 "context"
5 "io"
6 "time"
7
8 "github.com/mjl-/mox/webhook"
9)
10
11// todo future: we can have text and html templates, let submitters reference them along with parameters, and compose the message bodies ourselves.
12// todo future: generate api specs (e.g. openapi) for webapi
13// todo future: consider deprecating some of the webapi in favor of jmap
14
15// Methods of the webapi. More methods may be added in the future. See [Client]
16// for documentation.
17type Methods interface {
18 Send(ctx context.Context, request SendRequest) (response SendResult, err error)
19 SuppressionList(ctx context.Context, request SuppressionListRequest) (response SuppressionListResult, err error)
20 SuppressionAdd(ctx context.Context, request SuppressionAddRequest) (response SuppressionAddResult, err error)
21 SuppressionRemove(ctx context.Context, request SuppressionRemoveRequest) (response SuppressionRemoveResult, err error)
22 SuppressionPresent(ctx context.Context, request SuppressionPresentRequest) (response SuppressionPresentResult, err error)
23 MessageGet(ctx context.Context, request MessageGetRequest) (response MessageGetResult, err error)
24 MessageRawGet(ctx context.Context, request MessageRawGetRequest) (response io.ReadCloser, err error)
25 MessagePartGet(ctx context.Context, request MessagePartGetRequest) (response io.ReadCloser, err error)
26 MessageDelete(ctx context.Context, request MessageDeleteRequest) (response MessageDeleteResult, err error)
27 MessageFlagsAdd(ctx context.Context, request MessageFlagsAddRequest) (response MessageFlagsAddResult, err error)
28 MessageFlagsRemove(ctx context.Context, request MessageFlagsRemoveRequest) (response MessageFlagsRemoveResult, err error)
29 MessageMove(ctx context.Context, request MessageMoveRequest) (response MessageMoveResult, err error)
30}
31
32// Error indicates an API-related error.
33type Error struct {
34 // For programmatic handling. Common values: "user" for generic error by user,
35 // "server" for a server-side processing error, "badAddress" for malformed email
36 // addresses.
37 Code string
38
39 // Human readable error message.
40 Message string
41}
42
43// Error returns the human-readable error message.
44func (e Error) Error() string {
45 return e.Message
46}
47
48type NameAddress struct {
49 Name string // Optional, human-readable "display name" of the addressee.
50 Address string // Required, email address.
51}
52
53// Message is an email message, used both for outgoing submitted messages and
54// incoming messages.
55type Message struct {
56 // For sending, if empty, automatically filled based on authenticated user and
57 // account information. Outgoing messages are allowed maximum 1 From address,
58 // incoming messages can in theory have zero or multiple, but typically have just
59 // one.
60 From []NameAddress
61
62 // To/Cc/Bcc message headers. Outgoing messages are sent to all these addresses.
63 // All are optional, but there should be at least one addressee.
64 To []NameAddress
65 CC []NameAddress
66 // For submissions, BCC addressees receive the message but are not added to the
67 // headers of the outgoing message. Only the message saved the to the Sent mailbox
68 // gets the Bcc header prepended. For incoming messages, this is typically empty.
69 BCC []NameAddress
70
71 // Optional Reply-To header, where the recipient is asked to send replies to.
72 ReplyTo []NameAddress
73
74 // Message-ID from message header, should be wrapped in <>'s. For outgoing
75 // messages, a unique message-id is generated if empty.
76 MessageID string
77
78 // Optional. References to message-id's (including <>) of other messages, if this
79 // is a reply or forwarded message. References are from oldest (ancestor) to most
80 // recent message. For outgoing messages, if non-empty then In-Reply-To is set to
81 // the last element.
82 References []string
83
84 // Optional, set to time of submission for outgoing messages if nil.
85 Date *time.Time
86
87 // Subject header, optional.
88 Subject string
89
90 // For outgoing messages, at least text or HTML must be non-empty. If both are
91 // present, a multipart/alternative part is created. Lines must be
92 // \n-separated, automatically replaced with \r\n when composing the message.
93 // For parsed, incoming messages, values are truncated to 1MB (1024*1024 bytes).
94 // Use MessagePartGet to retrieve the full part data.
95 Text string
96 HTML string
97}
98
99// SendRequest submits a message to be delivered.
100type SendRequest struct {
101 // Message with headers and contents to compose. Additional headers and files can
102 // be added too (see below, and the use of multipart/form-data requests). The
103 // fields of Message are included directly in SendRequest. Required.
104 Message
105
106 // Metadata to associate with the delivery, through the queue, including webhooks
107 // about delivery events. Metadata can also be set with regular SMTP submission
108 // through message headers "X-Mox-Extra-<key>: <value>". Current behaviour is as
109 // follows, but this may change: 1. Keys are canonicalized, each dash-separated
110 // word changed to start with a capital. 2. Keys cannot be duplicated. 3. These
111 // headers are not removed when delivering.
112 Extra map[string]string
113
114 // Additional custom headers to include in outgoing message. Optional.
115 // Unless a User-Agent or X-Mailer header is present, a User-Agent is added.
116 Headers [][2]string
117
118 // Alternative files are added as (full) alternative representation of the text
119 // and/or html parts. Alternative files cause a part with content-type
120 // "multipart/alternative" to be added to the message. Optional.
121 AlternativeFiles []File
122
123 // Inline files are added to the message and should be displayed by mail clients as
124 // part of the message contents. Inline files cause a part with content-type
125 // "multipart/related" to be added to the message. Optional.
126 InlineFiles []File
127
128 // Attached files are added to the message and should be shown as files that can be
129 // saved. Attached files cause a part with content-type "multipart/mixed" to be
130 // added to the message. Optional.
131 AttachedFiles []File
132
133 // If absent/null, regular TLS requirements apply (opportunistic TLS, DANE,
134 // MTA-STS). If true, the SMTP REQUIRETLS extension is required, enforcing verified
135 // TLS along the delivery path. If false, TLS requirements are relaxed and
136 // DANE/MTA-STS policies may be ignored to increase the odds of successful but
137 // insecure delivery. Optional.
138 RequireTLS *bool
139
140 // If set, it should be a time in the future at which the first delivery attempt
141 // starts. Optional.
142 FutureRelease *time.Time
143
144 // Whether to store outgoing message in designated Sent mailbox (if configured).
145 SaveSent bool
146}
147
148type File struct {
149 Name string // Optional.
150 ContentType string // E.g. application/pdf or image/png, automatically detected if empty.
151 ContentID string // E.g. "<randomid>", for use in html email with "cid:<randomid>". Optional.
152 Data string // Base64-encoded contents of the file. Required.
153}
154
155// MessageMeta is returned as part of MessageGet.
156type MessageMeta struct {
157 Size int64 // Total size of raw message file.
158 DSN bool // Whether this message is a DSN.
159 Flags []string // Standard message flags like \seen, \answered, $forwarded, $junk, $nonjunk, and custom keywords.
160 MailFrom string // Address used during SMTP "MAIL FROM" command.
161 MailFromValidated bool // Whether SMTP MAIL FROM address was SPF-validated.
162 MsgFrom string // Address used in message "From" header.
163 MsgFromValidated bool // Whether address in message "From"-header was DMARC(-like) validated.
164 DKIMVerifiedDomains []string // Verified domains from DKIM-signature in message. Can be different domain than used in addresses.
165 RemoteIP string // Where the message was delivered from.
166 MailboxName string
167}
168
169type SendResult struct {
170 MessageID string // "<random>@<domain>", as added by submitter or automatically generated during submission.
171 Submissions []Submission // Messages submitted to queue for delivery. In order of To, CC, BCC fields in request.
172}
173
174type Submission struct {
175 Address string // From original recipient (to/cc/bcc).
176 QueueMsgID int64 // Of message added to delivery queue, later webhook calls reference this same ID.
177 FromID string // Unique ID used during delivery, later webhook calls reference this same FromID.
178}
179
180// Suppression is an address to which messages will not be delivered. Attempts to
181// deliver or queue will result in an immediate permanent failure to deliver.
182type Suppression struct {
183 ID int64
184 Created time.Time `bstore:"default now"`
185
186 // Suppression applies to this account only.
187 Account string `bstore:"nonzero,unique Account+BaseAddress"`
188
189 // Unicode. Address with fictional simplified localpart: lowercase, dots removed
190 // (gmail), first token before any "-" or "+" (typical catchall separator).
191 BaseAddress string `bstore:"nonzero"`
192
193 // Unicode. Address that caused this suppression.
194 OriginalAddress string `bstore:"nonzero"`
195
196 Manual bool
197 Reason string
198}
199
200type SuppressionListRequest struct{}
201type SuppressionListResult struct {
202 Suppressions []Suppression // Current suppressed addresses for account.
203}
204
205type SuppressionAddRequest struct {
206 EmailAddress string
207 Manual bool // Whether added manually or automatically.
208 Reason string // Free-form text.
209}
210type SuppressionAddResult struct{}
211
212type SuppressionRemoveRequest struct {
213 EmailAddress string
214}
215type SuppressionRemoveResult struct{}
216
217type SuppressionPresentRequest struct {
218 EmailAddress string
219}
220type SuppressionPresentResult struct {
221 Present bool
222}
223
224type MessageGetRequest struct {
225 MsgID int64
226}
227type MessageGetResult struct {
228 Message Message
229 Structure webhook.Structure // MIME structure.
230 Meta MessageMeta // Additional information about message and SMTP delivery.
231}
232
233type MessageRawGetRequest struct {
234 MsgID int64
235}
236
237type MessagePartGetRequest struct {
238 MsgID int64
239
240 // Indexes into MIME parts, e.g. [0, 2] first dereferences the first element in a
241 // multipart message, then the 3rd part within that first element.
242 PartPath []int
243}
244
245type MessageDeleteRequest struct {
246 MsgID int64
247}
248type MessageDeleteResult struct{}
249
250type MessageFlagsAddRequest struct {
251 MsgID int64
252 Flags []string // Standard message flags like \seen, \answered, $forwarded, $junk, $nonjunk, and custom keywords.
253}
254type MessageFlagsAddResult struct{}
255
256type MessageFlagsRemoveRequest struct {
257 MsgID int64
258 Flags []string
259}
260type MessageFlagsRemoveResult struct{}
261
262type MessageMoveRequest struct {
263 MsgID int64
264 DestMailboxName string // E.g. "Inbox", must already exist.
265}
266type MessageMoveResult struct{}
267