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:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use ahash::AHashSet;
use trc::AddContext;
use types::collection::Collection;
use crate::{
Deserialize, IterateParams, Store, U32_LEN, ValueKey,
write::{BatchBuilder, ValueClass, key::DeserializeBigEndian},
};
pub enum AclQuery {
SharedWith {
grant_account_id: u32,
to_account_id: u32,
to_collection: u8,
},
HasAccess {
grant_account_id: u32,
},
}
#[derive(Debug)]
pub struct AclItem {
pub to_account_id: u32,
pub to_collection: Collection,
pub to_document_id: u32,
pub permissions: u64,
}
impl Store {
pub async fn acl_query(&self, query: AclQuery) -> trc::Result<Vec<AclItem>> {
let mut results = Vec::new();
let (from_key, to_key) = match query {
AclQuery::SharedWith {
grant_account_id,
to_account_id,
to_collection,
} => {
let from_key = ValueKey {
account_id: to_account_id,
collection: to_collection,
document_id: 0,
class: ValueClass::Acl(grant_account_id),
};
let mut to_key = from_key.clone();
to_key.document_id = u32::MAX;
(from_key, to_key)
}
AclQuery::HasAccess { grant_account_id } => (
ValueKey {
account_id: 0,
collection: 0,
document_id: 0,
class: ValueClass::Acl(grant_account_id),
},
ValueKey {
account_id: u32::MAX,
collection: u8::MAX,
document_id: u32::MAX,
class: ValueClass::Acl(grant_account_id),
},
),
};
self.iterate(
IterateParams::new(from_key, to_key).ascending(),
|key, value| {
results.push(AclItem::deserialize(key)?.with_permissions(u64::deserialize(value)?));
Ok(true)
},
)
.await
.caused_by(trc::location!())
.map(|_| results)
}
pub async fn acl_revoke_all(&self, account_id: u32) -> trc::Result<AHashSet<u32>> {
let from_key = ValueKey {
account_id: 0,
collection: 0,
document_id: 0,
class: ValueClass::Acl(0),
};
let to_key = ValueKey {
account_id: u32::MAX,
collection: u8::MAX,
document_id: u32::MAX,
class: ValueClass::Acl(u32::MAX),
};
let mut delete_keys = Vec::new();
let mut revoked_accounts = AHashSet::new();
self.iterate(
IterateParams::new(from_key, to_key).ascending().no_values(),
|key, _| {
if account_id == key.deserialize_be_u32(U32_LEN)? {
let owner_account_id = key.deserialize_be_u32(0)?;
revoked_accounts.insert(owner_account_id);
delete_keys.push((owner_account_id, AclItem::deserialize(key)?));
}
Ok(true)
},
)
.await
.caused_by(trc::location!())?;
// Remove permissions
let mut batch = BatchBuilder::new();
batch.with_account_id(account_id);
let mut last_collection = Collection::None;
for (revoke_account_id, acl_item) in delete_keys.into_iter() {
if batch.is_large_batch() {
self.write(batch.build_all())
.await
.caused_by(trc::location!())?;
batch = BatchBuilder::new();
batch.with_account_id(account_id);
last_collection = Collection::None;
}
if acl_item.to_collection != last_collection {
batch.with_collection(acl_item.to_collection);
last_collection = acl_item.to_collection;
}
batch
.with_document(acl_item.to_document_id)
.acl_revoke(revoke_account_id);
}
if !batch.is_empty() {
self.write(batch.build_all())
.await
.caused_by(trc::location!())?;
}
Ok(revoked_accounts)
}
}
impl Deserialize for AclItem {
fn deserialize(bytes: &[u8]) -> trc::Result<Self> {
Ok(AclItem {
to_account_id: bytes.deserialize_be_u32(U32_LEN)?,
to_collection: bytes
.get(U32_LEN * 2)
.map(|b| Collection::from(*b))
.ok_or_else(|| trc::StoreEvent::DataCorruption.caused_by(trc::location!()))?,
to_document_id: bytes.deserialize_be_u32((U32_LEN * 2) + 1)?,
permissions: 0,
})
}
}
impl AclItem {
fn with_permissions(mut self, permissions: u64) -> Self {
self.permissions = permissions;
self
}
}
+487
View File
@@ -0,0 +1,487 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use trc::AddContext;
use types::collection::{SyncCollection, VanishedCollection};
use utils::codec::leb128::Leb128Iterator;
use crate::{
IterateParams, LogKey, Store, U32_LEN, U64_LEN,
write::{LogCollection, key::DeserializeBigEndian},
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Change {
InsertContainer(u64),
UpdateContainer(u64),
UpdateContainerProperty(u64),
DeleteContainer(u64),
InsertItem(u64),
UpdateItem(u64),
DeleteItem(u64),
}
#[derive(Debug)]
pub struct Changes {
pub changes: Vec<Change>,
pub from_change_id: u64,
pub to_change_id: u64,
pub container_change_id: Option<u64>,
pub item_change_id: Option<u64>,
pub is_truncated: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum Query {
All,
Since(u64),
SinceInclusive(u64),
RangeInclusive(u64, u64),
}
pub trait DeserializeVanished: Sized + Sync + Send {
fn deserialize_vanished<'x>(bytes: &mut impl Iterator<Item = &'x u8>) -> Option<Self>;
}
impl Default for Changes {
fn default() -> Self {
Self {
changes: Vec::with_capacity(10),
from_change_id: 0,
to_change_id: 0,
container_change_id: None,
item_change_id: None,
is_truncated: false,
}
}
}
impl Store {
pub async fn changes(
&self,
account_id: u32,
collection_: LogCollection,
query: Query,
) -> trc::Result<Changes> {
let is_share_log = matches!(
collection_,
LogCollection::Sync(SyncCollection::ShareNotification)
);
let collection = u8::from(collection_);
let (is_inclusive, from_change_id, to_change_id) = match query {
Query::All => (true, 0, u64::MAX),
Query::Since(change_id) => (false, change_id, u64::MAX),
Query::SinceInclusive(change_id) => (true, change_id, u64::MAX),
Query::RangeInclusive(from_change_id, to_change_id) => {
(true, from_change_id, to_change_id)
}
};
let from_key = LogKey {
account_id,
collection,
change_id: from_change_id,
};
let to_key = LogKey {
account_id,
collection,
change_id: to_change_id,
};
let mut changelog = Changes::default();
self.iterate(
IterateParams::new(from_key, to_key).ascending(),
|key, value| {
let change_id = key.deserialize_be_u64(key.len() - U64_LEN)?;
if is_inclusive || change_id != from_change_id {
if value.is_empty() {
changelog.is_truncated = true;
return Ok(true);
}
if changelog.changes.is_empty() {
changelog.from_change_id = change_id;
}
changelog.to_change_id = change_id;
if !is_share_log {
let (has_container_changes, has_item_changes) =
changelog.deserialize(value).ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
if has_container_changes {
changelog.container_change_id = Some(change_id);
}
if has_item_changes {
changelog.item_change_id = Some(change_id);
}
} else {
changelog.changes.push(Change::InsertItem(change_id));
}
} else {
changelog.from_change_id = change_id;
changelog.to_change_id = change_id;
}
Ok(true)
},
)
.await
.caused_by(trc::location!())?;
Ok(changelog)
}
pub async fn vanished<T: DeserializeVanished>(
&self,
account_id: u32,
collection: LogCollection,
query: Query,
) -> trc::Result<Vec<T>> {
let collection = u8::from(collection);
let (is_inclusive, from_change_id, to_change_id) = match query {
Query::All => (true, 0, u64::MAX),
Query::Since(change_id) => (false, change_id, u64::MAX),
Query::SinceInclusive(change_id) => (true, change_id, u64::MAX),
Query::RangeInclusive(from_change_id, to_change_id) => {
(true, from_change_id, to_change_id)
}
};
let from_key = LogKey {
account_id,
collection,
change_id: from_change_id,
};
let to_key = LogKey {
account_id,
collection,
change_id: to_change_id,
};
let mut vanished = Vec::default();
self.iterate(
IterateParams::new(from_key, to_key).ascending(),
|key, value| {
let change_id = key.deserialize_be_u64(key.len() - U64_LEN)?;
if is_inclusive || change_id != from_change_id {
let mut iter = value.iter().peekable();
while iter.peek().is_some() {
if let Some(item) = T::deserialize_vanished(&mut iter) {
vanished.push(item);
} else {
return Err(trc::Error::corrupted_key(
key,
value.into(),
trc::location!(),
));
}
}
}
Ok(true)
},
)
.await
.caused_by(trc::location!())?;
Ok(vanished)
}
pub async fn get_last_change_id(
&self,
account_id: u32,
collection: LogCollection,
) -> trc::Result<Option<u64>> {
let collection = u8::from(collection);
let from_key = LogKey {
account_id,
collection,
change_id: 0,
};
let to_key = LogKey {
account_id,
collection,
change_id: u64::MAX,
};
let mut last_change_id = None;
self.iterate(
IterateParams::new(from_key, to_key)
.descending()
.no_values()
.only_first(),
|key, _| {
last_change_id = key.deserialize_be_u64(key.len() - U64_LEN)?.into();
Ok(false)
},
)
.await
.caused_by(trc::location!())?;
Ok(last_change_id)
}
}
impl From<VanishedCollection> for LogCollection {
fn from(value: VanishedCollection) -> Self {
LogCollection::Vanished(value)
}
}
impl From<SyncCollection> for LogCollection {
fn from(value: SyncCollection) -> Self {
LogCollection::Sync(value)
}
}
impl Changes {
pub fn deserialize(&mut self, bytes: &[u8]) -> Option<(bool, bool)> {
let mut bytes_it = bytes.iter();
let container_inserts: usize = bytes_it.next_leb128()?;
let container_updates: usize = bytes_it.next_leb128()?;
let container_property_changes: usize = bytes_it.next_leb128()?;
let container_deletes: usize = bytes_it.next_leb128()?;
let item_inserts: usize = bytes_it.next_leb128()?;
let item_updates: usize = bytes_it.next_leb128()?;
let item_deletes: usize = bytes_it.next_leb128()?;
let has_container_changes =
container_inserts + container_updates + container_property_changes + container_deletes
> 0;
let has_item_changes = item_inserts + item_updates + item_deletes > 0;
if container_inserts > 0 {
for _ in 0..container_inserts {
self.changes
.push(Change::InsertContainer(bytes_it.next_leb128()?));
}
}
if container_updates > 0 || container_property_changes > 0 {
'update_outer: for change_pos in 0..(container_updates + container_property_changes) {
let id = bytes_it.next_leb128()?;
let mut is_property_change = change_pos >= container_updates;
for (idx, change) in self.changes.iter().enumerate() {
match change {
Change::InsertContainer(insert_id) if *insert_id == id => {
// Item updated after inserted, no need to count this change.
continue 'update_outer;
}
Change::UpdateContainer(update_id) if *update_id == id => {
// Move update to the front
is_property_change = false;
self.changes.remove(idx);
break;
}
Change::UpdateContainerProperty(update_id) if *update_id == id => {
// Move update to the front
self.changes.remove(idx);
break;
}
_ => (),
}
}
self.changes.push(if !is_property_change {
Change::UpdateContainer(id)
} else {
Change::UpdateContainerProperty(id)
});
}
}
if container_deletes > 0 {
'delete_outer: for _ in 0..container_deletes {
let id = bytes_it.next_leb128()?;
'delete_inner: for (idx, change) in self.changes.iter().enumerate() {
match change {
Change::InsertContainer(insert_id) if *insert_id == id => {
self.changes.remove(idx);
continue 'delete_outer;
}
Change::UpdateContainer(update_id) if *update_id == id => {
self.changes.remove(idx);
break 'delete_inner;
}
_ => (),
}
}
self.changes.push(Change::DeleteContainer(id));
}
}
// Item changes
if item_inserts > 0 {
for _ in 0..item_inserts {
self.changes
.push(Change::InsertItem(bytes_it.next_leb128()?));
}
}
if item_updates > 0 {
'update_outer: for _ in 0..item_updates {
let id = bytes_it.next_leb128()?;
for (idx, change) in self.changes.iter().enumerate() {
match change {
Change::InsertItem(insert_id) if *insert_id == id => {
// Item updated after inserted, no need to count this change.
continue 'update_outer;
}
Change::UpdateItem(update_id) if *update_id == id => {
// Move update to the front
self.changes.remove(idx);
break;
}
_ => (),
}
}
self.changes.push(Change::UpdateItem(id));
}
}
if item_deletes > 0 {
'delete_outer: for _ in 0..item_deletes {
let id = bytes_it.next_leb128()?;
'delete_inner: for (idx, change) in self.changes.iter().enumerate() {
match change {
Change::InsertItem(insert_id) if *insert_id == id => {
self.changes.remove(idx);
continue 'delete_outer;
}
Change::UpdateItem(update_id) if *update_id == id => {
self.changes.remove(idx);
break 'delete_inner;
}
_ => (),
}
}
self.changes.push(Change::DeleteItem(id));
}
}
Some((has_container_changes, has_item_changes))
}
}
impl Changes {
pub fn total_container_changes(&self) -> usize {
self.changes
.iter()
.filter(|change| change.is_container_change())
.count()
}
pub fn total_item_changes(&self) -> usize {
self.changes
.iter()
.filter(|change| change.is_item_change())
.count()
}
}
impl Change {
pub fn item_id(&self) -> Option<u64> {
match self {
Change::InsertItem(id) => Some(*id),
Change::UpdateItem(id) => Some(*id),
Change::DeleteItem(id) => Some(*id),
_ => None,
}
}
pub fn container_id(&self) -> Option<u64> {
match self {
Change::InsertContainer(id) => Some(*id),
Change::UpdateContainer(id) => Some(*id),
Change::UpdateContainerProperty(id) => Some(*id),
Change::DeleteContainer(id) => Some(*id),
_ => None,
}
}
pub fn try_unwrap_item_id(self) -> Option<u64> {
match self {
Change::InsertItem(id) => Some(id),
Change::UpdateItem(id) => Some(id),
Change::DeleteItem(id) => Some(id),
_ => None,
}
}
pub fn try_unwrap_container_id(self) -> Option<u64> {
match self {
Change::InsertContainer(id) => Some(id),
Change::UpdateContainer(id) => Some(id),
Change::UpdateContainerProperty(id) => Some(id),
Change::DeleteContainer(id) => Some(id),
_ => None,
}
}
pub fn is_container_change(&self) -> bool {
matches!(
self,
Change::InsertContainer(_)
| Change::UpdateContainer(_)
| Change::UpdateContainerProperty(_)
| Change::DeleteContainer(_)
)
}
pub fn is_item_change(&self) -> bool {
matches!(
self,
Change::InsertItem(_) | Change::UpdateItem(_) | Change::DeleteItem(_)
)
}
}
impl DeserializeVanished for u64 {
fn deserialize_vanished<'x>(bytes: &mut impl Iterator<Item = &'x u8>) -> Option<Self> {
let mut num = [0u8; U64_LEN];
for i in num.iter_mut() {
*i = *bytes.next()?;
}
Some(u64::from_be_bytes(num))
}
}
impl DeserializeVanished for (u32, u32) {
fn deserialize_vanished<'x>(bytes: &mut impl Iterator<Item = &'x u8>) -> Option<Self> {
let mut num1 = [0u8; U32_LEN];
let mut num2 = [0u8; U32_LEN];
for i in num1.iter_mut().chain(num2.iter_mut()) {
*i = *bytes.next()?;
}
Some((u32::from_be_bytes(num1), u32::from_be_bytes(num2)))
}
}
impl DeserializeVanished for String {
fn deserialize_vanished<'x>(bytes: &mut impl Iterator<Item = &'x u8>) -> Option<Self> {
let mut name = Vec::with_capacity(16);
loop {
let byte = bytes.next()?;
if *byte != 0 {
name.push(*byte);
} else {
break;
}
}
String::from_utf8(name).ok()
}
}
+52
View File
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod acl;
pub mod log;
use crate::{IterateParams, Key};
impl<T: Key> IterateParams<T> {
pub fn new(begin: T, end: T) -> Self {
IterateParams {
begin,
end,
first: false,
ascending: true,
values: true,
}
}
pub fn set_ascending(mut self, ascending: bool) -> Self {
self.ascending = ascending;
self
}
pub fn set_values(mut self, values: bool) -> Self {
self.values = values;
self
}
pub fn ascending(mut self) -> Self {
self.ascending = true;
self
}
pub fn descending(mut self) -> Self {
self.ascending = false;
self
}
pub fn only_first(mut self) -> Self {
self.first = true;
self
}
pub fn no_values(mut self) -> Self {
self.values = false;
self
}
}