Skip to main content

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}