You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
meli/melib/src/jmap/objects/submission.rs

388 lines
15 KiB
Rust

/*
* meli
*
* Copyright 2023 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use indexmap::IndexMap;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use super::*;
/// `UndoStatus`
///
/// This represents whether the submission may be canceled. This is
/// server set on create and MUST be one of the following values:
/// * `pending`: It may be possible to cancel this submission.
/// * `final`: The message has been relayed to at least one recipient
/// in a manner that cannot be recalled. It is no longer possible
/// to cancel this submission.
/// * `canceled`: The submission was canceled and will not be
/// delivered to any recipient.
/// On systems that do not support unsending, the value of this
/// property will always be `final`. On systems that do support
/// canceling submission, it will start as `pending` and MAY
/// transition to `final` when the server knows it definitely cannot
/// recall the message, but it MAY just remain `pending`. If in
/// pending state, a client can attempt to cancel the submission by
/// setting this property to `canceled`; if the update succeeds, the
/// submission was successfully canceled, and the message has not been
/// delivered to any of the original recipients.
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum UndoStatus {
/// It may be possible to cancel this submission.
Pending,
/// The message has been relayed to at least one recipient in a manner that
/// cannot be recalled. It is no longer possible to cancel this
/// submission.
#[default]
Final,
/// The submission was canceled and will not be delivered to any recipient.
Canceled,
}
/// This represents the delivery status for each of the submission's
/// recipients, if known. This property MAY not be supported by all
/// servers, in which case it will remain null. Servers that support
/// it SHOULD update the EmailSubmission object each time the status
/// of any of the recipients changes, even if some recipients are
/// still being retried.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeliveryStatusObject {
/// The SMTP reply string returned for this recipient when the
/// server last tried to relay the message, or in a later Delivery
/// Status Notification (DSN, as defined in `[RFC3464]`) response for
/// the message. This SHOULD be the response to the RCPT TO stage,
/// unless this was accepted and the message as a whole was
/// rejected at the end of the DATA stage, in which case the DATA
/// stage reply SHOULD be used instead.
///
/// Multi-line SMTP responses should be concatenated to a single
/// string as follows:
///
/// + The hyphen following the SMTP code on all but the last line
/// is replaced with a space.
///
/// + Any prefix in common with the first line is stripped from
/// lines after the first.
///
/// + CRLF is replaced by a space.
///
/// For example:
///
/// 550-5.7.1 Our system has detected that this message is
/// 550 5.7.1 likely spam.
///
/// would become:
///
/// 550 5.7.1 Our system has detected that this message is likely spam.
///
/// For messages relayed via an alternative to SMTP, the server MAY
/// generate a synthetic string representing the status instead.
/// If it does this, the string MUST be of the following form:
///
/// + A 3-digit SMTP reply code, as defined in `[RFC5321]`,
/// Section 4.2.3.
///
/// + Then a single space character.
///
/// + Then an SMTP Enhanced Mail System Status Code as defined in
/// `[RFC3463]`, with a registry defined in `[RFC5248]`.
///
/// + Then a single space character.
///
/// + Then an implementation-specific information string with a
/// human-readable explanation of the response.
pub smtp_reply: String,
/// Represents whether the message has been successfully delivered
/// to the recipient.
pub delivered: String,
/// Represents whether the message has been displayed to the recipient.
pub displayed: Displayed,
}
/// Represents whether the message has been displayed to the recipient.
/// If a Message Disposition Notification (MDN) is received for
/// this recipient with Disposition-Type (as per `[RFC8098]`,
/// Section 3.2.6.2) equal to `displayed`, this property SHOULD be
/// set to `yes`.
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Displayed {
/// The recipient's system claims the message content has been displayed to
/// the recipient. Note that there is no guarantee that the recipient
/// has noticed, read, or understood the content.
Yes,
/// The display status is unknown. This is the initial value.
#[default]
Unknown,
}
/// Represents whether the message has been successfully delivered
/// to the recipient.
///
/// Note that successful relaying to an external SMTP server SHOULD
/// NOT be taken as an indication that the message has successfully
/// reached the final mail store. In this case though, the server
/// may receive a DSN response, if requested.
///
/// If a DSN is received for the recipient with Action equal to
/// `delivered`, as per `[RFC3464]`, Section 2.3.3, then the
/// `delivered` property SHOULD be set to `yes`; if the Action
/// equals `failed`, the property SHOULD be set to `no`. Receipt
/// of any other DSN SHOULD NOT affect this property.
///
/// The server MAY also set this property based on other feedback
/// channels.
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Delivered {
/// The message is in a local mail queue and the status will change once it
/// exits the local mail queues. The `smtpReply` property may still
/// change.
#[default]
Queued,
/// The message was successfully delivered to the mail store of the
/// recipient. The `smtpReply` property is final.
Yes,
/// Delivery to the recipient permanently failed. The `smtpReply` property
/// is final.
No,
/// The final delivery status is unknown, (e.g., it was relayed to an
/// external machine and no further information is available). The
/// `smtpReply` property may still change if a DSN arrives.
Unknown,
}
/// # Email Submission
///
/// An *EmailSubmission* object represents the submission of an Email for
/// delivery to one or more recipients. It has the following properties:
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EmailSubmissionObject {
/// accountId: `Id`
/// The id of the account to use.
#[serde(skip_serializing)]
pub account_id: Id<Account>,
/// identityId: `Id` (immutable)
/// The id of the Identity to associate with this submission.
pub identity_id: Id<IdentityObject>,
/// The id of the Email to send. The Email being sent does not have
/// to be a draft, for example, when "redirecting" an existing Email
/// to a different address.
pub email_id: Argument<Id<EmailObject>>,
/// The Thread id of the Email to send. This is set by the server to
/// the `threadId` property of the Email referenced by the `emailId`.
#[serde(skip_serializing)]
pub thread_id: Id<ThreadObject>,
/// Information for use when sending via SMTP.
pub envelope: Option<EnvelopeObject>,
/// sendAt: `UTCDate` (immutable; server-set)
/// The date the submission was/will be released for delivery. If the
/// client successfully used `FUTURERELEASE` `[RFC4865]` with the
/// submission, this MUST be the time when the server will release the
/// message; otherwise, it MUST be the time the `EmailSubmission` was
/// created.
#[serde(skip_serializing)]
pub send_at: String,
/// This represents whether the submission may be canceled.
pub undo_status: UndoStatus,
/// deliveryStatus: `String[DeliveryStatus]|null` (server-set)
///
/// This represents the delivery status for each of the submission's
/// recipients, if known. This property MAY not be supported by all
/// servers, in which case it will remain null. Servers that support
/// it SHOULD update the `EmailSubmission` object each time the status
/// of any of the recipients changes, even if some recipients are
/// still being retried.
#[serde(skip_serializing)]
pub delivery_status: Option<IndexMap<String, DeliveryStatusObject>>,
/// dsnBlobIds: `Id[]` (server-set)
/// A list of blob ids for DSNs `[RFC3464]` received for this
/// submission, in order of receipt, oldest first. The blob is the
/// whole MIME message (with a top-level content-type of "multipart/
/// report"), as received.
#[serde(skip_serializing)]
pub dsn_blob_ids: Vec<Id<BlobObject>>,
/// mdnBlobIds: `Id[]` (server-set)
/// A list of blob ids for MDNs `[RFC8098]` received for this
/// submission, in order of receipt, oldest first. The blob is the
/// whole MIME message (with a top-level content-type of "multipart/
/// report"), as received.
#[serde(skip_serializing)]
pub mdn_blob_ids: Vec<Id<BlobObject>>,
}
impl Object for EmailSubmissionObject {
const NAME: &'static str = "EmailSubmission";
}
impl EmailSubmissionObject {
/// Create a new `EmailSubmissionObject`, with all the server-set fields
/// initialized as empty.
pub fn new(
account_id: Id<Account>,
identity_id: Id<IdentityObject>,
email_id: impl Into<Argument<Id<EmailObject>>>,
envelope: Option<EnvelopeObject>,
undo_status: Option<UndoStatus>,
) -> Self {
Self {
account_id,
identity_id,
email_id: email_id.into(),
thread_id: "".into(),
envelope,
send_at: String::new(),
undo_status: undo_status.unwrap_or_default(),
delivery_status: None,
dsn_blob_ids: vec![],
mdn_blob_ids: vec![],
}
}
}
impl Serialize for EmailSubmissionObject {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct(stringify! {EmailSubmissionObject}, 4)?;
state.serialize_field("identityId", &self.identity_id)?;
state.serialize_field(
if matches!(self.email_id, Argument::Value(_)) {
"emailId"
} else {
"#emailId"
},
&self.email_id,
)?;
state.serialize_field("envelope", &self.envelope)?;
state.serialize_field("undoStatus", &self.undo_status)?;
state.end()
}
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EmailSubmissionSet {
#[serde(flatten)]
pub set_call: Set<EmailSubmissionObject>,
/// onSuccessUpdateEmail: `Id[PatchObject]|null`
/// A map of [`EmailSubmissionObject`] id to an object containing properties
/// to update on the [`Email`](EmailObject) object referenced by the
/// [`EmailSubmissionObject`] if the create/update/destroy succeeds. (For
/// references to EmailSubmissions created in the same
/// `/set` invocation, this is equivalent to a creation-reference, so the id
/// will be the creation id prefixed with a `#`.)
#[serde(default)]
pub on_success_update_email: Option<IndexMap<Id<EmailSubmissionObject>, PatchObject>>,
/// onSuccessDestroyEmail: `Id[]|null`
/// A list of EmailSubmission ids for which the Email with the
/// corresponding `emailId` should be destroyed if the create/update/
/// destroy succeeds. (For references to EmailSubmission creations,
/// this is equivalent to a creation-reference, so the id will be the
/// creation id prefixed with a `#`.)
#[serde(default)]
pub on_success_destroy_email: Option<Vec<Id<EmailSubmissionObject>>>,
}
impl Method<EmailSubmissionObject> for EmailSubmissionSet {
const NAME: &'static str = "EmailSubmission/set";
}
impl EmailSubmissionSet {
pub fn new(set_call: Set<EmailSubmissionObject>) -> Self {
Self {
set_call,
on_success_update_email: None,
on_success_destroy_email: None,
}
}
pub fn on_success_update_email(
self,
on_success_update_email: Option<IndexMap<Id<EmailSubmissionObject>, PatchObject>>,
) -> Self {
Self {
on_success_update_email,
..self
}
}
pub fn on_success_destroy_email(
self,
on_success_destroy_email: Option<Vec<Id<EmailSubmissionObject>>>,
) -> Self {
Self {
on_success_destroy_email,
..self
}
}
}
/// Information for use when sending via SMTP.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EnvelopeObject {
/// The email address to use as the return address in the SMTP
/// submission, plus any parameters to pass with the MAIL FROM
/// address. The JMAP server MAY allow the address to be the empty
/// string.
/// When a JMAP server performs an SMTP message submission, it MAY
/// use the same id string for the ENVID parameter `[RFC3461]` and
/// the EmailSubmission object id. Servers that do this MAY
/// replace a client-provided value for ENVID with a server-
/// provided value.
pub mail_from: Address,
/// The email addresses to send the message to, and any RCPT TO
/// parameters to pass with the recipient.
pub rcpt_to: Vec<Address>,
}
impl Object for EnvelopeObject {
const NAME: &'static str = "Envelope";
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Address {
/// The email address being represented by the object. This is a
/// "Mailbox" as used in the Reverse-path or Forward-path of the
/// MAIL FROM or RCPT TO command in `[RFC5321]`.
pub email: String,
/// Any parameters to send with the email address (either mail-
/// parameter or rcpt-parameter as appropriate, as specified in
/// `[RFC5321]`). If supplied, each key in the object is a parameter
/// name, and the value is either the parameter value (type
/// `String`) or null if the parameter does not take a value. For
/// both name and value, any xtext or unitext encodings are removed
/// (see `[RFC3461]` and `[RFC6533]`) and JSON string encoding is
/// applied.
pub parameters: Option<Value>,
}