/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use common::{Server, auth::AccessToken, sharing::EffectiveAcl}; use email::cache::{MessageCacheFetch, email::MessageCacheAccess, mailbox::MailboxCacheAccess}; use jmap_proto::{ method::get::{GetRequest, GetResponse}, object::mailbox::{Mailbox, MailboxProperty, MailboxValue}, }; use jmap_tools::{Map, Value}; use std::future::Future; use store::ahash::AHashSet; use types::{acl::Acl, collection::Collection, keyword::Keyword, special_use::SpecialUse}; use crate::{api::acl::JmapRights, changes::state::JmapCacheState}; pub trait MailboxGet: Sync + Send { fn mailbox_get( &self, request: GetRequest, access_token: &AccessToken, ) -> impl Future>> + Send; } impl MailboxGet for Server { async fn mailbox_get( &self, mut request: GetRequest, access_token: &AccessToken, ) -> trc::Result> { let (ids, not_found_ids) = request.unwrap_ids(self.core.jmap.get_max_objects)?; let properties = request.unwrap_properties(&[ MailboxProperty::Id, MailboxProperty::Name, MailboxProperty::ParentId, MailboxProperty::Role, MailboxProperty::SortOrder, MailboxProperty::IsSubscribed, MailboxProperty::TotalEmails, MailboxProperty::UnreadEmails, MailboxProperty::TotalThreads, MailboxProperty::UnreadThreads, MailboxProperty::MyRights, ]); let account_id = request.account_id.document_id(); let personal_id = access_token.personal_id(account_id, Collection::Mailbox); let cache = self.get_cached_messages(account_id).await?; let shared_ids = if access_token.is_shared(account_id) { cache.shared_mailboxes(access_token, Acl::Read).into() } else { None }; let ids = if let Some(ids) = ids { ids } else { cache .mailboxes .index .keys() .filter(|id| shared_ids.as_ref().is_none_or(|ids| ids.contains(**id))) .copied() .take(self.core.jmap.get_max_objects) .map(Into::into) .collect::>() }; let mut response = GetResponse { account_id: request.account_id.into(), state: Some(cache.get_state(true)), list: Vec::with_capacity(ids.len()), not_found: not_found_ids, }; for id in ids { // Obtain the mailbox object let document_id = id.document_id(); let cached_mailbox = if let Some(mailbox) = cache.mailbox_by_id(&document_id).filter(|_| { shared_ids .as_ref() .is_none_or(|ids| ids.contains(document_id)) }) { mailbox } else { response.push_not_found(id); continue; }; let mut mailbox = Map::with_capacity(properties.len()); for property in &properties { let value = match property { MailboxProperty::Id => Value::Element(MailboxValue::Id(id)), MailboxProperty::Name => Value::Str(cached_mailbox.name.to_string().into()), MailboxProperty::Role => match cached_mailbox.role { SpecialUse::None => Value::Null, role => Value::Element(MailboxValue::Role(role)), }, MailboxProperty::SortOrder => { Value::Number(cached_mailbox.sort_order().unwrap_or_default().into()) } MailboxProperty::ParentId => { if let Some(parent_id) = cached_mailbox.parent_id() { Value::Element(MailboxValue::Id(parent_id.into())) } else { Value::Null } } MailboxProperty::TotalEmails => { Value::Number(cache.in_mailbox(document_id).count().into()) } MailboxProperty::UnreadEmails => Value::Number( cache .in_mailbox_without_keyword(document_id, &Keyword::Seen) .count() .into(), ), MailboxProperty::TotalThreads => Value::Number( cache .in_mailbox(document_id) .map(|m| m.thread_id) .collect::>() .len() .into(), ), MailboxProperty::UnreadThreads => Value::Number( cache .in_mailbox_without_keyword(document_id, &Keyword::Seen) .map(|m| m.thread_id) .collect::>() .len() .into(), ), MailboxProperty::MyRights => { if access_token.is_shared(account_id) { JmapRights::rights::( cached_mailbox.acls.as_slice().effective_acl(access_token), ) } else { JmapRights::all_rights::() } } MailboxProperty::IsSubscribed => { Value::Bool(cached_mailbox.subscribers.contains(&personal_id)) } MailboxProperty::ShareWith => JmapRights::share_with::( account_id, access_token, &cached_mailbox.acls, ), _ => Value::Null, }; mailbox.insert_unchecked(property.clone(), value); } // Add result to response response.list.push(mailbox.into()); } Ok(response) } }