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. Unicode.
161 MailFromValidated bool // Whether SMTP MAIL FROM address was SPF-validated.
162 RcptTo string // Address delivered to with SMTP "RCPT TO" command. Unicode.
163 MsgFrom string // Address used in message "From" header.
164 MsgFromValidated bool // Whether address in message "From"-header was DMARC(-like) validated.
165 DKIMVerifiedDomains []string // Verified domains from DKIM-signature in message. Can be different domain than used in addresses.
166 RemoteIP string // Where the message was delivered from.
167 MailboxName string
168}
169
170type SendResult struct {
171 MessageID string // "<random>@<domain>", as added by submitter or automatically generated during submission.
172 Submissions []Submission // Messages submitted to queue for delivery. In order of To, CC, BCC fields in request.
173}
174
175type Submission struct {
176 Address string // From original recipient (to/cc/bcc).
177 QueueMsgID int64 // Of message added to delivery queue, later webhook calls reference this same ID.
178 FromID string // Unique ID used during delivery, later webhook calls reference this same FromID.
179}
180
181// Suppression is an address to which messages will not be delivered. Attempts to
182// deliver or queue will result in an immediate permanent failure to deliver.
183type Suppression struct {
184 ID int64
185 Created time.Time `bstore:"default now"`
186
187 // Suppression applies to this account only.
188 Account string `bstore:"nonzero,unique Account+BaseAddress"`
189
190 // Unicode. Address with fictional simplified localpart: lowercase, dots removed
191 // (gmail), first token before any "-" or "+" (typical catchall separator).
192 BaseAddress string `bstore:"nonzero"`
193
194 // Unicode. Address that caused this suppression.
195 OriginalAddress string `bstore:"nonzero"`
196
197 Manual bool
198 Reason string
199}
200
201type SuppressionListRequest struct{}
202type SuppressionListResult struct {
203 Suppressions []Suppression // Current suppressed addresses for account.
204}
205
206type SuppressionAddRequest struct {
207 EmailAddress string
208 Manual bool // Whether added manually or automatically.
209 Reason string // Free-form text.
210}
211type SuppressionAddResult struct{}
212
213type SuppressionRemoveRequest struct {
214 EmailAddress string
215}
216type SuppressionRemoveResult struct{}
217
218type SuppressionPresentRequest struct {
219 EmailAddress string
220}
221type SuppressionPresentResult struct {
222 Present bool
223}
224
225type MessageGetRequest struct {
226 MsgID int64
227}
228type MessageGetResult struct {
229 Message Message
230 Structure webhook.Structure // MIME structure.
231 Meta MessageMeta // Additional information about message and SMTP delivery.
232}
233
234type MessageRawGetRequest struct {
235 MsgID int64
236}
237
238type MessagePartGetRequest struct {
239 MsgID int64
240
241 // Indexes into MIME parts, e.g. [0, 2] first dereferences the first element in a
242 // multipart message, then the 3rd part within that first element.
243 PartPath []int
244}
245
246type MessageDeleteRequest struct {
247 MsgID int64
248}
249type MessageDeleteResult struct{}
250
251type MessageFlagsAddRequest struct {
252 MsgID int64
253 Flags []string // Standard message flags like \seen, \answered, $forwarded, $junk, $nonjunk, and custom keywords.
254}
255type MessageFlagsAddResult struct{}
256
257type MessageFlagsRemoveRequest struct {
258 MsgID int64
259 Flags []string
260}
261type MessageFlagsRemoveResult struct{}
262
263type MessageMoveRequest struct {
264 MsgID int64
265 DestMailboxName string // E.g. "Inbox", must already exist.
266}
267type MessageMoveResult struct{}
268