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
+193
View File
@@ -0,0 +1,193 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::changes::state::StateManager;
use common::Server;
use email::sieve::{SieveScript, ingest::SieveScriptIngest};
use jmap_proto::{
method::get::{GetRequest, GetResponse},
object::vacation_response::{
VacationResponse, VacationResponseProperty, VacationResponseValue,
},
request::{
MaybeInvalid,
reference::{MaybeIdReference, MaybeResultReference},
},
types::date::UTCDate,
};
use jmap_tools::{Map, Value};
use std::future::Future;
use store::{
ValueKey,
write::{AlignedBytes, Archive},
};
use trc::AddContext;
use types::{
collection::{Collection, SyncCollection},
field::SieveField,
id::Id,
};
pub trait VacationResponseGet: Sync + Send {
fn vacation_response_get(
&self,
request: GetRequest<VacationResponse>,
) -> impl Future<Output = trc::Result<GetResponse<VacationResponse>>> + Send;
fn get_vacation_sieve_script_id(
&self,
account_id: u32,
) -> impl Future<Output = trc::Result<Option<u32>>> + Send;
}
impl VacationResponseGet for Server {
async fn vacation_response_get(
&self,
mut request: GetRequest<VacationResponse>,
) -> trc::Result<GetResponse<VacationResponse>> {
let account_id = request.account_id.document_id();
let properties = request.unwrap_properties(&[
VacationResponseProperty::Id,
VacationResponseProperty::IsEnabled,
VacationResponseProperty::FromDate,
VacationResponseProperty::ToDate,
VacationResponseProperty::Subject,
VacationResponseProperty::TextBody,
VacationResponseProperty::HtmlBody,
]);
let mut response = GetResponse {
account_id: request.account_id.into(),
state: self
.get_state(account_id, SyncCollection::SieveScript)
.await?
.into(),
list: Vec::with_capacity(1),
not_found: vec![],
};
let do_get = if let Some(MaybeResultReference::Value(ids)) = request.ids {
let mut do_get = false;
for id in ids {
match id {
MaybeIdReference::Id(id) if id.is_singleton() => {
do_get = true;
}
MaybeIdReference::Id(id) => {
response.push_not_found(id);
}
MaybeIdReference::Invalid(s) | MaybeIdReference::Reference(s) => {
response.not_found.push(MaybeInvalid::Invalid(s));
}
}
}
do_get
} else {
true
};
if do_get {
let mut result = Map::with_capacity(properties.len());
if let Some(document_id) = self.get_vacation_sieve_script_id(account_id).await?
&& let Some(sieve_) = self
.store()
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
account_id,
Collection::SieveScript,
document_id,
))
.await?
{
let active_script_id = self.sieve_script_get_active_id(account_id).await?;
let sieve = sieve_
.unarchive::<SieveScript>()
.caused_by(trc::location!())?;
let vacation = sieve.vacation_response.as_ref();
for property in &properties {
match property {
VacationResponseProperty::Id => {
result.insert_unchecked(VacationResponseProperty::Id, Id::singleton());
}
VacationResponseProperty::IsEnabled => {
result.insert_unchecked(
VacationResponseProperty::IsEnabled,
active_script_id == Some(document_id),
);
}
VacationResponseProperty::FromDate => {
result.insert_unchecked(
VacationResponseProperty::FromDate,
vacation.and_then(|r| {
r.from_date
.as_ref()
.map(u64::from)
.map(UTCDate::from)
.map(|v| Value::Element(VacationResponseValue::Date(v)))
}),
);
}
VacationResponseProperty::ToDate => {
result.insert_unchecked(
VacationResponseProperty::ToDate,
vacation.and_then(|r| {
r.to_date
.as_ref()
.map(u64::from)
.map(UTCDate::from)
.map(|v| Value::Element(VacationResponseValue::Date(v)))
}),
);
}
VacationResponseProperty::Subject => {
result.insert_unchecked(
VacationResponseProperty::Subject,
vacation.and_then(|r| r.subject.as_ref()),
);
}
VacationResponseProperty::TextBody => {
result.insert_unchecked(
VacationResponseProperty::TextBody,
vacation.and_then(|r| r.text_body.as_ref()),
);
}
VacationResponseProperty::HtmlBody => {
result.insert_unchecked(
VacationResponseProperty::HtmlBody,
vacation.and_then(|r| r.html_body.as_ref()),
);
}
}
}
} else {
for property in &properties {
match property {
VacationResponseProperty::Id => {
result.insert_unchecked(VacationResponseProperty::Id, Id::singleton());
}
VacationResponseProperty::IsEnabled => {
result.insert_unchecked(VacationResponseProperty::IsEnabled, false);
}
_ => {
result.insert_unchecked(property.clone(), Value::Null);
}
}
}
}
response.list.push(result.into());
}
Ok(response)
}
async fn get_vacation_sieve_script_id(&self, account_id: u32) -> trc::Result<Option<u32>> {
self.document_ids_matching(
account_id,
Collection::SieveScript,
SieveField::Name,
"vacation".as_bytes(),
)
.await
.map(|r| r.min())
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod get;
pub mod set;
+456
View File
@@ -0,0 +1,456 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::get::VacationResponseGet;
use crate::changes::state::StateManager;
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use email::sieve::{SieveScript, VacationResponse, ingest::SieveScriptIngest};
use jmap_proto::{
error::set::{SetError, SetErrorType},
method::set::{SetRequest, SetResponse},
object::vacation_response::{self, VacationResponseProperty, VacationResponseValue},
references::resolve::ResolveCreatedReference,
request::IntoValid,
types::{date::UTCDate, state::State},
};
use jmap_tools::{Key, Map, Value};
use mail_builder::MessageBuilder;
use mail_parser::decoders::html::html_to_text;
use std::borrow::Cow;
use std::future::Future;
use store::{
Serialize, SerializeInfallible, ValueKey,
write::{AlignedBytes, Archive, Archiver, BatchBuilder},
};
use trc::AddContext;
use types::{
collection::{Collection, SyncCollection},
field::PrincipalField,
id::Id,
};
pub trait VacationResponseSet: Sync + Send {
fn vacation_response_set(
&self,
request: SetRequest<'_, vacation_response::VacationResponse>,
access_token: &AccessToken,
) -> impl Future<Output = trc::Result<SetResponse<vacation_response::VacationResponse>>> + Send;
fn build_script(&self, obj: &mut SieveScript) -> trc::Result<Vec<u8>>;
}
impl VacationResponseSet for Server {
async fn vacation_response_set(
&self,
mut request: SetRequest<'_, vacation_response::VacationResponse>,
access_token: &AccessToken,
) -> trc::Result<SetResponse<vacation_response::VacationResponse>> {
let account_id = request.account_id.document_id();
let mut response = SetResponse::from_request(&request, self.core.jmap.set_max_objects)?
.with_state(
self.assert_state(
account_id,
SyncCollection::SieveScript,
&request.if_in_state,
)
.await?,
);
for id in request.unwrap_destroy().into_valid() {
response.not_destroyed.append(id, SetError::singleton());
}
// Process set or update requests
let create_id = None;
let mut changes = None;
match (request.create, request.update) {
(Some(create), Some(update)) if !create.is_empty() && !update.is_empty() => {
return Err(trc::JmapEvent::InvalidArguments
.into_err()
.details("Creating and updating on the same request is not allowed."));
}
(Some(create), _) if !create.is_empty() => {
for (id, _obj) in create {
response.not_created.append(id, SetError::singleton());
}
}
(_, Some(update)) if !update.is_empty() => {
for (id, obj) in update.into_valid() {
if id.is_singleton() {
changes = Some(obj);
} else {
response.not_updated.append(
id,
SetError::new(SetErrorType::NotFound).with_description("ID not found."),
);
}
}
}
_ => {
return Ok(response);
}
}
// Prepare write batch
let mut batch = BatchBuilder::new();
batch
.with_account_id(account_id)
.with_collection(Collection::SieveScript);
// Process changes
let active_script_id = self.sieve_script_get_active_id(account_id).await?;
if let Some(changes) = changes {
// Obtain current script
let document_id = self.get_vacation_sieve_script_id(account_id).await?;
let (mut sieve, prev_sieve) = if let Some(document_id) = document_id {
let prev_sieve = self
.store()
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
account_id,
Collection::SieveScript,
document_id,
))
.await?
.ok_or_else(|| {
trc::StoreEvent::NotFound
.into_err()
.caused_by(trc::location!())
})?
.into_deserialized::<SieveScript>()
.caused_by(trc::location!())?;
let mut sieve = prev_sieve.inner.clone();
if sieve.vacation_response.is_none() {
sieve.vacation_response = VacationResponse::default().into();
}
(sieve, Some(prev_sieve))
} else {
(
SieveScript {
name: "vacation".into(),
blob_hash: Default::default(),
size: 0,
vacation_response: VacationResponse::default().into(),
},
None,
)
};
// Parse properties
let mut is_active = document_id.is_some_and(|id| active_script_id == Some(id));
let mut build_script = create_id.is_some();
let vacation = sieve.vacation_response.as_mut().unwrap();
for (property, mut value) in changes.into_expanded_object() {
if let Err(err) = response.resolve_self_references(&mut value, 0, false) {
return Ok(set_error(response, create_id, err));
};
match (&property, value) {
(Key::Property(VacationResponseProperty::Subject), Value::Str(value))
if value.len() < 512 =>
{
build_script = true;
vacation.subject = Some(value.into_owned());
}
(Key::Property(VacationResponseProperty::HtmlBody), Value::Str(value))
if value.len() < 2048 =>
{
build_script = true;
vacation.html_body = Some(value.into_owned());
}
(Key::Property(VacationResponseProperty::TextBody), Value::Str(value))
if value.len() < 2048 =>
{
build_script = true;
vacation.text_body = Some(value.into_owned());
}
(
Key::Property(VacationResponseProperty::FromDate),
Value::Element(VacationResponseValue::Date(date)),
) => {
vacation.from_date = Some(date.timestamp() as u64);
build_script = true;
}
(
Key::Property(VacationResponseProperty::ToDate),
Value::Element(VacationResponseValue::Date(date)),
) => {
vacation.to_date = Some(date.timestamp() as u64);
build_script = true;
}
(Key::Property(VacationResponseProperty::IsEnabled), Value::Bool(value)) => {
is_active = value;
}
(Key::Property(VacationResponseProperty::IsEnabled), Value::Null) => {
is_active = false;
}
(
Key::Property(
VacationResponseProperty::Subject
| VacationResponseProperty::HtmlBody
| VacationResponseProperty::TextBody
| VacationResponseProperty::ToDate
| VacationResponseProperty::FromDate,
),
Value::Null,
) => {
if create_id.is_none() {
build_script = true;
match property {
Key::Property(VacationResponseProperty::Subject) => {
vacation.subject = None;
}
Key::Property(VacationResponseProperty::HtmlBody) => {
vacation.html_body = None;
}
Key::Property(VacationResponseProperty::TextBody) => {
vacation.text_body = None;
}
Key::Property(VacationResponseProperty::FromDate) => {
vacation.from_date = None;
}
Key::Property(VacationResponseProperty::ToDate) => {
vacation.to_date = None;
}
_ => unreachable!(),
}
}
}
(Key::Property(VacationResponseProperty::Id), value) => {
if create_id.is_some() || !crate::matches_id(&value, Id::singleton()) {
return Ok(set_error(
response,
create_id,
SetError::invalid_properties()
.with_property(VacationResponseProperty::Id)
.with_description("The id property is immutable."),
));
}
}
_ => {
return Ok(set_error(
response,
create_id,
SetError::invalid_properties()
.with_property(property.into_owned())
.with_description("Field could not be set."),
));
}
}
}
let mut obj = ObjectIndexBuilder::new()
.with_current_opt(prev_sieve)
.with_changes(sieve)
.with_changed_by(access_token.account_tenant_ids());
// Update id
let document_id = if let Some(document_id) = document_id {
batch.with_document(document_id);
document_id
} else {
let document_id = self
.store()
.assign_document_ids(account_id, Collection::SieveScript, 1)
.await
.caused_by(trc::location!())?;
batch.with_document(document_id);
document_id
};
// Create sieve script only if there are changes
if build_script {
// Upload new blob
let (blob_hash, blob_hold) = self
.put_temporary_blob(
account_id,
&self.build_script(obj.changes_mut().unwrap())?,
60,
)
.await?;
obj.changes_mut().unwrap().blob_hash = blob_hash;
batch.clear(blob_hold);
};
batch.custom(obj).caused_by(trc::location!())?;
// Deactivate other sieve scripts
let was_active = active_script_id == Some(document_id);
if is_active {
if !was_active {
batch
.with_collection(Collection::Principal)
.with_document(0)
.set(PrincipalField::ActiveScriptId, document_id.serialize());
}
} else if was_active {
batch
.with_collection(Collection::Principal)
.with_document(0)
.clear(PrincipalField::ActiveScriptId);
}
// Write changes
if !batch.is_empty() {
response.new_state = Some(State::Exact(
self.commit_batch(batch)
.await
.and_then(|ids| ids.last_change_id(account_id))
.caused_by(trc::location!())?,
));
}
// Add result
if let Some(create_id) = create_id {
response.created.insert(
create_id,
Map::with_capacity(1)
.with_key_value(VacationResponseProperty::Id, Id::singleton())
.into(),
);
} else {
response.updated.append(Id::singleton(), None);
}
}
Ok(response)
}
fn build_script(&self, obj: &mut SieveScript) -> trc::Result<Vec<u8>> {
// Build Sieve script
let mut script = Vec::with_capacity(1024);
script.extend_from_slice(b"require [\"vacation\", \"relational\", \"date\"];\r\n\r\n");
let mut num_blocks = 0;
// Add start date
if let Some(value) = obj.vacation_response.as_ref().and_then(|v| v.from_date) {
script.extend_from_slice(b"if currentdate :value \"ge\" \"iso8601\" \"");
script.extend_from_slice(UTCDate::from(value).to_string().as_bytes());
script.extend_from_slice(b"\" {\r\n");
num_blocks += 1;
}
// Add end date
if let Some(value) = obj.vacation_response.as_ref().and_then(|v| v.to_date) {
script.extend_from_slice(b"if currentdate :value \"le\" \"iso8601\" \"");
script.extend_from_slice(UTCDate::from(value).to_string().as_bytes());
script.extend_from_slice(b"\" {\r\n");
num_blocks += 1;
}
script.extend_from_slice(b"vacation :mime ");
if let Some(value) = obj
.vacation_response
.as_ref()
.and_then(|v| v.subject.as_ref())
{
script.extend_from_slice(b":subject \"");
for &ch in value.as_bytes().iter() {
match ch {
b'\\' | b'\"' => {
script.push(b'\\');
}
b'\r' | b'\n' => {
continue;
}
_ => (),
}
script.push(ch);
}
script.extend_from_slice(b"\" ");
}
let mut text_body = if let Some(value) = obj
.vacation_response
.as_ref()
.and_then(|v| v.text_body.as_ref())
{
Cow::from(value.as_str()).into()
} else {
None
};
let html_body = if let Some(value) = obj
.vacation_response
.as_ref()
.and_then(|v| v.html_body.as_ref())
{
Cow::from(value.as_str()).into()
} else {
None
};
match (&html_body, &text_body) {
(Some(html_body), None) => {
text_body = Cow::from(html_to_text(html_body.as_ref())).into();
}
(None, None) => {
text_body = Cow::from("I am away.").into();
}
_ => (),
}
let mut builder = MessageBuilder::new();
let mut body_len = 0;
if let Some(html_body) = html_body {
body_len = html_body.len();
builder = builder.html_body(html_body);
}
if let Some(text_body) = text_body {
body_len += text_body.len();
builder = builder.text_body(text_body);
}
let mut message_body = Vec::with_capacity(body_len + 128);
builder.write_body(&mut message_body).ok();
script.push(b'\"');
for ch in message_body {
if b"\\\"".contains(&ch) {
script.push(b'\\');
}
script.push(ch);
}
script.extend_from_slice(b"\";\r\n");
// Close blocks
for _ in 0..num_blocks {
script.extend_from_slice(b"}\r\n");
}
match self.core.sieve.untrusted_compiler.compile(&script) {
Ok(compiled_script) => {
// Update blob length
obj.size = script.len() as u32;
// Serialize script
script.extend(
Archiver::new(compiled_script)
.untrusted()
.serialize()
.caused_by(trc::location!())?,
);
Ok(script)
}
Err(err) => Err(trc::StoreEvent::UnexpectedError
.caused_by(trc::location!())
.reason(err)
.details("Vacation Sieve Script failed to compile.")),
}
}
}
fn set_error(
mut response: SetResponse<vacation_response::VacationResponse>,
id: Option<String>,
err: SetError<VacationResponseProperty>,
) -> SetResponse<vacation_response::VacationResponse> {
if let Some(id) = id {
response.not_created.append(id, err);
} else {
response.not_updated.append(Id::singleton(), err);
}
response
}