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.
321 lines
12 KiB
Rust
321 lines
12 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use super::get::ChangesLookup;
|
|
use crate::{
|
|
api::request::resolve_account_id, calendar_event::query::CalendarEventQuery,
|
|
calendar_event_notification::query::CalendarEventNotificationQuery,
|
|
contact::query::ContactCardQuery, email::query::EmailQuery, file::query::FileNodeQuery,
|
|
mailbox::query::MailboxQuery, share_notification::query::ShareNotificationQuery,
|
|
submission::query::EmailSubmissionQuery,
|
|
};
|
|
use common::{Server, auth::AccessToken};
|
|
use jmap_proto::{
|
|
method::{
|
|
changes::{ChangesRequest, ChangesResponse},
|
|
query_changes::{AddedItem, QueryChangesRequest, QueryChangesResponse},
|
|
},
|
|
object::{JmapObject, NullObject},
|
|
request::{QueryChangesRequestMethod, method::MethodObject},
|
|
};
|
|
use std::future::Future;
|
|
|
|
pub trait QueryChanges: Sync + Send {
|
|
fn query_changes(
|
|
&self,
|
|
request: QueryChangesRequestMethod,
|
|
access_token: &AccessToken,
|
|
) -> impl Future<Output = trc::Result<QueryChangesResponse>> + Send;
|
|
}
|
|
|
|
impl QueryChanges for Server {
|
|
async fn query_changes(
|
|
&self,
|
|
request: QueryChangesRequestMethod,
|
|
access_token: &AccessToken,
|
|
) -> trc::Result<QueryChangesResponse> {
|
|
let mut response;
|
|
let mut is_mutable = true;
|
|
let results;
|
|
let changes;
|
|
let has_changes;
|
|
let up_to_id;
|
|
|
|
match request {
|
|
QueryChangesRequestMethod::Email(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(&mut request.account_id, MethodObject::Email, access_token)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::Email,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
is_mutable = request.filter.iter().any(|f| !f.is_immutable())
|
|
|| request
|
|
.sort
|
|
.as_ref()
|
|
.is_some_and(|sort| sort.iter().any(|s| !s.is_immutable()));
|
|
|
|
results = self.email_query((*request).into(), access_token).await?;
|
|
}
|
|
QueryChangesRequestMethod::Mailbox(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(&mut request.account_id, MethodObject::Mailbox, access_token)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::Mailbox,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self.mailbox_query((*request).into(), access_token).await?;
|
|
}
|
|
QueryChangesRequestMethod::EmailSubmission(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::EmailSubmission,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::EmailSubmission,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self.email_submission_query((*request).into()).await?;
|
|
}
|
|
QueryChangesRequestMethod::ContactCard(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::ContactCard,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::ContactCard,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self
|
|
.contact_card_query((*request).into(), access_token)
|
|
.await?;
|
|
}
|
|
QueryChangesRequestMethod::FileNode(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::FileNode,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::FileNode,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self
|
|
.file_node_query((*request).into(), access_token)
|
|
.await?;
|
|
}
|
|
QueryChangesRequestMethod::CalendarEvent(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::CalendarEvent,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::CalendarEvent,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self
|
|
.calendar_event_query((*request).into(), access_token)
|
|
.await?;
|
|
}
|
|
QueryChangesRequestMethod::CalendarEventNotification(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::CalendarEventNotification,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::CalendarEventNotification,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self
|
|
.calendar_event_notification_query((*request).into(), access_token)
|
|
.await?;
|
|
}
|
|
QueryChangesRequestMethod::ShareNotification(mut request) => {
|
|
// Query changes
|
|
resolve_account_id(
|
|
&mut request.account_id,
|
|
MethodObject::ShareNotification,
|
|
access_token,
|
|
)?;
|
|
changes = self
|
|
.changes(
|
|
build_changes_request(&request),
|
|
MethodObject::ShareNotification,
|
|
access_token,
|
|
)
|
|
.await?
|
|
.response;
|
|
let calculate_total = request.calculate_total.unwrap_or(false);
|
|
has_changes = changes.has_changes();
|
|
response = build_query_changes_response(&request, &changes);
|
|
|
|
if !has_changes && !calculate_total {
|
|
return Ok(response);
|
|
}
|
|
|
|
up_to_id = request.up_to_id;
|
|
results = self.share_notification_query((*request).into()).await?;
|
|
}
|
|
QueryChangesRequestMethod::Principal(_) => {
|
|
return Err(trc::JmapEvent::CannotCalculateChanges.into_err());
|
|
}
|
|
QueryChangesRequestMethod::Quota(_) => {
|
|
return Err(trc::JmapEvent::CannotCalculateChanges.into_err());
|
|
}
|
|
}
|
|
|
|
if has_changes {
|
|
if is_mutable {
|
|
for (index, id) in results.ids.into_iter().enumerate() {
|
|
if changes.created.contains(&id) || changes.updated.contains(&id) {
|
|
response.added.push(AddedItem::new(id, index));
|
|
}
|
|
}
|
|
|
|
response.removed = changes.updated;
|
|
} else {
|
|
for (index, id) in results.ids.into_iter().enumerate() {
|
|
if changes.created.contains(&id) {
|
|
response.added.push(AddedItem::new(id, index));
|
|
}
|
|
if matches!(up_to_id, Some(up_to_id) if up_to_id == id) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !changes.destroyed.is_empty() {
|
|
response.removed.extend(changes.destroyed);
|
|
}
|
|
}
|
|
response.total = results.total;
|
|
|
|
Ok(response)
|
|
}
|
|
}
|
|
|
|
fn build_changes_request<T: JmapObject>(req: &QueryChangesRequest<T>) -> ChangesRequest {
|
|
ChangesRequest {
|
|
account_id: req.account_id,
|
|
since_state: req.since_query_state.clone(),
|
|
max_changes: req.max_changes,
|
|
}
|
|
}
|
|
|
|
fn build_query_changes_response<T: JmapObject>(
|
|
req: &QueryChangesRequest<T>,
|
|
changes: &ChangesResponse<NullObject>,
|
|
) -> QueryChangesResponse {
|
|
QueryChangesResponse {
|
|
account_id: req.account_id,
|
|
old_query_state: changes.old_state.clone(),
|
|
new_query_state: changes.new_state.clone(),
|
|
total: None,
|
|
removed: vec![],
|
|
added: vec![],
|
|
}
|
|
}
|