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