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
@@ -0,0 +1,198 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::changes::state::JmapCacheState;
use calcard::{
icalendar::{ArchivedICalendarProperty, ICalendar},
jscalendar::import::ConversionOptions,
};
use common::{Server, auth::AccessToken};
use groupware::{
cache::GroupwareCache,
calendar::{
ArchivedChangedBy, CalendarEventNotification, EVENT_NOTIFICATION_IS_CHANGE,
EVENT_NOTIFICATION_IS_DRAFT,
},
};
use jmap_proto::{
method::get::GetRequest,
object::calendar_event_notification::{
self, CalendarEventNotificationGetResponse, CalendarEventNotificationObject,
CalendarEventNotificationProperty, CalendarEventNotificationType, PersonObject,
},
types::date::UTCDate,
};
use store::{
ValueKey,
write::{AlignedBytes, Archive, serialize::rkyv_deserialize},
};
use trc::AddContext;
use types::{
blob::BlobId,
collection::{Collection, SyncCollection},
id::Id,
};
pub trait CalendarEventNotificationGet: Sync + Send {
fn calendar_event_notification_get(
&self,
request: GetRequest<calendar_event_notification::CalendarEventNotification>,
access_token: &AccessToken,
) -> impl Future<Output = trc::Result<CalendarEventNotificationGetResponse>> + Send;
}
impl CalendarEventNotificationGet for Server {
async fn calendar_event_notification_get(
&self,
mut request: GetRequest<calendar_event_notification::CalendarEventNotification>,
access_token: &AccessToken,
) -> trc::Result<CalendarEventNotificationGetResponse> {
let (ids, not_found_ids) = request.unwrap_ids(self.core.jmap.get_max_objects)?;
let properties = request.unwrap_properties(&[
CalendarEventNotificationProperty::Id,
CalendarEventNotificationProperty::Created,
CalendarEventNotificationProperty::Type,
CalendarEventNotificationProperty::ChangedBy,
]);
let account_id = request.account_id.document_id();
let cache = self
.fetch_dav_resources(
access_token.account_id(),
account_id,
SyncCollection::CalendarEventNotification,
)
.await
.caused_by(trc::location!())?;
let ids = if let Some(ids) = ids {
ids
} else {
cache
.document_ids(false)
.take(self.core.jmap.get_max_objects)
.map(Into::into)
.collect::<Vec<_>>()
};
let mut response = CalendarEventNotificationGetResponse {
account_id: request.account_id.into(),
state: cache.get_state(false).into(),
list: Vec::with_capacity(ids.len()),
not_found: not_found_ids,
};
for id in ids {
// Obtain the event object
let document_id = id.document_id();
let _event = if let Some(event) = self
.store()
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
account_id,
Collection::CalendarEventNotification,
document_id,
))
.await?
{
event
} else {
response.push_not_found(id);
continue;
};
let event = _event
.unarchive::<CalendarEventNotification>()
.caused_by(trc::location!())?;
let mut result = CalendarEventNotificationObject {
id,
..Default::default()
};
for property in &properties {
match property {
CalendarEventNotificationProperty::Id => {}
CalendarEventNotificationProperty::Created => {
result.created = Some(UTCDate::from_timestamp(event.created.to_native()));
}
CalendarEventNotificationProperty::CalendarEventId => {
result.calendar_event_id =
event.event_id.as_ref().map(|id| id.to_native().into());
}
CalendarEventNotificationProperty::ChangedBy => {
let mut changed_by = PersonObject::default();
match &event.changed_by {
ArchivedChangedBy::PrincipalId(id) => {
if let Ok(account) = self.account(id.to_native()).await {
changed_by.name =
account.description().unwrap_or(account.name()).to_string();
changed_by.email = account.name().to_string().into();
}
changed_by.principal_id = Some(id.to_native().into());
}
ArchivedChangedBy::CalendarAddress(email) => {
changed_by.email = Some(email.to_string());
changed_by.calendar_address = Some(format!("mailto:{email}"));
}
}
result.changed_by = Some(changed_by);
}
CalendarEventNotificationProperty::Comment => {
result.comment = event
.event
.components
.iter()
.filter(|c| c.component_type.is_scheduling_object())
.flat_map(|c| c.entries.iter())
.find(|e| matches!(e.name, ArchivedICalendarProperty::Comment))
.and_then(|e| e.values.first().and_then(|v| v.as_text()))
.map(|v| v.to_string());
}
CalendarEventNotificationProperty::Type => {
result.notification_type =
Some(if event.flags & EVENT_NOTIFICATION_IS_CHANGE != 0 {
CalendarEventNotificationType::Updated
} else if !event.event.components.is_empty() {
CalendarEventNotificationType::Created
} else {
CalendarEventNotificationType::Destroyed
});
}
CalendarEventNotificationProperty::IsDraft => {
result.is_draft = Some(event.flags & EVENT_NOTIFICATION_IS_DRAFT != 0);
}
CalendarEventNotificationProperty::Event => {
if event.flags & EVENT_NOTIFICATION_IS_CHANGE == 0 && result.event.is_none()
{
let js_event = rkyv_deserialize::<_, ICalendar>(&event.event)
.caused_by(trc::location!())?
.into_jscalendar_with_opt::<Id, BlobId>(
ConversionOptions::default()
.include_ical_components(false)
.return_first(true),
);
result.event = js_event.into();
}
}
CalendarEventNotificationProperty::EventPatch => {
if event.flags & EVENT_NOTIFICATION_IS_CHANGE != 0
&& result.event_patch.is_none()
{
let js_event = rkyv_deserialize::<_, ICalendar>(&event.event)
.caused_by(trc::location!())?
.into_jscalendar_with_opt::<Id, BlobId>(
ConversionOptions::default()
.include_ical_components(false)
.return_first(true),
);
result.event_patch = js_event.into();
}
}
}
}
response.list.push(result);
}
Ok(response)
}
}
@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod get;
pub mod query;
pub mod set;
@@ -0,0 +1,196 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::{api::query::QueryResponseBuilder, changes::state::JmapCacheState};
use common::{Server, auth::AccessToken};
use groupware::cache::GroupwareCache;
use jmap_proto::{
method::query::{Filter, QueryRequest, QueryResponse},
object::calendar_event_notification::{
CalendarEventNotification, CalendarEventNotificationComparator,
CalendarEventNotificationFilter,
},
request::IntoValid,
};
use store::{
IterateParams, U32_LEN, U64_LEN, ValueKey,
ahash::AHashSet,
roaring::RoaringBitmap,
search::{SearchFilter, SearchQuery},
write::{IndexPropertyClass, SearchIndex, ValueClass, key::DeserializeBigEndian},
};
use trc::AddContext;
use types::{
collection::{Collection, SyncCollection},
field::CalendarNotificationField,
};
pub trait CalendarEventNotificationQuery: Sync + Send {
fn calendar_event_notification_query(
&self,
request: QueryRequest<CalendarEventNotification>,
access_token: &AccessToken,
) -> impl Future<Output = trc::Result<QueryResponse>> + Send;
}
struct Notification {
document_id: u32,
created: u64,
event_id: u32,
}
impl CalendarEventNotificationQuery for Server {
async fn calendar_event_notification_query(
&self,
mut request: QueryRequest<CalendarEventNotification>,
access_token: &AccessToken,
) -> trc::Result<QueryResponse> {
let account_id = request.account_id.document_id();
let mut filters = Vec::with_capacity(request.filter.len());
let cache = self
.fetch_dav_resources(
access_token.account_id(),
account_id,
SyncCollection::CalendarEventNotification,
)
.await?;
let mut notifications = Vec::with_capacity(16);
let mut document_ids = RoaringBitmap::new();
self.store()
.iterate(
IterateParams::new(
ValueKey {
account_id,
collection: Collection::CalendarEventNotification.into(),
document_id: 0,
class: ValueClass::IndexProperty(IndexPropertyClass::Integer {
property: CalendarNotificationField::CreatedToId.into(),
value: 0,
}),
},
ValueKey {
account_id,
collection: Collection::CalendarEventNotification.into(),
document_id: 0,
class: ValueClass::IndexProperty(IndexPropertyClass::Integer {
property: CalendarNotificationField::CreatedToId.into(),
value: u64::MAX,
}),
},
)
.ascending(),
|key, value| {
let document_id = key.deserialize_be_u32(key.len() - U32_LEN)?;
notifications.push(Notification {
document_id,
created: key.deserialize_be_u64(key.len() - U32_LEN - U64_LEN)?,
event_id: value.deserialize_be_u32(0)?,
});
document_ids.insert(document_id);
Ok(true)
},
)
.await
.caused_by(trc::location!())?;
for cond in std::mem::take(&mut request.filter) {
match cond {
Filter::Property(cond) => match cond {
CalendarEventNotificationFilter::Before(before) => {
let before = before.timestamp() as u64;
filters.push(SearchFilter::is_in_set(RoaringBitmap::from_iter(
notifications
.iter()
.filter_map(|n| (n.created < before).then_some(n.document_id)),
)))
}
CalendarEventNotificationFilter::After(after) => {
let after = after.timestamp() as u64;
filters.push(SearchFilter::is_in_set(RoaringBitmap::from_iter(
notifications
.iter()
.filter_map(|n| (n.created > after).then_some(n.document_id)),
)))
}
CalendarEventNotificationFilter::CalendarEventIds(ids) => {
let ids = ids
.into_valid()
.map(|id| id.document_id())
.collect::<AHashSet<_>>();
filters.push(SearchFilter::is_in_set(RoaringBitmap::from_iter(
notifications
.iter()
.filter_map(|n| ids.contains(&n.event_id).then_some(n.document_id)),
)))
}
unsupported => {
return Err(trc::JmapEvent::UnsupportedFilter
.into_err()
.details(unsupported.into_string()));
}
},
Filter::And => {
filters.push(SearchFilter::And);
}
Filter::Or => {
filters.push(SearchFilter::Or);
}
Filter::Not => {
filters.push(SearchFilter::Not);
}
Filter::Close => {
filters.push(SearchFilter::End);
}
}
}
// Parse sort criteria
let mut is_ascending = true;
for comparator in request.sort.take().unwrap_or_default() {
match comparator.property {
CalendarEventNotificationComparator::Created => {
is_ascending = comparator.is_ascending;
}
CalendarEventNotificationComparator::_T(unsupported) => {
return Err(trc::JmapEvent::UnsupportedSort
.into_err()
.details(unsupported));
}
};
}
if !is_ascending {
notifications.reverse();
}
let results = SearchQuery::new(SearchIndex::InMemory)
.with_filters(filters)
.with_mask(document_ids)
.filter()
.into_bitmap();
let mut response = QueryResponseBuilder::new(
results.len() as usize,
self.core.jmap.query_max_results,
cache.get_state(false),
&request,
);
if !results.is_empty() {
let results = results.into_iter().collect::<AHashSet<_>>();
for notification in notifications {
if results.contains(&notification.document_id)
&& !response.add(0, notification.document_id)
{
break;
}
}
}
response.build()
}
}
@@ -0,0 +1,120 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use common::{Server, auth::AccessToken};
use groupware::{DestroyArchive, cache::GroupwareCache, calendar::CalendarEventNotification};
use http_proto::HttpSessionData;
use jmap_proto::{
error::set::SetError,
method::set::{SetRequest, SetResponse},
object::calendar_event_notification,
request::IntoValid,
types::state::State,
};
use store::{
ValueKey,
write::{AlignedBytes, Archive, BatchBuilder},
};
use trc::AddContext;
use types::collection::{Collection, SyncCollection};
pub trait CalendarEventNotificationSet: Sync + Send {
fn calendar_event_notification_set(
&self,
request: SetRequest<'_, calendar_event_notification::CalendarEventNotification>,
access_token: &AccessToken,
session: &HttpSessionData,
) -> impl Future<
Output = trc::Result<SetResponse<calendar_event_notification::CalendarEventNotification>>,
> + Send;
}
impl CalendarEventNotificationSet for Server {
async fn calendar_event_notification_set(
&self,
mut request: SetRequest<'_, calendar_event_notification::CalendarEventNotification>,
access_token: &AccessToken,
_session: &HttpSessionData,
) -> trc::Result<SetResponse<calendar_event_notification::CalendarEventNotification>> {
let account_id = request.account_id.document_id();
let cache = self
.fetch_dav_resources(
access_token.account_id(),
account_id,
SyncCollection::CalendarEventNotification,
)
.await?;
let mut response = SetResponse::from_request(&request, self.core.jmap.set_max_objects)?;
let mut batch = BatchBuilder::new();
for (id, _) in request.unwrap_create() {
response.not_created.append(
id,
SetError::forbidden().with_description("Cannot create event notifications."),
);
}
// Process updates
for (id, _) in request.unwrap_update().into_valid() {
response.not_updated.append(
id,
SetError::forbidden().with_description("Cannot update event notifications."),
);
}
// Process deletions
for id in request.unwrap_destroy().into_valid() {
let document_id = id.document_id();
if !cache.has_item_id(&document_id) {
response.not_destroyed.append(id, SetError::not_found());
continue;
};
let _event = if let Some(event) = self
.store()
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
account_id,
Collection::CalendarEventNotification,
document_id,
))
.await?
{
event
} else {
response.not_destroyed.append(id, SetError::not_found());
continue;
};
let event = _event
.to_unarchived::<CalendarEventNotification>()
.caused_by(trc::location!())?;
DestroyArchive(event)
.delete(
access_token.account_tenant_ids(),
account_id,
document_id,
&mut batch,
)
.caused_by(trc::location!())?;
response.destroyed.push(id);
}
// Write changes
if !batch.is_empty() {
let change_id = self
.commit_batch(batch)
.await
.and_then(|ids| ids.last_change_id(account_id))
.caused_by(trc::location!())?;
response.new_state = State::Exact(change_id).into();
}
Ok(response)
}
}