Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
Vendored
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::mailbox::{ArchivedMailbox, Mailbox, manage::MailboxFnc};
|
||||
use common::{
|
||||
MailboxCache, MailboxesCache, MessageStoreCache, Server, auth::AccessToken,
|
||||
sharing::EffectiveAcl,
|
||||
};
|
||||
use store::{
|
||||
ValueKey,
|
||||
write::{AlignedBytes, Archive},
|
||||
};
|
||||
use store::{ahash::AHashMap, roaring::RoaringBitmap};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::{Acl, AclGrant},
|
||||
collection::Collection,
|
||||
special_use::SpecialUse,
|
||||
};
|
||||
use utils::{map::bitmap::Bitmap, topological::TopologicalSort};
|
||||
|
||||
struct MailboxesCacheBuilder {
|
||||
pub change_id: u64,
|
||||
pub index: AHashMap<u32, u32>,
|
||||
pub items: Vec<MailboxCache>,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
pub(crate) async fn update_mailbox_cache(
|
||||
server: &Server,
|
||||
account_id: u32,
|
||||
changed_ids: &AHashMap<u32, bool>,
|
||||
store_cache: &MessageStoreCache,
|
||||
) -> trc::Result<MailboxesCache> {
|
||||
let mut new_cache = MailboxesCacheBuilder {
|
||||
items: Vec::with_capacity(store_cache.mailboxes.items.len()),
|
||||
index: AHashMap::with_capacity(store_cache.mailboxes.items.len()),
|
||||
size: 0,
|
||||
change_id: 0,
|
||||
};
|
||||
|
||||
for (document_id, is_update) in changed_ids {
|
||||
if *is_update
|
||||
&& let Some(archive) = server
|
||||
.store()
|
||||
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
|
||||
account_id,
|
||||
Collection::Mailbox,
|
||||
*document_id,
|
||||
))
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
{
|
||||
insert_item(
|
||||
&mut new_cache,
|
||||
*document_id,
|
||||
archive.unarchive::<Mailbox>()?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for item in store_cache.mailboxes.items.iter() {
|
||||
if !changed_ids.contains_key(&item.document_id) {
|
||||
mailbox_insert(&mut new_cache, item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
build_tree(&mut new_cache);
|
||||
|
||||
Ok(new_cache.build())
|
||||
}
|
||||
|
||||
pub(crate) async fn full_mailbox_cache_build(
|
||||
server: &Server,
|
||||
account_id: u32,
|
||||
) -> trc::Result<MailboxesCache> {
|
||||
// Build cache
|
||||
let mut cache = MailboxesCacheBuilder {
|
||||
items: Default::default(),
|
||||
index: Default::default(),
|
||||
size: 0,
|
||||
change_id: 0,
|
||||
};
|
||||
|
||||
server
|
||||
.archives(
|
||||
account_id,
|
||||
Collection::Mailbox,
|
||||
&(),
|
||||
|document_id, archive| {
|
||||
insert_item(&mut cache, document_id, archive.unarchive::<Mailbox>()?);
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
if cache.items.is_empty() {
|
||||
server
|
||||
.create_system_folders(account_id)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
server
|
||||
.archives(
|
||||
account_id,
|
||||
Collection::Mailbox,
|
||||
&(),
|
||||
|document_id, archive| {
|
||||
insert_item(&mut cache, document_id, archive.unarchive::<Mailbox>()?);
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
}
|
||||
|
||||
build_tree(&mut cache);
|
||||
|
||||
Ok(cache.build())
|
||||
}
|
||||
|
||||
fn insert_item(cache: &mut MailboxesCacheBuilder, document_id: u32, mailbox: &ArchivedMailbox) {
|
||||
let parent_id = mailbox.parent_id.to_native();
|
||||
let item = MailboxCache {
|
||||
document_id,
|
||||
name: mailbox.name.as_str().into(),
|
||||
path: "".into(),
|
||||
role: (&mailbox.role).into(),
|
||||
parent_id: if parent_id > 0 {
|
||||
parent_id - 1
|
||||
} else {
|
||||
u32::MAX
|
||||
},
|
||||
sort_order: mailbox
|
||||
.sort_order
|
||||
.as_ref()
|
||||
.map(|s| s.to_native())
|
||||
.unwrap_or(u32::MAX),
|
||||
subscribers: mailbox.subscribers.iter().map(|s| s.to_native()).collect(),
|
||||
uid_validity: mailbox.uid_validity.to_native(),
|
||||
acls: mailbox
|
||||
.acls
|
||||
.iter()
|
||||
.map(|acl| AclGrant {
|
||||
account_id: acl.account_id.to_native(),
|
||||
grants: Bitmap::from(&acl.grants),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
mailbox_insert(cache, item);
|
||||
}
|
||||
|
||||
fn build_tree(cache: &mut MailboxesCacheBuilder) {
|
||||
cache.size = 0;
|
||||
let mut topological_sort = TopologicalSort::with_capacity(cache.items.len());
|
||||
|
||||
for (idx, mailbox) in cache.items.iter_mut().enumerate() {
|
||||
topological_sort.insert(
|
||||
if mailbox.parent_id == u32::MAX {
|
||||
0
|
||||
} else {
|
||||
mailbox.parent_id + 1
|
||||
},
|
||||
mailbox.document_id + 1,
|
||||
);
|
||||
mailbox.path = if matches!(mailbox.role, SpecialUse::Inbox) {
|
||||
"INBOX".into()
|
||||
} else if mailbox.is_root() && mailbox.name.as_str().eq_ignore_ascii_case("inbox") {
|
||||
format!("INBOX {}", idx + 1)
|
||||
} else {
|
||||
mailbox.name.clone()
|
||||
};
|
||||
|
||||
cache.size += item_size(mailbox);
|
||||
}
|
||||
|
||||
for folder_id in topological_sort.into_iterator() {
|
||||
if folder_id != 0 {
|
||||
let folder_id = folder_id - 1;
|
||||
if let Some((path, parent_path)) = by_id(cache, &folder_id)
|
||||
.and_then(|folder| {
|
||||
folder
|
||||
.parent_id()
|
||||
.map(|parent_id| (&folder.path, parent_id))
|
||||
})
|
||||
.and_then(|(path, parent_id)| {
|
||||
by_id(cache, &parent_id).map(|folder| (path, &folder.path))
|
||||
})
|
||||
{
|
||||
let mut new_path = String::with_capacity(parent_path.len() + path.len() + 1);
|
||||
new_path.push_str(parent_path.as_str());
|
||||
new_path.push('/');
|
||||
new_path.push_str(path.as_str());
|
||||
let folder = by_id_mut(cache, &folder_id).unwrap();
|
||||
folder.path = new_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxesCacheBuilder {
|
||||
fn build(mut self) -> MailboxesCache {
|
||||
self.index.shrink_to_fit();
|
||||
MailboxesCache {
|
||||
change_id: self.change_id,
|
||||
index: self.index,
|
||||
items: self.items.into_boxed_slice(),
|
||||
size: self.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MailboxCacheAccess {
|
||||
fn mailbox_by_id(&self, id: &u32) -> Option<&MailboxCache>;
|
||||
fn mailbox_by_name(&self, name: &str) -> Option<&MailboxCache>;
|
||||
fn mailbox_by_path(&self, name: &str) -> Option<&MailboxCache>;
|
||||
fn mailbox_by_role(&self, role: &SpecialUse) -> Option<&MailboxCache>;
|
||||
fn shared_mailboxes(
|
||||
&self,
|
||||
access_token: &AccessToken,
|
||||
check_acls: impl Into<Bitmap<Acl>> + Sync + Send,
|
||||
) -> RoaringBitmap;
|
||||
fn has_mailbox_id(&self, id: &u32) -> bool;
|
||||
}
|
||||
|
||||
impl MailboxCacheAccess for MessageStoreCache {
|
||||
fn mailbox_by_name(&self, name: &str) -> Option<&MailboxCache> {
|
||||
self.mailboxes
|
||||
.items
|
||||
.iter()
|
||||
.find(|m| m.name.eq_ignore_ascii_case(name))
|
||||
}
|
||||
|
||||
fn mailbox_by_path(&self, path: &str) -> Option<&MailboxCache> {
|
||||
self.mailboxes
|
||||
.items
|
||||
.iter()
|
||||
.find(|m| m.path.eq_ignore_ascii_case(path))
|
||||
}
|
||||
|
||||
fn mailbox_by_role(&self, role: &SpecialUse) -> Option<&MailboxCache> {
|
||||
self.mailboxes.items.iter().find(|m| &m.role == role)
|
||||
}
|
||||
|
||||
fn shared_mailboxes(
|
||||
&self,
|
||||
access_token: &AccessToken,
|
||||
check_acls: impl Into<Bitmap<Acl>> + Sync + Send,
|
||||
) -> RoaringBitmap {
|
||||
let check_acls = check_acls.into();
|
||||
|
||||
RoaringBitmap::from_iter(
|
||||
self.mailboxes
|
||||
.items
|
||||
.iter()
|
||||
.filter(|m| {
|
||||
m.acls
|
||||
.as_slice()
|
||||
.effective_acl(access_token)
|
||||
.contains_all(check_acls)
|
||||
})
|
||||
.map(|m| m.document_id),
|
||||
)
|
||||
}
|
||||
|
||||
fn mailbox_by_id(&self, id: &u32) -> Option<&MailboxCache> {
|
||||
self.mailboxes
|
||||
.index
|
||||
.get(id)
|
||||
.and_then(|idx| self.mailboxes.items.get(*idx as usize))
|
||||
}
|
||||
|
||||
fn has_mailbox_id(&self, id: &u32) -> bool {
|
||||
self.mailboxes.index.contains_key(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn by_id<'x>(cache: &'x MailboxesCacheBuilder, id: &u32) -> Option<&'x MailboxCache> {
|
||||
cache
|
||||
.index
|
||||
.get(id)
|
||||
.and_then(|idx| cache.items.get(*idx as usize))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn by_id_mut<'x>(cache: &'x mut MailboxesCacheBuilder, id: &u32) -> Option<&'x mut MailboxCache> {
|
||||
cache
|
||||
.index
|
||||
.get(id)
|
||||
.and_then(|idx| cache.items.get_mut(*idx as usize))
|
||||
}
|
||||
|
||||
fn mailbox_insert(cache: &mut MailboxesCacheBuilder, item: MailboxCache) {
|
||||
let id = item.document_id;
|
||||
if let Some(idx) = cache.index.get(&id) {
|
||||
cache.items[*idx as usize] = item;
|
||||
} else {
|
||||
let idx = cache.items.len() as u32;
|
||||
cache.items.push(item);
|
||||
cache.index.insert(id, idx);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn item_size(item: &MailboxCache) -> u64 {
|
||||
(std::mem::size_of::<MailboxCache>()
|
||||
+ (if item.name.len() > std::mem::size_of::<String>() {
|
||||
item.name.len()
|
||||
} else {
|
||||
0
|
||||
})
|
||||
+ (if item.path.len() > std::mem::size_of::<String>() {
|
||||
item.path.len()
|
||||
} else {
|
||||
0
|
||||
})) as u64
|
||||
}
|
||||
Reference in New Issue
Block a user