io_jmap/rfc8621/email_submission.rs
1//! JMAP for Mail: EmailSubmission (RFC 8621 §7).
2
3use core::fmt;
4
5use alloc::{collections::BTreeMap, string::String, vec::Vec};
6
7use serde::{Deserialize, Serialize};
8
9pub mod cancel;
10pub mod get;
11pub mod query;
12pub mod set;
13
14/// JMAP for Mail Submission capability (RFC 8621 §7).
15pub const JMAP_SUBMISSION_CAPABILITY: &str = "urn:ietf:params:jmap:submission";
16
17/// The undo status of an email submission (RFC 8621 §7.1).
18#[derive(Clone, Debug, Serialize, Deserialize)]
19#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
20#[serde(rename_all = "camelCase")]
21pub enum JmapUndoStatus {
22 /// The submission may still be cancelled.
23 Pending,
24 /// The submission can no longer be cancelled.
25 Final,
26 /// The submission was cancelled.
27 Canceled,
28}
29
30impl fmt::Display for JmapUndoStatus {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::Pending => write!(f, "pending"),
34 Self::Final => write!(f, "final"),
35 Self::Canceled => write!(f, "canceled"),
36 }
37 }
38}
39
40/// A JMAP EmailSubmission object (RFC 8621 §7.1).
41///
42/// Represents a sending of an email from a particular identity.
43#[derive(Clone, Debug, Default, Serialize, Deserialize)]
44#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
45#[serde(rename_all = "camelCase")]
46pub struct JmapEmailSubmission {
47 /// Server-assigned ID.
48 pub id: Option<String>,
49 /// The identity to send as.
50 pub identity_id: Option<String>,
51 /// The ID of the email to send.
52 pub email_id: Option<String>,
53 /// The thread the email belongs to.
54 pub thread_id: Option<String>,
55 /// SMTP envelope to use for delivery.
56 pub envelope: Option<JmapEnvelope>,
57 /// Date/time the submission was made (RFC 3339).
58 pub send_at: Option<String>,
59 /// Current undo status: `"pending"`, `"final"`, or `"canceled"`.
60 pub undo_status: Option<JmapUndoStatus>,
61 /// Per-recipient delivery status.
62 pub delivery_status: Option<BTreeMap<String, JmapDeliveryStatus>>,
63 /// Blob IDs of DSN messages.
64 pub dsn_blob_ids: Option<Vec<String>>,
65 /// Blob IDs of MDN messages.
66 pub mdn_blob_ids: Option<Vec<String>>,
67}
68
69/// SMTP envelope for an email submission.
70#[derive(Clone, Debug, Serialize, Deserialize)]
71#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
72#[serde(rename_all = "camelCase")]
73pub struct JmapEnvelope {
74 /// MAIL FROM address and parameters.
75 pub mail_from: JmapEmailAddressWithParameters,
76 /// RCPT TO addresses and parameters.
77 pub rcpt_to: Vec<JmapEmailAddressWithParameters>,
78}
79
80/// An email address with optional SMTP parameters.
81#[derive(Clone, Debug, Serialize, Deserialize)]
82#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
83#[serde(rename_all = "camelCase")]
84pub struct JmapEmailAddressWithParameters {
85 /// The email address.
86 pub email: String,
87 /// SMTP parameters (e.g. `NOTIFY`, `ORCPT`).
88 pub parameters: Option<BTreeMap<String, Option<String>>>,
89}
90
91/// Delivery state of a single recipient (RFC 8621 §7.1.1).
92#[derive(Clone, Debug, Serialize, Deserialize)]
93#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
94#[serde(rename_all = "camelCase")]
95pub enum JmapDelivered {
96 /// The message is in a local mail queue.
97 Queued,
98 /// The message was successfully delivered.
99 Yes,
100 /// Delivery failed permanently.
101 No,
102 /// The delivery status is unknown.
103 Unknown,
104}
105
106/// Whether the email has been displayed to the recipient (RFC 8621 §7.1.1).
107#[derive(Clone, Debug, Serialize, Deserialize)]
108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
109#[serde(rename_all = "camelCase")]
110pub enum JmapDisplayed {
111 /// Display status is unknown.
112 Unknown,
113 /// The message has been displayed.
114 Yes,
115 /// The message has not been displayed.
116 No,
117}
118
119/// Per-recipient delivery status from a submission.
120#[derive(Clone, Debug, Serialize, Deserialize)]
121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
122#[serde(rename_all = "camelCase")]
123pub struct JmapDeliveryStatus {
124 /// The SMTP reply for this recipient.
125 pub smtp_reply: String,
126 /// Delivery state for this recipient.
127 pub delivered: JmapDelivered,
128 /// Whether the message has been displayed to the recipient.
129 pub displayed: JmapDisplayed,
130}
131
132/// Per-object error returned in `EmailSubmission/set` responses
133/// (RFC 8621 §7.5); shared by the create (`set`) and cancel flows.
134#[derive(Clone, Debug, Deserialize)]
135#[serde(tag = "type", rename_all = "camelCase")]
136pub enum JmapEmailSubmissionSetItemError {
137 /// The message had too many recipients (RFC 8621 §7.5).
138 TooManyRecipients {
139 /// Optional human-readable detail.
140 description: Option<String>,
141 },
142 /// The message had no recipients (RFC 8621 §7.5).
143 NoRecipients {
144 /// Optional human-readable detail.
145 description: Option<String>,
146 },
147 /// One or more recipient addresses were invalid (RFC 8621 §7.5).
148 InvalidRecipients {
149 /// Optional human-readable detail.
150 description: Option<String>,
151 },
152 /// The From address is not permitted for this identity (RFC 8621 §7.5).
153 ForbiddenFrom {
154 /// Optional human-readable detail.
155 description: Option<String>,
156 },
157 /// The MAIL FROM address is not permitted (RFC 8621 §7.5).
158 ForbiddenMailFrom {
159 /// Optional human-readable detail.
160 description: Option<String>,
161 },
162 /// This user is not permitted to send email (RFC 8621 §7.5).
163 ForbiddenToSend {
164 /// Optional human-readable detail.
165 description: Option<String>,
166 },
167 /// The submission cannot be unsent (RFC 8621 §7.5).
168 CannotUnsendMessage {
169 /// Optional human-readable detail.
170 description: Option<String>,
171 },
172 /// The email object was not a valid message (RFC 8621 §7.5).
173 InvalidEmail {
174 /// Optional human-readable detail.
175 description: Option<String>,
176 },
177 /// Standard set error (RFC 8620 §5.3): target id not found.
178 NotFound {
179 /// Optional human-readable detail.
180 description: Option<String>,
181 },
182 /// Standard set error (RFC 8620 §5.3): one or more properties were invalid.
183 InvalidProperties {
184 /// Optional human-readable detail.
185 description: Option<String>,
186 /// The invalid property names.
187 #[serde(default)]
188 properties: Vec<String>,
189 },
190 /// Catch-all for set errors not modelled above.
191 #[serde(other)]
192 Unknown,
193}