/* * SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use std::future::Future; use super::{cache::MessageMailboxCache, *}; use common::{Server, config::jmap::settings::SpecialUse, storage::index::ObjectIndexBuilder}; use jmap_proto::types::collection::Collection; use store::write::BatchBuilder; use trc::AddContext; pub trait MailboxFnc: Sync + Send { fn create_system_folders( &self, account_id: u32, ) -> impl Future> + Send; fn mailbox_create_path( &self, account_id: u32, path: &str, ) -> impl Future>> + Send; } impl MailboxFnc for Server { async fn create_system_folders(&self, account_id: u32) -> trc::Result<()> { #[cfg(feature = "test_mode")] if account_id == 0 { return Ok(()); } let mut batch = BatchBuilder::new(); batch .with_account_id(account_id) .with_collection(Collection::Mailbox); // Create mailboxes let mut last_document_id = ARCHIVE_ID; for folder in &self.core.jmap.default_folders { let document_id = match folder.special_use { SpecialUse::Inbox => INBOX_ID, SpecialUse::Trash => TRASH_ID, SpecialUse::Junk => JUNK_ID, SpecialUse::Drafts => DRAFTS_ID, SpecialUse::Sent => SENT_ID, SpecialUse::Archive => ARCHIVE_ID, SpecialUse::None | SpecialUse::Important => { last_document_id += 1; last_document_id } SpecialUse::Shared => unreachable!(), }; let mut object = Mailbox::new(folder.name.clone()).with_role(folder.special_use); if folder.subscribe { object.add_subscriber(account_id); } batch .create_document(document_id) .custom(ObjectIndexBuilder::<(), _>::new().with_changes(object)) .caused_by(trc::location!())?; } self.store() .assign_document_ids(account_id, Collection::Mailbox, (ARCHIVE_ID + 1) as u64) .await .caused_by(trc::location!())?; self.core .storage .data .write(batch.build_all()) .await .caused_by(trc::location!())?; Ok(()) } async fn mailbox_create_path(&self, account_id: u32, path: &str) -> trc::Result> { let folders = self .get_cached_mailboxes(account_id) .await .caused_by(trc::location!())?; let mut next_parent_id = 0; let mut create_paths = Vec::with_capacity(2); let mut path = path.split('/').map(|v| v.trim()); let mut found_path = String::with_capacity(16); { while let Some(name) = path.next() { if !found_path.is_empty() { found_path.push('/'); } for ch in name.chars() { for ch in ch.to_lowercase() { found_path.push(ch); } } if let Some((document_id, _)) = folders .items .iter() .find(|(_, item)| item.path.to_lowercase() == found_path) { next_parent_id = *document_id + 1; } else { create_paths.push(name.to_string()); create_paths.extend(path.map(|v| v.to_string())); break; } } } // Create missing folders if !create_paths.is_empty() { if create_paths .iter() .any(|name| name.len() > self.core.jmap.mailbox_name_max_len) { return Ok(None); } let mut next_document_id = self .store() .assign_document_ids(account_id, Collection::Mailbox, create_paths.len() as u64) .await .caused_by(trc::location!())?; let mut batch = BatchBuilder::new(); for name in create_paths { let document_id = next_document_id; next_document_id -= 1; batch .with_account_id(account_id) .with_collection(Collection::Mailbox) .create_document(document_id) .custom( ObjectIndexBuilder::<(), _>::new() .with_changes(Mailbox::new(name).with_parent_id(next_parent_id)), ) .caused_by(trc::location!())?; next_parent_id = document_id + 1; } self.commit_batch(batch).await.caused_by(trc::location!())?; } Ok(Some(next_parent_id - 1)) } }