mirror of
https://github.com/rustmailer/rustmailer.git
synced 2026-08-18 16:01:07 +00:00
184 lines
7.0 KiB
Rust
184 lines
7.0 KiB
Rust
// Copyright © 2025 rustmailer.com
|
|
// Licensed under RustMailer License Agreement v1.0
|
|
// Unauthorized copying, modification, or distribution is prohibited.
|
|
|
|
use crate::{
|
|
encode_mailbox_name,
|
|
modules::{
|
|
account::{entity::MailerType, migration::AccountModel},
|
|
cache::vendor::gmail::sync::client::GmailClient,
|
|
context::executors::RUST_MAIL_CONTEXT,
|
|
envelope::generate_uid_set,
|
|
error::{code::ErrorCode, RustMailerResult},
|
|
},
|
|
raise_error,
|
|
};
|
|
use poem_openapi::Object;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
|
pub struct MailboxTransferRequest {
|
|
/// A list of unique message identifiers as strings.
|
|
///
|
|
/// - For IMAP accounts, each UID is converted to a numeric string (parseable back to `u32`).
|
|
/// - For Gmail API accounts, each element is a message ID (`mid`) returned by the API.
|
|
/// Unifying them as strings simplifies handling across different backends.
|
|
pub ids: Vec<String>,
|
|
/// The name of the mailbox from which the messages will be moved.
|
|
/// For IMAP: the decoded, human-readable name of the mailbox (e.g., "INBOX").
|
|
/// For Gmail API: represents the label name.
|
|
pub current_mailbox: String,
|
|
/// The name of the mailbox to which the messages will be moved.
|
|
/// For IMAP: the decoded, human-readable name of the mailbox (e.g., "INBOX").
|
|
/// For Gmail API: represents the label name.
|
|
pub target_mailbox: String,
|
|
}
|
|
|
|
#[derive(Clone, Default, Debug)]
|
|
pub enum MessageTransfer {
|
|
#[default]
|
|
Move,
|
|
Copy,
|
|
}
|
|
|
|
pub async fn transfer_messages(
|
|
account_id: u64,
|
|
payload: &MailboxTransferRequest,
|
|
transfer: MessageTransfer,
|
|
) -> RustMailerResult<()> {
|
|
// Ensure the account exists before proceeding
|
|
let account = AccountModel::check_account_active(account_id, false).await?;
|
|
|
|
match account.mailer_type {
|
|
MailerType::ImapSmtp => {
|
|
if payload.ids.is_empty() {
|
|
return Err(raise_error!(
|
|
"`ids` must contain at least one element".into(),
|
|
ErrorCode::InvalidParameter
|
|
));
|
|
}
|
|
|
|
let uids: Vec<u32> = payload
|
|
.ids
|
|
.iter()
|
|
.map(|id| {
|
|
id.parse::<u32>().map_err(|_| {
|
|
raise_error!(
|
|
format!("Invalid IMAP UID: '{}', must be a numeric string", id),
|
|
ErrorCode::InvalidParameter
|
|
)
|
|
})
|
|
})
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
let uid_set = generate_uid_set(uids);
|
|
let executor = RUST_MAIL_CONTEXT.imap(account_id).await?;
|
|
// Encode the mailbox names using UTF-7 encoding
|
|
let current_mailbox = encode_mailbox_name!(payload.current_mailbox.clone());
|
|
let target_mailbox = encode_mailbox_name!(payload.target_mailbox.clone());
|
|
|
|
match transfer {
|
|
MessageTransfer::Move => {
|
|
// Move the messages from the current mailbox to the target mailbox
|
|
executor
|
|
.uid_move_envelopes(
|
|
uid_set.as_str(),
|
|
current_mailbox.as_str(),
|
|
target_mailbox.as_str(),
|
|
)
|
|
.await
|
|
}
|
|
MessageTransfer::Copy => {
|
|
// Copy the messages from the current mailbox to the target mailbox
|
|
executor
|
|
.uid_copy_envelopes(
|
|
uid_set.as_str(),
|
|
current_mailbox.as_str(),
|
|
target_mailbox.as_str(),
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
}
|
|
MailerType::GmailApi => {
|
|
let mids = &payload.ids;
|
|
|
|
if mids.is_empty() {
|
|
return Err(raise_error!(
|
|
"Gmail API copy requires at least one message ID".into(),
|
|
ErrorCode::InvalidParameter
|
|
));
|
|
}
|
|
|
|
if mids.len() > 500 {
|
|
return Err(raise_error!(
|
|
format!(
|
|
"Gmail API batchModify supports at most 500 message IDs, got {}",
|
|
mids.len()
|
|
),
|
|
ErrorCode::InvalidParameter
|
|
));
|
|
}
|
|
|
|
let labels_map =
|
|
GmailClient::reverse_label_map(account_id, account.use_proxy, true).await?;
|
|
match transfer {
|
|
MessageTransfer::Move => {
|
|
let target_label_id =
|
|
labels_map.get(&payload.target_mailbox).ok_or_else(|| {
|
|
raise_error!(
|
|
format!(
|
|
"Target mailbox/label `{}` not found in Gmail labels",
|
|
payload.target_mailbox
|
|
),
|
|
ErrorCode::InvalidParameter
|
|
)
|
|
})?;
|
|
|
|
let current_label_id =
|
|
labels_map.get(&payload.current_mailbox).ok_or_else(|| {
|
|
raise_error!(
|
|
format!(
|
|
"Current mailbox/label `{}` not found in Gmail labels",
|
|
payload.current_mailbox
|
|
),
|
|
ErrorCode::InvalidParameter
|
|
)
|
|
})?;
|
|
//modify label
|
|
GmailClient::batch_modify(
|
|
account_id,
|
|
account.use_proxy,
|
|
mids,
|
|
vec![target_label_id.into()],
|
|
vec![current_label_id.into()],
|
|
)
|
|
.await
|
|
}
|
|
MessageTransfer::Copy => {
|
|
let target_label_id =
|
|
labels_map.get(&payload.target_mailbox).ok_or_else(|| {
|
|
raise_error!(
|
|
format!(
|
|
"Target mailbox/label `{}` not found in Gmail labels",
|
|
payload.target_mailbox
|
|
),
|
|
ErrorCode::InvalidParameter
|
|
)
|
|
})?;
|
|
//add label
|
|
GmailClient::batch_modify(
|
|
account_id,
|
|
account.use_proxy,
|
|
mids,
|
|
vec![target_label_id.into()],
|
|
vec![],
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
}
|
|
MailerType::GraphApi => todo!(),
|
|
}
|
|
}
|