mirror of
https://github.com/stalwartlabs/stalwart.git
synced 2026-03-17 14:34:03 +00:00
Moved shared data types to 'types' subcrate
This commit is contained in:
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -1285,6 +1285,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"unicode-security",
|
||||
"utils",
|
||||
"whatlang",
|
||||
@@ -1737,11 +1738,11 @@ dependencies = [
|
||||
"hashify",
|
||||
"http_proto",
|
||||
"hyper 1.7.0",
|
||||
"jmap_proto",
|
||||
"percent-encoding",
|
||||
"rkyv",
|
||||
"store",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -1949,7 +1950,6 @@ dependencies = [
|
||||
"compact_str",
|
||||
"deadpool 0.10.0",
|
||||
"futures",
|
||||
"jmap_proto",
|
||||
"ldap3",
|
||||
"mail-builder",
|
||||
"mail-parser",
|
||||
@@ -1976,6 +1976,7 @@ dependencies = [
|
||||
"tokio-rustls 0.26.2",
|
||||
"totp-rs",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -2230,7 +2231,6 @@ dependencies = [
|
||||
"directory",
|
||||
"groupware",
|
||||
"hashify",
|
||||
"jmap_proto",
|
||||
"mail-builder",
|
||||
"mail-parser",
|
||||
"nlp",
|
||||
@@ -2249,6 +2249,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -2848,12 +2849,12 @@ dependencies = [
|
||||
"dav-proto",
|
||||
"directory",
|
||||
"hashify",
|
||||
"jmap_proto",
|
||||
"percent-encoding",
|
||||
"rkyv",
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -3171,6 +3172,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
"x509-parser 0.17.0",
|
||||
]
|
||||
@@ -3558,7 +3560,6 @@ dependencies = [
|
||||
"email",
|
||||
"imap_proto",
|
||||
"indexmap 2.11.1",
|
||||
"jmap_proto",
|
||||
"mail-parser",
|
||||
"mail-send",
|
||||
"md5 0.8.0",
|
||||
@@ -3571,6 +3572,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -3582,11 +3584,11 @@ dependencies = [
|
||||
"chrono",
|
||||
"compact_str",
|
||||
"hashify",
|
||||
"jmap_proto",
|
||||
"mail-parser",
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3891,6 +3893,7 @@ dependencies = [
|
||||
"tokio-tungstenite 0.27.0",
|
||||
"trc",
|
||||
"tungstenite 0.27.0",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -3931,6 +3934,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -4426,6 +4430,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -4523,7 +4528,6 @@ dependencies = [
|
||||
"directory",
|
||||
"email",
|
||||
"groupware",
|
||||
"jmap_proto",
|
||||
"lz4_flex",
|
||||
"mail-auth",
|
||||
"mail-parser",
|
||||
@@ -4536,6 +4540,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -5521,7 +5526,6 @@ dependencies = [
|
||||
"directory",
|
||||
"email",
|
||||
"imap",
|
||||
"jmap_proto",
|
||||
"mail-parser",
|
||||
"mail-send",
|
||||
"rustls 0.23.31",
|
||||
@@ -5529,6 +5533,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -7411,6 +7416,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -7633,6 +7639,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
"webpki-roots 1.0.2",
|
||||
"x509-parser 0.17.0",
|
||||
@@ -7768,7 +7775,6 @@ dependencies = [
|
||||
"imap",
|
||||
"jemallocator",
|
||||
"jmap",
|
||||
"jmap_proto",
|
||||
"managesieve",
|
||||
"migration",
|
||||
"pop3",
|
||||
@@ -7778,6 +7784,7 @@ dependencies = [
|
||||
"store",
|
||||
"tokio",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -7861,6 +7868,7 @@ dependencies = [
|
||||
"tokio-postgres",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
"xxhash-rust",
|
||||
"zenoh",
|
||||
@@ -8082,6 +8090,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -8690,6 +8699,19 @@ version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "types"
|
||||
version = "0.13.3"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"compact_str",
|
||||
"hashify",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"trc",
|
||||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typewit"
|
||||
version = "1.14.2"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
resolver = "2"
|
||||
members = [
|
||||
"crates/main",
|
||||
"crates/types",
|
||||
"crates/http",
|
||||
"crates/http-proto",
|
||||
"crates/jmap",
|
||||
|
||||
@@ -11,6 +11,7 @@ nlp = { path = "../nlp" }
|
||||
store = { path = "../store" }
|
||||
trc = { path = "../trc" }
|
||||
directory = { path = "../directory" }
|
||||
types = { path = "../types" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
imap_proto = { path = "../imap-proto" }
|
||||
sieve-rs = { version = "0.7", features = ["rkyv", "serde"] }
|
||||
|
||||
@@ -18,16 +18,13 @@ use directory::{
|
||||
manage::{ChangedPrincipals, ManageDirectory},
|
||||
},
|
||||
};
|
||||
use jmap_proto::{
|
||||
request::RequestMethod,
|
||||
types::{acl::Acl, collection::Collection, id::Id},
|
||||
};
|
||||
use std::{
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
sync::Arc,
|
||||
};
|
||||
use store::{query::acl::AclQuery, rand};
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::Collection};
|
||||
use utils::map::{
|
||||
bitmap::{Bitmap, BitmapItem},
|
||||
vec_map::VecMap,
|
||||
@@ -163,7 +160,7 @@ impl Server {
|
||||
{
|
||||
if !access_token.is_member(acl_item.to_account_id) {
|
||||
let acl = Bitmap::<Acl>::from(acl_item.permissions);
|
||||
let collection = Collection::from(acl_item.to_collection);
|
||||
let collection = acl_item.to_collection;
|
||||
if !collection.is_valid() {
|
||||
return Err(trc::StoreEvent::DataCorruption
|
||||
.ctx(trc::Key::Reason, "Corrupted collection found in ACL key.")
|
||||
@@ -485,8 +482,7 @@ impl AccessToken {
|
||||
!self.is_member(account_id) && self.access_to.iter().any(|(id, _)| *id == account_id)
|
||||
}
|
||||
|
||||
pub fn shared_accounts(&self, collection: impl Into<Collection>) -> impl Iterator<Item = &u32> {
|
||||
let collection = collection.into();
|
||||
pub fn shared_accounts(&self, collection: Collection) -> impl Iterator<Item = &u32> {
|
||||
self.member_of
|
||||
.iter()
|
||||
.chain(self.access_to.iter().filter_map(move |(id, cols)| {
|
||||
@@ -510,152 +506,6 @@ impl AccessToken {
|
||||
self.is_member(to_account_id) || self.access_to.iter().any(|(id, _)| *id == to_account_id)
|
||||
}
|
||||
|
||||
pub fn assert_has_access(
|
||||
&self,
|
||||
to_account_id: Id,
|
||||
to_collection: Collection,
|
||||
) -> trc::Result<&Self> {
|
||||
if self.has_access(to_account_id.document_id(), to_collection) {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(trc::JmapEvent::Forbidden.into_err().details(format!(
|
||||
"You do not have access to account {}",
|
||||
to_account_id
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_is_member(&self, account_id: Id) -> trc::Result<&Self> {
|
||||
if self.is_member(account_id.document_id()) {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(trc::JmapEvent::Forbidden
|
||||
.into_err()
|
||||
.details(format!("You are not an owner of account {}", account_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_has_jmap_permission(&self, request: &RequestMethod) -> trc::Result<()> {
|
||||
let permission = match request {
|
||||
RequestMethod::Get(m) => match &m.arguments {
|
||||
jmap_proto::method::get::RequestArguments::Email(_) => Permission::JmapEmailGet,
|
||||
jmap_proto::method::get::RequestArguments::Mailbox => Permission::JmapMailboxGet,
|
||||
jmap_proto::method::get::RequestArguments::Thread => Permission::JmapThreadGet,
|
||||
jmap_proto::method::get::RequestArguments::Identity => Permission::JmapIdentityGet,
|
||||
jmap_proto::method::get::RequestArguments::EmailSubmission => {
|
||||
Permission::JmapEmailSubmissionGet
|
||||
}
|
||||
jmap_proto::method::get::RequestArguments::PushSubscription => {
|
||||
Permission::JmapPushSubscriptionGet
|
||||
}
|
||||
jmap_proto::method::get::RequestArguments::SieveScript => {
|
||||
Permission::JmapSieveScriptGet
|
||||
}
|
||||
jmap_proto::method::get::RequestArguments::VacationResponse => {
|
||||
Permission::JmapVacationResponseGet
|
||||
}
|
||||
jmap_proto::method::get::RequestArguments::Principal => {
|
||||
Permission::JmapPrincipalGet
|
||||
}
|
||||
jmap_proto::method::get::RequestArguments::Quota => Permission::JmapQuotaGet,
|
||||
jmap_proto::method::get::RequestArguments::Blob(_) => Permission::JmapBlobGet,
|
||||
},
|
||||
RequestMethod::Set(m) => match &m.arguments {
|
||||
jmap_proto::method::set::RequestArguments::Email => Permission::JmapEmailSet,
|
||||
jmap_proto::method::set::RequestArguments::Mailbox(_) => Permission::JmapMailboxSet,
|
||||
jmap_proto::method::set::RequestArguments::Identity => Permission::JmapIdentitySet,
|
||||
jmap_proto::method::set::RequestArguments::EmailSubmission(_) => {
|
||||
Permission::JmapEmailSubmissionSet
|
||||
}
|
||||
jmap_proto::method::set::RequestArguments::PushSubscription => {
|
||||
Permission::JmapPushSubscriptionSet
|
||||
}
|
||||
jmap_proto::method::set::RequestArguments::SieveScript(_) => {
|
||||
Permission::JmapSieveScriptSet
|
||||
}
|
||||
jmap_proto::method::set::RequestArguments::VacationResponse => {
|
||||
Permission::JmapVacationResponseSet
|
||||
}
|
||||
},
|
||||
RequestMethod::Changes(m) => match m.arguments {
|
||||
jmap_proto::method::changes::RequestArguments::Email => {
|
||||
Permission::JmapEmailChanges
|
||||
}
|
||||
jmap_proto::method::changes::RequestArguments::Mailbox => {
|
||||
Permission::JmapMailboxChanges
|
||||
}
|
||||
jmap_proto::method::changes::RequestArguments::Thread => {
|
||||
Permission::JmapThreadChanges
|
||||
}
|
||||
jmap_proto::method::changes::RequestArguments::Identity => {
|
||||
Permission::JmapIdentityChanges
|
||||
}
|
||||
jmap_proto::method::changes::RequestArguments::EmailSubmission => {
|
||||
Permission::JmapEmailSubmissionChanges
|
||||
}
|
||||
jmap_proto::method::changes::RequestArguments::Quota => {
|
||||
Permission::JmapQuotaChanges
|
||||
}
|
||||
},
|
||||
RequestMethod::Copy(m) => match m.arguments {
|
||||
jmap_proto::method::copy::RequestArguments::Email => Permission::JmapEmailCopy,
|
||||
},
|
||||
RequestMethod::CopyBlob(_) => Permission::JmapBlobCopy,
|
||||
RequestMethod::ImportEmail(_) => Permission::JmapEmailImport,
|
||||
RequestMethod::ParseEmail(_) => Permission::JmapEmailParse,
|
||||
RequestMethod::QueryChanges(m) => match m.arguments {
|
||||
jmap_proto::method::query::RequestArguments::Email(_) => {
|
||||
Permission::JmapEmailQueryChanges
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::Mailbox(_) => {
|
||||
Permission::JmapMailboxQueryChanges
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::EmailSubmission => {
|
||||
Permission::JmapEmailSubmissionQueryChanges
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::SieveScript => {
|
||||
Permission::JmapSieveScriptQueryChanges
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::Principal => {
|
||||
Permission::JmapPrincipalQueryChanges
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::Quota => {
|
||||
Permission::JmapQuotaQueryChanges
|
||||
}
|
||||
},
|
||||
RequestMethod::Query(m) => match m.arguments {
|
||||
jmap_proto::method::query::RequestArguments::Email(_) => Permission::JmapEmailQuery,
|
||||
jmap_proto::method::query::RequestArguments::Mailbox(_) => {
|
||||
Permission::JmapMailboxQuery
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::EmailSubmission => {
|
||||
Permission::JmapEmailSubmissionQuery
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::SieveScript => {
|
||||
Permission::JmapSieveScriptQuery
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::Principal => {
|
||||
Permission::JmapPrincipalQuery
|
||||
}
|
||||
jmap_proto::method::query::RequestArguments::Quota => Permission::JmapQuotaQuery,
|
||||
},
|
||||
RequestMethod::SearchSnippet(_) => Permission::JmapSearchSnippet,
|
||||
RequestMethod::ValidateScript(_) => Permission::JmapSieveScriptValidate,
|
||||
RequestMethod::LookupBlob(_) => Permission::JmapBlobLookup,
|
||||
RequestMethod::UploadBlob(_) => Permission::JmapBlobUpload,
|
||||
RequestMethod::Echo(_) => Permission::JmapEcho,
|
||||
RequestMethod::Error(_) => return Ok(()),
|
||||
};
|
||||
|
||||
if self.has_permission(permission) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(trc::JmapEvent::Forbidden
|
||||
.into_err()
|
||||
.details("You are not authorized to perform this action"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_resource_token(&self) -> ResourceToken {
|
||||
ResourceToken {
|
||||
account_id: self.primary_id,
|
||||
|
||||
@@ -9,10 +9,10 @@ use directory::{
|
||||
Directory, FALLBACK_ADMIN_ID, Permission, Permissions, Principal, QueryParams, Type,
|
||||
backend::internal::lookup::DirectoryStore, core::secret::verify_secret_hash,
|
||||
};
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use mail_send::Credentials;
|
||||
use oauth::GrantType;
|
||||
use std::{net::IpAddr, sync::Arc};
|
||||
use types::collection::Collection;
|
||||
use utils::{
|
||||
cache::CacheItemWeight,
|
||||
map::{bitmap::Bitmap, vec_map::VecMap},
|
||||
|
||||
@@ -4,18 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use ahash::AHashSet;
|
||||
use jmap_proto::{
|
||||
request::capability::{
|
||||
BlobCapabilities, Capabilities, Capability, CoreCapabilities, EmptyCapabilities,
|
||||
MailCapabilities, SieveAccountCapabilities, SieveSessionCapabilities,
|
||||
SubmissionCapabilities,
|
||||
},
|
||||
types::type_state::DataType,
|
||||
};
|
||||
use utils::{config::Config, map::vec_map::VecMap};
|
||||
|
||||
use super::settings::JmapConfig;
|
||||
use ahash::AHashSet;
|
||||
use jmap_proto::request::capability::{
|
||||
BlobCapabilities, Capabilities, Capability, CoreCapabilities, EmptyCapabilities,
|
||||
MailCapabilities, SieveAccountCapabilities, SieveSessionCapabilities, SubmissionCapabilities,
|
||||
};
|
||||
use types::type_state::DataType;
|
||||
use utils::{config::Config, map::vec_map::VecMap};
|
||||
|
||||
impl JmapConfig {
|
||||
pub fn add_capabilities(&mut self, config: &mut Config) {
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::{str::FromStr, time::Duration};
|
||||
|
||||
use jmap_proto::request::capability::BaseCapabilities;
|
||||
use nlp::language::Language;
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use utils::config::{Config, Rate, cron::SimpleCron, utils::ParseValue};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
|
||||
@@ -17,13 +17,6 @@ use crate::{
|
||||
ipc::{BroadcastEvent, StateEvent},
|
||||
};
|
||||
use directory::{Directory, QueryParams, Type, backend::internal::manage::ManageDirectory};
|
||||
use jmap_proto::types::{
|
||||
blob::BlobId,
|
||||
collection::{Collection, SyncCollection},
|
||||
property::Property,
|
||||
state::StateChange,
|
||||
type_state::DataType,
|
||||
};
|
||||
use mail_auth::IpLookupStrategy;
|
||||
use sieve::Sieve;
|
||||
use std::{
|
||||
@@ -31,8 +24,8 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use store::{
|
||||
BitmapKey, BlobClass, BlobStore, Deserialize, FtsStore, InMemoryStore, IndexKey, IterateParams,
|
||||
Key, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN, U64_LEN, ValueKey,
|
||||
BitmapKey, BlobStore, Deserialize, FtsStore, InMemoryStore, IndexKey, IterateParams, Key,
|
||||
LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN, U64_LEN, ValueKey,
|
||||
dispatch::DocumentSet,
|
||||
roaring::RoaringBitmap,
|
||||
write::{
|
||||
@@ -41,7 +34,13 @@ use store::{
|
||||
},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use utils::BlobHash;
|
||||
use types::{
|
||||
blob::{BlobClass, BlobId},
|
||||
blob_hash::BlobHash,
|
||||
collection::{Collection, SyncCollection},
|
||||
field::{EmailField, Field},
|
||||
type_state::{DataType, StateChange},
|
||||
};
|
||||
|
||||
impl Server {
|
||||
#[inline(always)]
|
||||
@@ -353,14 +352,14 @@ impl Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: 0,
|
||||
field: Property::Size.into(),
|
||||
field: EmailField::Size.into(),
|
||||
key: 0u32.serialize(),
|
||||
},
|
||||
IndexKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: u32::MAX,
|
||||
field: Property::Size.into(),
|
||||
field: EmailField::Size.into(),
|
||||
key: u32::MAX.serialize(),
|
||||
},
|
||||
)
|
||||
@@ -503,7 +502,7 @@ impl Server {
|
||||
account_id,
|
||||
collection: collection.into(),
|
||||
document_id,
|
||||
class: ValueClass::Property(Property::Value.into()),
|
||||
class: ValueClass::Property(Field::ARCHIVE.into()),
|
||||
})
|
||||
.await
|
||||
.add_context(|err| {
|
||||
@@ -520,9 +519,8 @@ impl Server {
|
||||
account_id: u32,
|
||||
collection: Collection,
|
||||
document_id: u32,
|
||||
property: impl AsRef<Property> + Sync + Send,
|
||||
property: Field,
|
||||
) -> trc::Result<Option<Archive<AlignedBytes>>> {
|
||||
let property = property.as_ref();
|
||||
self.core
|
||||
.storage
|
||||
.data
|
||||
@@ -563,13 +561,13 @@ impl Server {
|
||||
account_id,
|
||||
collection,
|
||||
document_id: documents.min(),
|
||||
class: ValueClass::Property(Property::Value.into()),
|
||||
class: ValueClass::Property(Field::ARCHIVE.into()),
|
||||
},
|
||||
ValueKey {
|
||||
account_id,
|
||||
collection,
|
||||
document_id: documents.max(),
|
||||
class: ValueClass::Property(Property::Value.into()),
|
||||
class: ValueClass::Property(Field::ARCHIVE.into()),
|
||||
},
|
||||
),
|
||||
|key, value| {
|
||||
@@ -654,12 +652,12 @@ impl Server {
|
||||
let mut state_change =
|
||||
StateChange::new(account_id, assigned_ids.last_change_id(account_id)?);
|
||||
for changed_collection in changed_collections.changed_containers {
|
||||
if let Some(data_type) = DataType::try_from_id(changed_collection, true) {
|
||||
if let Some(data_type) = DataType::try_from_sync(changed_collection, true) {
|
||||
state_change.set_change(data_type);
|
||||
}
|
||||
}
|
||||
for changed_collection in changed_collections.changed_items {
|
||||
if let Some(data_type) = DataType::try_from_id(changed_collection, false) {
|
||||
if let Some(data_type) = DataType::try_from_sync(changed_collection, false) {
|
||||
state_change.set_change(data_type);
|
||||
}
|
||||
}
|
||||
@@ -753,10 +751,7 @@ impl Server {
|
||||
|
||||
// Write truncation entry for cache
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(collection)
|
||||
.set(
|
||||
batch.with_account_id(account_id).set(
|
||||
ValueClass::Any(AnyClass {
|
||||
subspace: SUBSPACE_LOGS,
|
||||
key: LogKey {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use crate::Core;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use store::{
|
||||
IterateParams, U32_LEN, U64_LEN, ValueKey,
|
||||
@@ -18,9 +19,7 @@ use store::{
|
||||
},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use utils::{BLOB_HASH_LEN, BlobHash};
|
||||
|
||||
use crate::Core;
|
||||
use types::blob_hash::{BLOB_HASH_LEN, BlobHash};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DeletedBlob<H, T, C> {
|
||||
|
||||
@@ -4,24 +4,22 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use ahash::RandomState;
|
||||
use jmap_proto::types::{state::StateChange, type_state::DataType};
|
||||
use mail_auth::{
|
||||
dmarc::Dmarc,
|
||||
mta_sts::TlsRpt,
|
||||
report::{Record, tlsrpt::FailureDetails},
|
||||
};
|
||||
use store::{BlobStore, InMemoryStore, Store};
|
||||
use tokio::sync::mpsc;
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
use crate::config::smtp::{
|
||||
queue::QueueName,
|
||||
report::AggregateFrequency,
|
||||
resolver::{Policy, Tlsa},
|
||||
};
|
||||
use ahash::RandomState;
|
||||
use mail_auth::{
|
||||
dmarc::Dmarc,
|
||||
mta_sts::TlsRpt,
|
||||
report::{Record, tlsrpt::FailureDetails},
|
||||
};
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use store::{BlobStore, InMemoryStore, Store};
|
||||
use tokio::sync::mpsc;
|
||||
use types::type_state::{DataType, StateChange};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
pub enum HousekeeperEvent {
|
||||
AcmeReschedule {
|
||||
|
||||
@@ -25,7 +25,6 @@ use config::{
|
||||
telemetry::Metrics,
|
||||
};
|
||||
use ipc::{BroadcastEvent, HousekeeperEvent, QueueEvent, ReportingEvent, StateEvent};
|
||||
use jmap_proto::types::value::AclGrant;
|
||||
use listener::{asn::AsnGeoLookupData, blocked::Security, tls::AcmeProviders};
|
||||
use mail_auth::{MX, Txt};
|
||||
use manager::webadmin::{Resource, WebAdminManager};
|
||||
@@ -41,6 +40,7 @@ use std::{
|
||||
use tinyvec::TinyVec;
|
||||
use tokio::sync::{Notify, Semaphore, mpsc};
|
||||
use tokio_rustls::TlsConnector;
|
||||
use types::acl::AclGrant;
|
||||
use utils::{
|
||||
cache::{Cache, CacheItemWeight, CacheWithTtl},
|
||||
snowflake::SnowflakeIdGenerator,
|
||||
@@ -123,10 +123,6 @@ pub const KV_LOCK_HOUSEKEEPER: u8 = 24;
|
||||
pub const KV_LOCK_DAV: u8 = 25;
|
||||
pub const KV_SIEVE_ID: u8 = 26;
|
||||
|
||||
pub const IDX_UID: u8 = 0;
|
||||
pub const IDX_EMAIL: u8 = 1;
|
||||
pub const IDX_CREATED: u8 = 2;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Server {
|
||||
pub inner: Arc<Inner>,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::Core;
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
io::{BufWriter, Write},
|
||||
@@ -11,9 +13,6 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc::{self, SyncSender},
|
||||
};
|
||||
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use store::{
|
||||
BitmapKey, Deserialize, IndexKey, IterateParams, LogKey, SUBSPACE_BITMAP_ID,
|
||||
SUBSPACE_BITMAP_TAG, SUBSPACE_BITMAP_TEXT, SerializeInfallible, U32_LEN, U64_LEN, ValueKey,
|
||||
@@ -22,15 +21,17 @@ use store::{
|
||||
QueueEvent, TagValue, ValueClass, key::DeserializeBigEndian,
|
||||
},
|
||||
};
|
||||
|
||||
use types::{
|
||||
blob_hash::{BLOB_HASH_LEN, BlobHash},
|
||||
collection::Collection,
|
||||
field::{Field, MailboxField},
|
||||
};
|
||||
use utils::{
|
||||
BLOB_HASH_LEN, BlobHash, UnwrapFailure,
|
||||
UnwrapFailure,
|
||||
codec::leb128::{Leb128_, Leb128Reader},
|
||||
failed,
|
||||
};
|
||||
|
||||
use crate::Core;
|
||||
|
||||
pub(super) const MAGIC_MARKER: u8 = 123;
|
||||
pub(super) const FILE_VERSION: u8 = 2;
|
||||
|
||||
@@ -196,21 +197,21 @@ impl Core {
|
||||
|
||||
// Obtain UID counter
|
||||
if collection == u8::from(Collection::Mailbox)
|
||||
&& u8::from(Property::Value) == field
|
||||
&& u8::from(Field::ARCHIVE) == field
|
||||
{
|
||||
let value = store
|
||||
.get_counter(ValueKey {
|
||||
account_id,
|
||||
collection,
|
||||
document_id,
|
||||
class: ValueClass::Property(Property::EmailIds.into()),
|
||||
class: MailboxField::UidCounter.into(),
|
||||
})
|
||||
.await
|
||||
.failed("Failed to get counter");
|
||||
if value != 0 {
|
||||
writer
|
||||
.send(Op::KeyValue((
|
||||
vec![u8::from(Property::EmailIds)],
|
||||
vec![u8::from(MailboxField::UidCounter)],
|
||||
value.serialize(),
|
||||
)))
|
||||
.failed("Failed to send key value");
|
||||
|
||||
@@ -17,8 +17,8 @@ use store::{
|
||||
write::{BatchBuilder, ValueClass},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::semver::Semver;
|
||||
use utils::{
|
||||
Semver,
|
||||
config::{Config, ConfigKey},
|
||||
glob::GlobPattern,
|
||||
};
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::Core;
|
||||
use ahash::AHashMap;
|
||||
use std::{
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::Core;
|
||||
use ahash::AHashMap;
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use store::{
|
||||
BlobStore, Key, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN,
|
||||
roaring::RoaringBitmap,
|
||||
@@ -28,7 +26,8 @@ use tokio::{
|
||||
fs::File,
|
||||
io::{AsyncReadExt, BufReader},
|
||||
};
|
||||
use utils::{BlobHash, UnwrapFailure, failed};
|
||||
use types::{blob_hash::BlobHash, collection::Collection, field::MailboxField};
|
||||
use utils::{UnwrapFailure, failed};
|
||||
|
||||
use super::backup::{DeserializeBytes, FILE_VERSION, Family, MAGIC_MARKER, Op};
|
||||
|
||||
@@ -65,9 +64,9 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
let mut reader = OpReader::new(path).await;
|
||||
let mut account_id = u32::MAX;
|
||||
let mut document_id = u32::MAX;
|
||||
let mut collection = u8::MAX;
|
||||
let mut collection = Collection::None;
|
||||
let mut collection_raw = u8::MAX;
|
||||
let mut family = Family::None;
|
||||
let email_collection = u8::from(Collection::Email);
|
||||
let mut due = now();
|
||||
|
||||
let mut batch_size = 0;
|
||||
@@ -83,7 +82,8 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
batch.with_account_id(account_id);
|
||||
}
|
||||
Op::Collection(c) => {
|
||||
collection = c;
|
||||
collection_raw = c;
|
||||
collection = Collection::from(c);
|
||||
batch.with_collection(collection);
|
||||
}
|
||||
Op::DocumentId(d) => {
|
||||
@@ -99,8 +99,8 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
.as_slice()
|
||||
.deserialize_u8(0)
|
||||
.expect("Failed to deserialize field");
|
||||
if collection == u8::from(Collection::Mailbox)
|
||||
&& u8::from(Property::EmailIds) == field
|
||||
if collection == Collection::Mailbox
|
||||
&& u8::from(MailboxField::UidCounter) == field
|
||||
{
|
||||
batch.add(
|
||||
ValueClass::Property(field),
|
||||
@@ -145,7 +145,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
let hash = BlobHash::try_from_hash_slice(&key).expect("Invalid blob hash");
|
||||
|
||||
if account_id != u32::MAX && document_id != u32::MAX {
|
||||
if reader.version == 1 && collection == email_collection {
|
||||
if reader.version == 1 && collection == Collection::Email {
|
||||
batch.set(
|
||||
ValueClass::TaskQueue(TaskQueueClass::IndexEmail {
|
||||
due,
|
||||
@@ -301,7 +301,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
),
|
||||
},
|
||||
4 => {
|
||||
if reader.version == 1 && collection == email_collection {
|
||||
if reader.version == 1 && collection == Collection::Email {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
|
||||
subspace: SUBSPACE_LOGS,
|
||||
key: LogKey {
|
||||
account_id,
|
||||
collection,
|
||||
collection: collection_raw,
|
||||
change_id,
|
||||
}
|
||||
.serialize(0),
|
||||
|
||||
@@ -4,116 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{Server, auth::AccessToken};
|
||||
use crate::Server;
|
||||
use directory::{
|
||||
QueryParams, Type,
|
||||
backend::internal::{
|
||||
PrincipalField,
|
||||
manage::{ChangedPrincipals, ManageDirectory},
|
||||
},
|
||||
Type,
|
||||
backend::internal::{PrincipalField, manage::ChangedPrincipals},
|
||||
};
|
||||
use jmap_proto::{
|
||||
error::set::SetError,
|
||||
types::{
|
||||
acl::Acl,
|
||||
property::Property,
|
||||
value::{AclGrant, ArchivedAclGrant, MaybePatchValue, Value},
|
||||
},
|
||||
};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
use types::acl::{AclGrant, ArchivedAclGrant};
|
||||
|
||||
impl Server {
|
||||
pub async fn acl_set(
|
||||
&self,
|
||||
changes: &mut Vec<AclGrant>,
|
||||
current: Option<&[AclGrant]>,
|
||||
acl_changes: MaybePatchValue,
|
||||
) -> Result<(), SetError> {
|
||||
match acl_changes {
|
||||
MaybePatchValue::Value(Value::List(values)) => {
|
||||
*changes = self.map_acl_set(values).await?;
|
||||
}
|
||||
MaybePatchValue::Patch(patch) => {
|
||||
let (mut patch, is_update) = self.map_acl_patch(patch).await?;
|
||||
if let Some(changes_) = current {
|
||||
*changes = changes_.to_vec();
|
||||
}
|
||||
|
||||
if let Some(is_set) = is_update {
|
||||
if !patch.grants.is_empty() {
|
||||
if let Some(acl_item) = changes
|
||||
.iter_mut()
|
||||
.find(|item| item.account_id == patch.account_id)
|
||||
{
|
||||
let item = patch.grants.pop().unwrap();
|
||||
if is_set {
|
||||
acl_item.grants.insert(item);
|
||||
} else {
|
||||
acl_item.grants.remove(item);
|
||||
if acl_item.grants.is_empty() {
|
||||
changes.retain(|item| item.account_id != patch.account_id);
|
||||
}
|
||||
}
|
||||
} else if is_set {
|
||||
changes.push(patch);
|
||||
}
|
||||
}
|
||||
} else if !patch.grants.is_empty() {
|
||||
if let Some(acl_item) = changes
|
||||
.iter_mut()
|
||||
.find(|item| item.account_id == patch.account_id)
|
||||
{
|
||||
acl_item.grants = patch.grants;
|
||||
} else {
|
||||
changes.push(patch);
|
||||
}
|
||||
} else {
|
||||
changes.retain(|item| item.account_id != patch.account_id);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(SetError::invalid_properties()
|
||||
.with_property(Property::Acl)
|
||||
.with_description("Invalid ACL property."));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn acl_get(
|
||||
&self,
|
||||
value: &[AclGrant],
|
||||
access_token: &AccessToken,
|
||||
account_id: u32,
|
||||
) -> Value {
|
||||
if access_token.is_member(account_id)
|
||||
|| value.iter().any(|item| {
|
||||
access_token.is_member(item.account_id) && item.grants.contains(Acl::Administer)
|
||||
})
|
||||
{
|
||||
let mut acl_obj = jmap_proto::types::value::Object::with_capacity(value.len() / 2);
|
||||
for item in value {
|
||||
if let Some(name) = self
|
||||
.store()
|
||||
.get_principal_name(item.account_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
{
|
||||
acl_obj.append(
|
||||
Property::_T(name),
|
||||
item.grants
|
||||
.map(|acl_item| Value::Text(acl_item.to_string()))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Value::Object(acl_obj)
|
||||
} else {
|
||||
Value::Null
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn refresh_acls(&self, acl_changes: &[AclGrant], current: Option<&[AclGrant]>) {
|
||||
let mut changed_principals = ChangedPrincipals::new();
|
||||
if let Some(acl_current) = current {
|
||||
@@ -205,77 +103,4 @@ impl Server {
|
||||
|
||||
self.invalidate_principal_caches(changed_principals).await;
|
||||
}
|
||||
|
||||
pub async fn map_acl_set(&self, acl_set: Vec<Value>) -> Result<Vec<AclGrant>, SetError> {
|
||||
let mut acls = Vec::with_capacity(acl_set.len() / 2);
|
||||
for item in acl_set.chunks_exact(2) {
|
||||
if let (Value::Text(account_name), Value::UnsignedInt(grants)) = (&item[0], &item[1]) {
|
||||
match self
|
||||
.core
|
||||
.storage
|
||||
.directory
|
||||
.query(QueryParams::name(account_name).with_return_member_of(false))
|
||||
.await
|
||||
{
|
||||
Ok(Some(principal)) => {
|
||||
acls.push(AclGrant {
|
||||
account_id: principal.id(),
|
||||
grants: Bitmap::from(*grants),
|
||||
});
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(SetError::invalid_properties()
|
||||
.with_property(Property::Acl)
|
||||
.with_description(format!("Account {account_name} does not exist.")));
|
||||
}
|
||||
_ => {
|
||||
return Err(SetError::forbidden()
|
||||
.with_property(Property::Acl)
|
||||
.with_description("Temporary server failure during lookup"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(SetError::invalid_properties()
|
||||
.with_property(Property::Acl)
|
||||
.with_description("Invalid ACL value found."));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(acls)
|
||||
}
|
||||
|
||||
pub async fn map_acl_patch(
|
||||
&self,
|
||||
acl_patch: Vec<Value>,
|
||||
) -> Result<(AclGrant, Option<bool>), SetError> {
|
||||
if let (Value::Text(account_name), Value::UnsignedInt(grants)) =
|
||||
(&acl_patch[0], &acl_patch[1])
|
||||
{
|
||||
match self
|
||||
.core
|
||||
.storage
|
||||
.directory
|
||||
.query(QueryParams::name(account_name).with_return_member_of(false))
|
||||
.await
|
||||
{
|
||||
Ok(Some(principal)) => Ok((
|
||||
AclGrant {
|
||||
account_id: principal.id(),
|
||||
grants: Bitmap::from(*grants),
|
||||
},
|
||||
acl_patch.get(2).map(|v| v.as_bool().unwrap_or(false)),
|
||||
)),
|
||||
Ok(None) => Err(SetError::invalid_properties()
|
||||
.with_property(Property::Acl)
|
||||
.with_description(format!("Account {account_name} does not exist."))),
|
||||
_ => Err(SetError::forbidden()
|
||||
.with_property(Property::Acl)
|
||||
.with_description("Temporary server failure during lookup")),
|
||||
}
|
||||
} else {
|
||||
Err(SetError::invalid_properties()
|
||||
.with_property(Property::Acl)
|
||||
.with_description("Invalid ACL value found."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use jmap_proto::types::{acl::Acl, collection::Collection};
|
||||
use crate::{Server, auth::AccessToken};
|
||||
use store::{ValueKey, query::acl::AclQuery, roaring::RoaringBitmap, write::ValueClass};
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::Collection};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
use crate::{Server, auth::AccessToken};
|
||||
|
||||
impl Server {
|
||||
pub async fn shared_containers(
|
||||
&self,
|
||||
|
||||
@@ -5,11 +5,8 @@
|
||||
*/
|
||||
|
||||
use crate::auth::AccessToken;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
value::{AclGrant, ArchivedAclGrant},
|
||||
};
|
||||
use rkyv::vec::ArchivedVec;
|
||||
use types::acl::{Acl, AclGrant, ArchivedAclGrant};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
pub mod acl;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
use crate::{DavResources, auth::AccessToken};
|
||||
use jmap_proto::types::acl::Acl;
|
||||
use store::roaring::RoaringBitmap;
|
||||
use types::acl::Acl;
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
impl DavResources {
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use jmap_proto::types::blob::BlobSection;
|
||||
use crate::Server;
|
||||
use mail_parser::{
|
||||
Encoding,
|
||||
decoders::{base64::base64_decode, quoted_printable::quoted_printable_decode},
|
||||
};
|
||||
use utils::BlobHash;
|
||||
|
||||
use crate::Server;
|
||||
use types::{blob::BlobSection, blob_hash::BlobHash};
|
||||
|
||||
impl Server {
|
||||
pub async fn get_blob_section(
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::auth::AsTenantId;
|
||||
use ahash::AHashSet;
|
||||
use jmap_proto::types::{property::Property, value::AclGrant};
|
||||
use rkyv::{
|
||||
option::ArchivedOption,
|
||||
primitive::{ArchivedU32, ArchivedU64},
|
||||
@@ -16,22 +16,20 @@ use store::{
|
||||
Serialize, SerializeInfallible,
|
||||
write::{Archive, Archiver, BatchBuilder, BlobOp, DirectoryClass, IntoOperations, TagValue},
|
||||
};
|
||||
use utils::BlobHash;
|
||||
|
||||
use crate::auth::AsTenantId;
|
||||
use types::{acl::AclGrant, blob_hash::BlobHash, collection::SyncCollection, field::Field};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum IndexValue<'x> {
|
||||
Index {
|
||||
field: u8,
|
||||
field: Field,
|
||||
value: IndexItem<'x>,
|
||||
},
|
||||
IndexList {
|
||||
field: u8,
|
||||
field: Field,
|
||||
value: Vec<IndexItem<'x>>,
|
||||
},
|
||||
Tag {
|
||||
field: u8,
|
||||
field: Field,
|
||||
value: Vec<TagValue>,
|
||||
},
|
||||
Blob {
|
||||
@@ -41,14 +39,14 @@ pub enum IndexValue<'x> {
|
||||
used: u32,
|
||||
},
|
||||
LogContainer {
|
||||
sync_collection: u8,
|
||||
sync_collection: SyncCollection,
|
||||
},
|
||||
LogContainerProperty {
|
||||
sync_collection: u8,
|
||||
sync_collection: SyncCollection,
|
||||
ids: Vec<u32>,
|
||||
},
|
||||
LogItem {
|
||||
sync_collection: u8,
|
||||
sync_collection: SyncCollection,
|
||||
prefix: Option<u32>,
|
||||
},
|
||||
Acl {
|
||||
@@ -297,14 +295,14 @@ impl<C: IndexableObject, N: IndexableAndSerializableObject> IntoOperations
|
||||
}
|
||||
if N::is_versioned() {
|
||||
let (offset, bytes) = Archiver::new(changes).serialize_versioned()?;
|
||||
batch.set_versioned(Property::Value, bytes, offset);
|
||||
batch.set_versioned(Field::ARCHIVE, bytes, offset);
|
||||
} else {
|
||||
batch.set(Property::Value, Archiver::new(changes).serialize()?);
|
||||
batch.set(Field::ARCHIVE, Archiver::new(changes).serialize()?);
|
||||
}
|
||||
}
|
||||
(Some(current), Some(changes)) => {
|
||||
// Update
|
||||
batch.assert_value(Property::Value, ¤t);
|
||||
batch.assert_value(Field::ARCHIVE, ¤t);
|
||||
for (current, change) in current.inner.index_values().zip(changes.index_values()) {
|
||||
if current != change {
|
||||
merge_index(batch, current, change, self.tenant_id)?;
|
||||
@@ -325,19 +323,19 @@ impl<C: IndexableObject, N: IndexableAndSerializableObject> IntoOperations
|
||||
}
|
||||
if N::is_versioned() {
|
||||
let (offset, bytes) = Archiver::new(changes).serialize_versioned()?;
|
||||
batch.set_versioned(Property::Value, bytes, offset);
|
||||
batch.set_versioned(Field::ARCHIVE, bytes, offset);
|
||||
} else {
|
||||
batch.set(Property::Value, Archiver::new(changes).serialize()?);
|
||||
batch.set(Field::ARCHIVE, Archiver::new(changes).serialize()?);
|
||||
}
|
||||
}
|
||||
(Some(current), None) => {
|
||||
// Deletion
|
||||
batch.assert_value(Property::Value, ¤t);
|
||||
batch.assert_value(Field::ARCHIVE, ¤t);
|
||||
for item in current.inner.index_values() {
|
||||
build_index(batch, item, self.tenant_id, false);
|
||||
}
|
||||
|
||||
batch.clear(Property::Value);
|
||||
batch.clear(Field::ARCHIVE);
|
||||
}
|
||||
(None, None) => unreachable!(),
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use jmap_proto::types::{state::StateChange, type_state::DataType};
|
||||
use tokio::sync::mpsc;
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
use crate::{IPC_CHANNEL_BUFFER, Server, ipc::StateEvent};
|
||||
use tokio::sync::mpsc;
|
||||
use types::type_state::{DataType, StateChange};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
impl Server {
|
||||
pub async fn subscribe_state_manager(
|
||||
|
||||
@@ -12,7 +12,7 @@ utils = { path = "../utils" }
|
||||
groupware = { path = "../groupware" }
|
||||
directory = { path = "../directory" }
|
||||
http_proto = { path = "../http-proto" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
types = { path = "../types" }
|
||||
trc = { path = "../trc" }
|
||||
calcard = { version = "0.1.3", features = ["rkyv"] }
|
||||
hashify = { version = "0.2" }
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
};
|
||||
use calcard::common::timezone::Tz;
|
||||
use common::{DavName, Server, auth::AccessToken};
|
||||
use dav_proto::{Depth, RequestHeaders};
|
||||
@@ -14,23 +23,12 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection, VanishedCollection},
|
||||
};
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
};
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
|
||||
pub(crate) trait CalendarCopyMoveRequestHandler: Sync + Send {
|
||||
fn handle_calendar_copy_move_request(
|
||||
|
||||
@@ -22,12 +22,12 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
pub(crate) trait CalendarDeleteRequestHandler: Sync + Send {
|
||||
fn handle_calendar_delete_request(
|
||||
|
||||
@@ -24,16 +24,16 @@ use dav_proto::{
|
||||
use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
write::{now, serialize::rkyv_deserialize},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CalendarFreebusyRequestHandler: Sync + Send {
|
||||
fn handle_calendar_freebusy_request(
|
||||
|
||||
@@ -4,17 +4,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
|
||||
use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
@@ -23,6 +12,16 @@ use crate::{
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
|
||||
use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CalendarGetRequestHandler: Sync + Send {
|
||||
fn handle_calendar_get_request(
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::proppatch::CalendarPropPatchRequestHandler;
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{
|
||||
RequestHeaders, Return,
|
||||
@@ -15,20 +24,9 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::{Collection, SyncCollection};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
|
||||
use super::proppatch::CalendarPropPatchRequestHandler;
|
||||
use types::collection::{Collection, SyncCollection};
|
||||
|
||||
pub(crate) trait CalendarMkColRequestHandler: Sync + Send {
|
||||
fn handle_calendar_mkcol_request(
|
||||
|
||||
@@ -15,16 +15,15 @@ pub mod scheduling;
|
||||
pub mod update;
|
||||
|
||||
use crate::{DavError, DavErrorCondition};
|
||||
use common::IDX_UID;
|
||||
use common::{DavResources, Server};
|
||||
use dav_proto::schema::{
|
||||
property::{CalDavProperty, CalendarData, DavProperty, WebDavProperty},
|
||||
response::CalCondition,
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::query::Filter;
|
||||
use trc::AddContext;
|
||||
use types::{collection::Collection, field::CalendarField};
|
||||
|
||||
pub(crate) static CALENDAR_CONTAINER_PROPS: [DavProperty; 31] = [
|
||||
DavProperty::WebDav(WebDavProperty::CreationDate),
|
||||
@@ -101,7 +100,7 @@ pub(crate) async fn assert_is_unique_uid(
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::CalendarEvent,
|
||||
vec![Filter::eq(IDX_UID, uid.as_bytes().to_vec())],
|
||||
vec![Filter::eq(CalendarField::Uid, uid.as_bytes().to_vec())],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
@@ -29,13 +29,13 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CalendarPropPatchRequestHandler: Sync + Send {
|
||||
fn handle_calendar_proppatch_request(
|
||||
|
||||
@@ -34,13 +34,13 @@ use dav_proto::{
|
||||
use groupware::{cache::GroupwareCache, calendar::ArchivedCalendarEvent};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
|
||||
use std::{fmt::Write, slice::Iter, str::FromStr};
|
||||
use store::{
|
||||
ahash::{AHashMap, AHashSet},
|
||||
write::serialize::rkyv_deserialize,
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::SyncCollection};
|
||||
|
||||
pub(crate) trait CalendarQueryRequestHandler: Sync + Send {
|
||||
fn handle_calendar_query_request(
|
||||
|
||||
@@ -32,9 +32,9 @@ use dav_proto::{
|
||||
use groupware::{DestroyArchive, cache::GroupwareCache, calendar::CalendarScheduling};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::{Collection, SyncCollection};
|
||||
use store::{ahash::AHashMap, write::BatchBuilder};
|
||||
use trc::AddContext;
|
||||
use types::collection::{Collection, SyncCollection};
|
||||
use utils::sanitize_email;
|
||||
|
||||
pub(crate) trait CalendarSchedulingHandler: Sync + Send {
|
||||
|
||||
@@ -33,13 +33,13 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CalendarUpdateRequestHandler: Sync + Send {
|
||||
fn handle_calendar_update_request(
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
};
|
||||
use common::{DavName, Server, auth::AccessToken};
|
||||
use dav_proto::{Depth, RequestHeaders};
|
||||
use groupware::{
|
||||
@@ -13,23 +22,12 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection, VanishedCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
};
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
|
||||
pub(crate) trait CardCopyMoveRequestHandler: Sync + Send {
|
||||
fn handle_card_copy_move_request(
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
ETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken, sharing::EffectiveAcl};
|
||||
use dav_proto::RequestHeaders;
|
||||
use groupware::{
|
||||
@@ -13,20 +21,11 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
ETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CardDeleteRequestHandler: Sync + Send {
|
||||
|
||||
@@ -4,17 +4,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
|
||||
use groupware::{cache::GroupwareCache, contact::ContactCard};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
common::{
|
||||
@@ -23,6 +12,16 @@ use crate::{
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
|
||||
use groupware::{cache::GroupwareCache, contact::ContactCard};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CardGetRequestHandler: Sync + Send {
|
||||
fn handle_card_get_request(
|
||||
|
||||
@@ -21,9 +21,9 @@ use dav_proto::{
|
||||
use groupware::{cache::GroupwareCache, contact::AddressBook};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::{Collection, SyncCollection};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::collection::{Collection, SyncCollection};
|
||||
|
||||
pub(crate) trait CardMkColRequestHandler: Sync + Send {
|
||||
fn handle_card_mkcol_request(
|
||||
|
||||
@@ -4,18 +4,16 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::IDX_UID;
|
||||
use crate::{DavError, DavErrorCondition};
|
||||
use common::{DavResources, Server};
|
||||
use dav_proto::schema::{
|
||||
property::{CardDavProperty, DavProperty, WebDavProperty},
|
||||
response::CardCondition,
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::query::Filter;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{DavError, DavErrorCondition};
|
||||
use types::{collection::Collection, field::ContactField};
|
||||
|
||||
pub mod copy_move;
|
||||
pub mod delete;
|
||||
@@ -87,7 +85,7 @@ pub(crate) async fn assert_is_unique_uid(
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::ContactCard,
|
||||
vec![Filter::eq(IDX_UID, uid.as_bytes().to_vec())],
|
||||
vec![Filter::eq(ContactField::Uid, uid.as_bytes().to_vec())],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ETag, ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{
|
||||
RequestHeaders, Return,
|
||||
@@ -20,20 +28,11 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ETag, ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CardPropPatchRequestHandler: Sync + Send {
|
||||
|
||||
@@ -28,9 +28,9 @@ use dav_proto::{
|
||||
use groupware::cache::GroupwareCache;
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
|
||||
use std::fmt::Write;
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::SyncCollection};
|
||||
|
||||
pub(crate) trait CardQueryRequestHandler: Sync + Send {
|
||||
fn handle_card_query_request(
|
||||
|
||||
@@ -4,22 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use calcard::{Entry, Parser};
|
||||
use common::{DavName, Server, auth::AccessToken};
|
||||
use dav_proto::{
|
||||
RequestHeaders, Return,
|
||||
schema::{property::Rfc1123DateTime, response::CardCondition},
|
||||
};
|
||||
use groupware::{cache::GroupwareCache, contact::ContactCard};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
use crate::{
|
||||
DavError, DavErrorCondition, DavMethod,
|
||||
common::{
|
||||
@@ -30,8 +15,21 @@ use crate::{
|
||||
file::DavFileResource,
|
||||
fix_percent_encoding,
|
||||
};
|
||||
|
||||
use super::assert_is_unique_uid;
|
||||
use calcard::{Entry, Parser};
|
||||
use common::{DavName, Server, auth::AccessToken};
|
||||
use dav_proto::{
|
||||
RequestHeaders, Return,
|
||||
schema::{property::Rfc1123DateTime, response::CardCondition},
|
||||
};
|
||||
use groupware::{cache::GroupwareCache, contact::ContactCard};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait CardUpdateRequestHandler: Sync + Send {
|
||||
fn handle_card_update_request(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::ArchivedResource;
|
||||
use crate::{
|
||||
DavError, DavErrorCondition, DavResourceName, common::uri::DavUriResource,
|
||||
principal::propfind::PrincipalPropFind,
|
||||
@@ -22,18 +23,15 @@ use groupware::RFC_3986;
|
||||
use groupware::{cache::GroupwareCache, calendar::Calendar, contact::AddressBook, file::FileNode};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::Collection,
|
||||
value::{AclGrant, ArchivedAclGrant},
|
||||
};
|
||||
use rkyv::vec::ArchivedVec;
|
||||
use store::{ahash::AHashSet, roaring::RoaringBitmap, write::BatchBuilder};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::{Acl, AclGrant, ArchivedAclGrant},
|
||||
collection::Collection,
|
||||
};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
use super::ArchivedResource;
|
||||
|
||||
pub(crate) trait DavAclHandler: Sync + Send {
|
||||
fn handle_acl_request(
|
||||
&self,
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::ETag;
|
||||
use super::uri::{DavUriResource, OwnedUri, UriResource, Urn};
|
||||
use crate::{DavError, DavErrorCondition, DavMethod};
|
||||
use common::KV_LOCK_DAV;
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::schema::property::{ActiveLock, LockScope, WebDavProperty};
|
||||
@@ -11,21 +14,16 @@ use dav_proto::schema::request::{DavPropertyValue, DeadProperty};
|
||||
use dav_proto::schema::response::{BaseCondition, List, PropResponse};
|
||||
use dav_proto::{Condition, Depth, Timeout};
|
||||
use dav_proto::{RequestHeaders, schema::request::LockInfo};
|
||||
|
||||
use groupware::cache::GroupwareCache;
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use std::collections::HashMap;
|
||||
use store::dispatch::lookup::KeyValue;
|
||||
use store::write::serialize::rkyv_deserialize;
|
||||
use store::write::{AlignedBytes, Archive, Archiver, now};
|
||||
use store::{Serialize, U32_LEN};
|
||||
use trc::AddContext;
|
||||
|
||||
use super::ETag;
|
||||
use super::uri::{DavUriResource, OwnedUri, UriResource, Urn};
|
||||
use crate::{DavError, DavErrorCondition, DavMethod};
|
||||
use types::collection::Collection;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ResourceState<'x> {
|
||||
|
||||
@@ -27,10 +27,10 @@ use groupware::{
|
||||
contact::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard},
|
||||
file::{ArchivedFileNode, FileNode},
|
||||
};
|
||||
use jmap_proto::types::{collection::Collection, property::Property, value::ArchivedAclGrant};
|
||||
use propfind::PropFindItem;
|
||||
use rkyv::vec::ArchivedVec;
|
||||
use store::write::{AlignedBytes, Archive, BatchBuilder, Operation, ValueClass, ValueOp};
|
||||
use types::{acl::ArchivedAclGrant, collection::Collection, field::Field};
|
||||
use uri::{OwnedUri, Urn};
|
||||
|
||||
pub mod acl;
|
||||
@@ -109,7 +109,7 @@ impl<T> ETag for Archive<T> {
|
||||
|
||||
impl ExtractETag for BatchBuilder {
|
||||
fn etag(&self) -> Option<String> {
|
||||
let p_value = u8::from(Property::Value);
|
||||
let p_value = u8::from(Field::ARCHIVE);
|
||||
for op in self.ops().iter().rev() {
|
||||
match op {
|
||||
Operation::Value {
|
||||
|
||||
@@ -56,10 +56,6 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
@@ -68,6 +64,10 @@ use store::{
|
||||
write::{AlignedBytes, Archive},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait PropFindRequestHandler: Sync + Send {
|
||||
fn handle_propfind_request(
|
||||
@@ -1182,7 +1182,7 @@ async fn get(
|
||||
SyncType::From { id, seq } => {
|
||||
let changes = server
|
||||
.store()
|
||||
.changes(account_id, sync_collection, Query::Since(id))
|
||||
.changes(account_id, sync_collection.into(), Query::Since(id))
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let mut vanished: Vec<String> = Vec::new();
|
||||
@@ -1258,7 +1258,7 @@ async fn get(
|
||||
{
|
||||
vanished = server
|
||||
.store()
|
||||
.vanished(account_id, vanished_collection, Query::Since(id))
|
||||
.vanished(account_id, vanished_collection.into(), Query::Since(id))
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
total_changes += vanished.len();
|
||||
|
||||
@@ -4,19 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{DavError, DavResourceName};
|
||||
use common::{Server, auth::AccessToken};
|
||||
|
||||
use directory::backend::internal::manage::ManageDirectory;
|
||||
|
||||
use groupware::cache::GroupwareCache;
|
||||
use http_proto::request::decode_path_element;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use std::fmt::Display;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{DavError, DavResourceName};
|
||||
use types::collection::Collection;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UriResource<A, R> {
|
||||
|
||||
@@ -21,16 +21,16 @@ use dav_proto::{Depth, RequestHeaders};
|
||||
use groupware::{DestroyArchive, cache::GroupwareCache, file::FileNode};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection, VanishedCollection},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
write::{BatchBuilder, now},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection, VanishedCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait FileCopyMoveRequestHandler: Sync + Send {
|
||||
fn handle_file_copy_move_request(
|
||||
|
||||
@@ -16,8 +16,8 @@ use dav_proto::RequestHeaders;
|
||||
use groupware::{DestroyArchive, cache::GroupwareCache};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::SyncCollection};
|
||||
|
||||
pub(crate) trait FileDeleteRequestHandler: Sync + Send {
|
||||
fn handle_file_delete_request(
|
||||
|
||||
@@ -9,11 +9,11 @@ use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
|
||||
use groupware::{cache::GroupwareCache, file::FileNode};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod,
|
||||
|
||||
@@ -23,12 +23,12 @@ use dav_proto::{
|
||||
use groupware::{cache::GroupwareCache, file::FileNode};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
|
||||
pub(crate) trait FileMkColRequestHandler: Sync + Send {
|
||||
fn handle_file_mkcol_request(
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ETag, ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
};
|
||||
use common::{Server, auth::AccessToken, sharing::EffectiveAcl};
|
||||
use dav_proto::{
|
||||
RequestHeaders, Return,
|
||||
@@ -16,21 +25,11 @@ use dav_proto::{
|
||||
use groupware::{cache::GroupwareCache, file::FileNode};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::{
|
||||
DavError, DavMethod, PropStatBuilder,
|
||||
common::{
|
||||
ETag, ExtractETag,
|
||||
lock::{LockRequestHandler, ResourceState},
|
||||
uri::DavUriResource,
|
||||
},
|
||||
file::DavFileResource,
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait FilePropPatchRequestHandler: Sync + Send {
|
||||
|
||||
@@ -24,13 +24,13 @@ use groupware::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::{
|
||||
acl::Acl,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use store::write::{BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
use utils::BlobHash;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
blob_hash::BlobHash,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
|
||||
pub(crate) trait FileUpdateRequestHandler: Sync + Send {
|
||||
fn handle_file_update_request(
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::propfind::PrincipalPropFind;
|
||||
use crate::{
|
||||
DavError,
|
||||
common::{
|
||||
DavQuery, DavQueryResource,
|
||||
propfind::PropFindRequestHandler,
|
||||
uri::{DavUriResource, UriResource},
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::{
|
||||
RequestHeaders,
|
||||
@@ -15,19 +24,8 @@ use dav_proto::{
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::roaring::RoaringBitmap;
|
||||
|
||||
use crate::{
|
||||
DavError,
|
||||
common::{
|
||||
DavQuery, DavQueryResource,
|
||||
propfind::PropFindRequestHandler,
|
||||
uri::{DavUriResource, UriResource},
|
||||
},
|
||||
};
|
||||
|
||||
use super::propfind::PrincipalPropFind;
|
||||
use types::collection::Collection;
|
||||
|
||||
pub(crate) trait PrincipalMatching: Sync + Send {
|
||||
fn handle_principal_match(
|
||||
|
||||
@@ -23,9 +23,9 @@ use directory::{QueryParams, Type, backend::internal::manage::ManageDirectory};
|
||||
use groupware::RFC_3986;
|
||||
use groupware::cache::GroupwareCache;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use std::borrow::Cow;
|
||||
use trc::AddContext;
|
||||
use types::collection::Collection;
|
||||
|
||||
pub(crate) trait PrincipalPropFind: Sync + Send {
|
||||
fn prepare_principal_propfind_response(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::propfind::PrincipalPropFind;
|
||||
use common::{
|
||||
Server,
|
||||
auth::{AccessToken, AsTenantId},
|
||||
@@ -16,11 +17,9 @@ use dav_proto::schema::{
|
||||
use directory::{Type, backend::internal::manage::ManageDirectory};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::roaring::RoaringBitmap;
|
||||
use trc::AddContext;
|
||||
|
||||
use super::propfind::PrincipalPropFind;
|
||||
use types::collection::Collection;
|
||||
|
||||
pub(crate) trait PrincipalPropSearch: Sync + Send {
|
||||
fn handle_principal_property_search(
|
||||
|
||||
@@ -50,9 +50,9 @@ use dav_proto::{
|
||||
use directory::Permission;
|
||||
use http_proto::{HttpRequest, HttpResponse, HttpSessionData, request::fetch_body};
|
||||
use hyper::{StatusCode, header};
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use trc::{EventType, LimitEvent, StoreEvent, WebDavEvent};
|
||||
use types::collection::Collection;
|
||||
|
||||
pub trait DavRequestHandler: Sync + Send {
|
||||
fn handle_dav_request(
|
||||
|
||||
@@ -10,7 +10,7 @@ proc_macros = { path = "../utils/proc-macros" }
|
||||
store = { path = "../store" }
|
||||
trc = { path = "../trc" }
|
||||
nlp = { path = "../nlp" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
types = { path = "../types" }
|
||||
smtp-proto = { version = "0.2" }
|
||||
mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] }
|
||||
mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::{
|
||||
};
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use compact_str::CompactString;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use nlp::tokenizers::word::WordTokenizer;
|
||||
use store::{
|
||||
Deserialize, IterateParams, Serialize, SerializeInfallible, Store, U32_LEN, ValueKey,
|
||||
@@ -27,6 +26,7 @@ use store::{
|
||||
},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::collection::Collection;
|
||||
use utils::sanitize_email;
|
||||
|
||||
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
@@ -9,7 +9,7 @@ utils = { path = "../utils" }
|
||||
nlp = { path = "../nlp" }
|
||||
store = { path = "../store" }
|
||||
trc = { path = "../trc" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
types = { path = "../types" }
|
||||
common = { path = "../common" }
|
||||
directory = { path = "../directory" }
|
||||
groupware = { path = "../groupware" }
|
||||
|
||||
6
crates/email/src/cache/email.rs
vendored
6
crates/email/src/cache/email.rs
vendored
@@ -9,13 +9,13 @@ use common::{
|
||||
MessageCache, MessageStoreCache, MessageUidCache, MessagesCache, Server, auth::AccessToken,
|
||||
sharing::EffectiveAcl,
|
||||
};
|
||||
use jmap_proto::types::{
|
||||
use store::{ahash::AHashMap, roaring::RoaringBitmap, write::Archive};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
collection::Collection,
|
||||
keyword::{Keyword, OTHER},
|
||||
};
|
||||
use store::{ahash::AHashMap, roaring::RoaringBitmap, write::Archive};
|
||||
use trc::AddContext;
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
pub(crate) async fn update_email_cache(
|
||||
|
||||
5
crates/email/src/cache/mailbox.rs
vendored
5
crates/email/src/cache/mailbox.rs
vendored
@@ -9,9 +9,12 @@ use common::{
|
||||
MailboxCache, MailboxesCache, MessageStoreCache, Server, auth::AccessToken,
|
||||
config::jmap::settings::SpecialUse, sharing::EffectiveAcl,
|
||||
};
|
||||
use jmap_proto::types::{acl::Acl, collection::Collection, value::AclGrant};
|
||||
use store::{ahash::AHashMap, roaring::RoaringBitmap};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::{Acl, AclGrant},
|
||||
collection::Collection,
|
||||
};
|
||||
use utils::{map::bitmap::Bitmap, topological::TopologicalSort};
|
||||
|
||||
pub(crate) async fn update_mailbox_cache(
|
||||
|
||||
9
crates/email/src/cache/mod.rs
vendored
9
crates/email/src/cache/mod.rs
vendored
@@ -4,18 +4,17 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::{collections::hash_map::Entry, sync::Arc, time::Instant};
|
||||
|
||||
use common::{CacheSwap, MessageStoreCache, Server};
|
||||
use email::{full_email_cache_build, update_email_cache};
|
||||
use jmap_proto::types::collection::SyncCollection;
|
||||
use mailbox::{full_mailbox_cache_build, update_mailbox_cache};
|
||||
use std::{collections::hash_map::Entry, sync::Arc, time::Instant};
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
query::log::{Change, Query},
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use trc::{AddContext, StoreEvent};
|
||||
use types::collection::SyncCollection;
|
||||
|
||||
pub mod email;
|
||||
pub mod mailbox;
|
||||
@@ -70,7 +69,7 @@ impl MessageCacheFetch for Server {
|
||||
.data
|
||||
.changes(
|
||||
account_id,
|
||||
SyncCollection::Email,
|
||||
SyncCollection::Email.into(),
|
||||
Query::Since(cache.last_change_id),
|
||||
)
|
||||
.await
|
||||
@@ -203,7 +202,7 @@ async fn full_cache_build(
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.get_last_change_id(account_id, SyncCollection::Email)
|
||||
.get_last_change_id(account_id, SyncCollection::Email.into())
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -4,15 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use jmap_proto::types::collection::SyncCollection;
|
||||
|
||||
use super::{ArchivedIdentity, Identity};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use types::collection::SyncCollection;
|
||||
|
||||
impl IndexableObject for Identity {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Identity.into(),
|
||||
sync_collection: SyncCollection::Identity,
|
||||
prefix: None,
|
||||
}]
|
||||
.into_iter()
|
||||
@@ -22,7 +21,7 @@ impl IndexableObject for Identity {
|
||||
impl IndexableObject for &ArchivedIdentity {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Identity.into(),
|
||||
sync_collection: SyncCollection::Identity,
|
||||
prefix: None,
|
||||
}]
|
||||
.into_iter()
|
||||
|
||||
@@ -13,12 +13,9 @@ use common::{
|
||||
Server, auth::AccessToken, sharing::EffectiveAcl, storage::index::ObjectIndexBuilder,
|
||||
};
|
||||
use directory::Permission;
|
||||
use jmap_proto::{
|
||||
error::set::{SetError, SetErrorType},
|
||||
types::{acl::Acl, collection::Collection, property::Property},
|
||||
};
|
||||
use store::{roaring::RoaringBitmap, write::BatchBuilder};
|
||||
use trc::AddContext;
|
||||
use types::{acl::Acl, collection::Collection, field::MailboxField};
|
||||
|
||||
pub trait MailboxDestroy: Sync + Send {
|
||||
fn mailbox_destroy(
|
||||
@@ -27,7 +24,16 @@ pub trait MailboxDestroy: Sync + Send {
|
||||
document_id: u32,
|
||||
access_token: &AccessToken,
|
||||
remove_emails: bool,
|
||||
) -> impl Future<Output = trc::Result<Result<Option<u64>, SetError>>> + Send;
|
||||
) -> impl Future<Output = trc::Result<Result<Option<u64>, MailboxDestroyError>>> + Send;
|
||||
}
|
||||
|
||||
pub enum MailboxDestroyError {
|
||||
CannotDestroy,
|
||||
Forbidden,
|
||||
HasChildren,
|
||||
HasEmails,
|
||||
NotFound,
|
||||
AssertionFailed,
|
||||
}
|
||||
|
||||
impl MailboxDestroy for Server {
|
||||
@@ -37,24 +43,20 @@ impl MailboxDestroy for Server {
|
||||
document_id: u32,
|
||||
access_token: &AccessToken,
|
||||
remove_emails: bool,
|
||||
) -> trc::Result<Result<Option<u64>, SetError>> {
|
||||
) -> trc::Result<Result<Option<u64>, MailboxDestroyError>> {
|
||||
// Internal folders cannot be deleted
|
||||
#[cfg(feature = "test_mode")]
|
||||
if [INBOX_ID, TRASH_ID].contains(&document_id)
|
||||
&& !access_token.has_permission(Permission::DeleteSystemFolders)
|
||||
{
|
||||
return Ok(Err(SetError::forbidden().with_description(
|
||||
"You are not allowed to delete Inbox, Junk or Trash folders.",
|
||||
)));
|
||||
return Ok(Err(MailboxDestroyError::CannotDestroy));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "test_mode"))]
|
||||
if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id)
|
||||
&& !access_token.has_permission(Permission::DeleteSystemFolders)
|
||||
{
|
||||
return Ok(Err(SetError::forbidden().with_description(
|
||||
"You are not allowed to delete Inbox, Junk or Trash folders.",
|
||||
)));
|
||||
return Ok(Err(MailboxDestroyError::CannotDestroy));
|
||||
}
|
||||
|
||||
// Verify that this mailbox does not have sub-mailboxes
|
||||
@@ -68,8 +70,7 @@ impl MailboxDestroy for Server {
|
||||
.iter()
|
||||
.any(|item| item.parent_id == document_id)
|
||||
{
|
||||
return Ok(Err(SetError::new(SetErrorType::MailboxHasChild)
|
||||
.with_description("Mailbox has at least one children.")));
|
||||
return Ok(Err(MailboxDestroyError::HasChildren));
|
||||
}
|
||||
|
||||
// Verify that the mailbox is empty
|
||||
@@ -142,8 +143,7 @@ impl MailboxDestroy for Server {
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
return Ok(Err(SetError::new(SetErrorType::MailboxHasEmail)
|
||||
.with_description("Mailbox is not empty.")));
|
||||
return Ok(Err(MailboxDestroyError::HasEmails));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,26 +159,22 @@ impl MailboxDestroy for Server {
|
||||
// Validate ACLs
|
||||
if access_token.is_shared(account_id) {
|
||||
let acl = mailbox.inner.acls.effective_acl(access_token);
|
||||
if !acl.contains(Acl::Administer) {
|
||||
if !acl.contains(Acl::Delete) {
|
||||
return Ok(Err(SetError::forbidden()
|
||||
.with_description("You are not allowed to delete this mailbox.")));
|
||||
} else if remove_emails && !acl.contains(Acl::RemoveItems) {
|
||||
return Ok(Err(SetError::forbidden().with_description(
|
||||
"You are not allowed to delete emails from this mailbox.",
|
||||
)));
|
||||
}
|
||||
if !acl.contains(Acl::Administer)
|
||||
&& (!acl.contains(Acl::Delete)
|
||||
|| (remove_emails && !acl.contains(Acl::RemoveItems)))
|
||||
{
|
||||
return Ok(Err(MailboxDestroyError::Forbidden));
|
||||
}
|
||||
}
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::Mailbox)
|
||||
.delete_document(document_id)
|
||||
.clear(Property::EmailIds)
|
||||
.clear(MailboxField::UidCounter)
|
||||
.custom(ObjectIndexBuilder::<_, ()>::new().with_current(mailbox))
|
||||
.caused_by(trc::location!())?;
|
||||
} else {
|
||||
return Ok(Err(SetError::not_found()));
|
||||
return Ok(Err(MailboxDestroyError::NotFound));
|
||||
};
|
||||
|
||||
if !batch.is_empty() {
|
||||
@@ -188,11 +184,9 @@ impl MailboxDestroy for Server {
|
||||
.and_then(|ids| ids.last_change_id(account_id))
|
||||
{
|
||||
Ok(change_id) => Ok(Ok(Some(change_id))),
|
||||
Err(err) if err.is_assertion_failure() => Ok(Err(SetError::forbidden()
|
||||
.with_description(concat!(
|
||||
"Another process modified a message in this mailbox ",
|
||||
"while deleting it, please try again."
|
||||
)))),
|
||||
Err(err) if err.is_assertion_failure() => {
|
||||
Ok(Err(MailboxDestroyError::AssertionFailed))
|
||||
}
|
||||
Err(err) => Err(err.caused_by(trc::location!())),
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
|
||||
|
||||
use super::{ArchivedMailbox, Mailbox};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use types::{acl::AclGrant, collection::SyncCollection};
|
||||
|
||||
impl IndexableObject for Mailbox {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
},
|
||||
IndexValue::Acl {
|
||||
value: (&self.acls).into(),
|
||||
@@ -27,7 +26,7 @@ impl IndexableObject for &ArchivedMailbox {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
},
|
||||
IndexValue::Acl {
|
||||
value: self
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
use super::*;
|
||||
use crate::cache::MessageCacheFetch;
|
||||
use common::{Server, config::jmap::settings::SpecialUse, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use std::future::Future;
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
use types::collection::Collection;
|
||||
|
||||
pub trait MailboxFnc: Sync + Send {
|
||||
fn create_system_folders(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
use common::config::jmap::settings::SpecialUse;
|
||||
use jmap_proto::types::value::AclGrant;
|
||||
use types::acl::AclGrant;
|
||||
|
||||
pub mod destroy;
|
||||
pub mod index;
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
use super::metadata::MessageMetadata;
|
||||
use common::Server;
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use mail_parser::Message;
|
||||
use spam_filter::{
|
||||
SpamFilterInput, analysis::init::SpamFilterInit, modules::bayes::BayesClassifier,
|
||||
};
|
||||
use std::future::Future;
|
||||
use store::write::{TaskQueueClass, now};
|
||||
use trc::StoreEvent;
|
||||
use utils::BlobHash;
|
||||
|
||||
use super::metadata::MessageMetadata;
|
||||
use types::{blob_hash::BlobHash, collection::Collection, field::EmailField};
|
||||
|
||||
pub trait EmailBayesTrain: Sync + Send {
|
||||
fn email_bayes_train(
|
||||
@@ -63,7 +60,7 @@ impl EmailBayesTrain for Server {
|
||||
account_id,
|
||||
Collection::Email,
|
||||
document_id,
|
||||
Property::BodyStructure,
|
||||
EmailField::Metadata.into(),
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
|
||||
@@ -11,23 +11,20 @@ use super::{
|
||||
};
|
||||
use crate::mailbox::UidMailbox;
|
||||
use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::{
|
||||
error::set::SetError,
|
||||
types::{
|
||||
blob::BlobId,
|
||||
collection::{Collection, SyncCollection},
|
||||
date::UTCDate,
|
||||
id::Id,
|
||||
keyword::Keyword,
|
||||
property::Property,
|
||||
},
|
||||
};
|
||||
use mail_parser::{HeaderName, HeaderValue, parsers::fields::thread::thread_name};
|
||||
use store::{
|
||||
BlobClass,
|
||||
write::{BatchBuilder, TaskQueueClass, ValueClass, now},
|
||||
};
|
||||
use store::write::{BatchBuilder, TaskQueueClass, ValueClass, now};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
blob::{BlobClass, BlobId},
|
||||
collection::{Collection, SyncCollection},
|
||||
field::EmailField,
|
||||
keyword::Keyword,
|
||||
};
|
||||
|
||||
pub enum CopyMessageError {
|
||||
NotFound,
|
||||
OverQuota,
|
||||
}
|
||||
|
||||
pub trait EmailCopy: Sync + Send {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -38,9 +35,9 @@ pub trait EmailCopy: Sync + Send {
|
||||
resource_token: &ResourceToken,
|
||||
mailboxes: Vec<u32>,
|
||||
keywords: Vec<Keyword>,
|
||||
received_at: Option<UTCDate>,
|
||||
received_at: Option<u64>,
|
||||
session_id: u64,
|
||||
) -> impl Future<Output = trc::Result<Result<IngestedEmail, SetError>>> + Send;
|
||||
) -> impl Future<Output = trc::Result<Result<IngestedEmail, CopyMessageError>>> + Send;
|
||||
}
|
||||
|
||||
impl EmailCopy for Server {
|
||||
@@ -52,9 +49,9 @@ impl EmailCopy for Server {
|
||||
resource_token: &ResourceToken,
|
||||
mailboxes: Vec<u32>,
|
||||
keywords: Vec<Keyword>,
|
||||
received_at: Option<UTCDate>,
|
||||
received_at: Option<u64>,
|
||||
session_id: u64,
|
||||
) -> trc::Result<Result<IngestedEmail, SetError>> {
|
||||
) -> trc::Result<Result<IngestedEmail, CopyMessageError>> {
|
||||
// Obtain metadata
|
||||
let account_id = resource_token.account_id;
|
||||
let mut metadata = if let Some(metadata) = self
|
||||
@@ -62,7 +59,7 @@ impl EmailCopy for Server {
|
||||
from_account_id,
|
||||
Collection::Email,
|
||||
from_message_id,
|
||||
Property::BodyStructure,
|
||||
EmailField::Metadata.into(),
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -70,10 +67,7 @@ impl EmailCopy for Server {
|
||||
.deserialize::<MessageMetadata>()
|
||||
.caused_by(trc::location!())?
|
||||
} else {
|
||||
return Ok(Err(SetError::not_found().with_description(format!(
|
||||
"Message not found not found in account {}.",
|
||||
Id::from(from_account_id)
|
||||
))));
|
||||
return Ok(Err(CopyMessageError::NotFound));
|
||||
};
|
||||
|
||||
// Check quota
|
||||
@@ -87,7 +81,7 @@ impl EmailCopy for Server {
|
||||
|| err.matches(trc::EventType::Limit(trc::LimitEvent::TenantQuota))
|
||||
{
|
||||
trc::error!(err.account_id(account_id).span_id(session_id));
|
||||
return Ok(Err(SetError::over_quota()));
|
||||
return Ok(Err(CopyMessageError::OverQuota));
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
@@ -96,7 +90,7 @@ impl EmailCopy for Server {
|
||||
|
||||
// Set receivedAt
|
||||
if let Some(received_at) = received_at {
|
||||
metadata.received_at = received_at.timestamp() as u64;
|
||||
metadata.received_at = received_at;
|
||||
}
|
||||
|
||||
// Obtain threadId
|
||||
@@ -226,7 +220,8 @@ impl EmailCopy for Server {
|
||||
self.notify_task_queue();
|
||||
|
||||
// Update response
|
||||
email.id = Id::from_parts(thread_id, document_id);
|
||||
email.document_id = document_id;
|
||||
email.thread_id = thread_id;
|
||||
email.change_id = change_id;
|
||||
email.blob_id = BlobId::new(
|
||||
blob_hash,
|
||||
|
||||
@@ -8,8 +8,6 @@ use super::metadata::MessageData;
|
||||
use crate::{cache::MessageCacheFetch, mailbox::*, message::metadata::MessageMetadata};
|
||||
use common::{KV_LOCK_PURGE_ACCOUNT, Server, storage::index::ObjectIndexBuilder};
|
||||
use groupware::calendar::storage::ItipAutoExpunge;
|
||||
use jmap_proto::types::collection::VanishedCollection;
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use std::future::Future;
|
||||
use store::rand::prelude::SliceRandom;
|
||||
use store::write::key::DeserializeBigEndian;
|
||||
@@ -21,7 +19,10 @@ use store::{
|
||||
};
|
||||
use store::{IndexKey, IterateParams, SerializeInfallible, U32_LEN};
|
||||
use trc::AddContext;
|
||||
use utils::BlobHash;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use types::blob_hash::BlobHash;
|
||||
use types::collection::{Collection, VanishedCollection};
|
||||
use types::field::EmailField;
|
||||
|
||||
pub trait EmailDeletion: Sync + Send {
|
||||
fn emails_tombstone(
|
||||
@@ -78,7 +79,7 @@ impl EmailDeletion for Server {
|
||||
.update_document(document_id)
|
||||
.custom(ObjectIndexBuilder::<_, ()>::new().with_current(metadata))
|
||||
.caused_by(trc::location!())?
|
||||
.tag(Property::MailboxIds, TagValue::Id(TOMBSTONE_ID))
|
||||
.tag(EmailField::MailboxIds, TagValue::Id(TOMBSTONE_ID))
|
||||
.commit_point();
|
||||
|
||||
deleted_ids.insert(document_id);
|
||||
@@ -211,14 +212,14 @@ impl EmailDeletion for Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: 0,
|
||||
field: Property::ReceivedAt.into(),
|
||||
field: EmailField::ReceivedAt.into(),
|
||||
key: 0u64.serialize(),
|
||||
},
|
||||
IndexKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: u32::MAX,
|
||||
field: Property::ReceivedAt.into(),
|
||||
field: EmailField::ReceivedAt.into(),
|
||||
key: now().saturating_sub(hold_period).serialize(),
|
||||
},
|
||||
)
|
||||
@@ -269,7 +270,7 @@ impl EmailDeletion for Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
class: BitmapClass::Tag {
|
||||
field: Property::MailboxIds.into(),
|
||||
field: EmailField::MailboxIds.into(),
|
||||
value: TagValue::Id(TOMBSTONE_ID),
|
||||
},
|
||||
document_id: 0,
|
||||
@@ -291,7 +292,7 @@ impl EmailDeletion for Server {
|
||||
self.core
|
||||
.storage
|
||||
.fts
|
||||
.remove(account_id, Collection::Email.into(), &tombstoned_ids)
|
||||
.remove(account_id, Collection::Email, &tombstoned_ids)
|
||||
.await?;
|
||||
|
||||
// Obtain tenant id
|
||||
@@ -310,8 +311,8 @@ impl EmailDeletion for Server {
|
||||
batch
|
||||
.with_collection(Collection::Email)
|
||||
.delete_document(document_id)
|
||||
.clear(Property::Value)
|
||||
.untag(Property::MailboxIds, TagValue::Id(TOMBSTONE_ID));
|
||||
.clear(EmailField::Archive)
|
||||
.untag(EmailField::MailboxIds, TagValue::Id(TOMBSTONE_ID));
|
||||
|
||||
// Remove message metadata
|
||||
if let Some(metadata_) = self
|
||||
@@ -322,7 +323,7 @@ impl EmailDeletion for Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id,
|
||||
class: ValueClass::Property(Property::BodyStructure.into()),
|
||||
class: ValueClass::Property(EmailField::Metadata.into()),
|
||||
})
|
||||
.await?
|
||||
{
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::ingest::{EmailIngest, IngestEmail, IngestSource};
|
||||
use crate::{mailbox::INBOX_ID, sieve::ingest::SieveScriptIngest};
|
||||
use common::Server;
|
||||
|
||||
use directory::Permission;
|
||||
use jmap_proto::types::{state::StateChange, type_state::DataType};
|
||||
use mail_parser::MessageParser;
|
||||
use std::{borrow::Cow, future::Future};
|
||||
use store::ahash::AHashMap;
|
||||
use utils::BlobHash;
|
||||
|
||||
use crate::{mailbox::INBOX_ID, sieve::ingest::SieveScriptIngest};
|
||||
|
||||
use super::ingest::{EmailIngest, IngestEmail, IngestSource};
|
||||
use types::{
|
||||
blob_hash::BlobHash,
|
||||
type_state::{DataType, StateChange},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IngestMessage {
|
||||
|
||||
@@ -12,7 +12,6 @@ use super::metadata::{
|
||||
MessageMetadata, MessageMetadataPart,
|
||||
};
|
||||
use common::storage::index::{IndexValue, IndexableObject, ObjectIndexBuilder};
|
||||
use jmap_proto::types::{collection::SyncCollection, property::Property};
|
||||
use mail_parser::{
|
||||
Addr, Address, ArchivedAddress, ArchivedHeaderName, ArchivedHeaderValue, Group, HeaderName,
|
||||
HeaderValue,
|
||||
@@ -29,7 +28,7 @@ use store::{
|
||||
write::{Archiver, BatchBuilder, BlobOp, DirectoryClass},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use utils::BlobHash;
|
||||
use types::{blob_hash::BlobHash, collection::SyncCollection, field::EmailField};
|
||||
|
||||
pub const MAX_MESSAGE_PARTS: usize = 1000;
|
||||
pub const MAX_ID_LENGTH: usize = 100;
|
||||
@@ -53,14 +52,14 @@ impl MessageMetadata {
|
||||
if set {
|
||||
// Serialize metadata
|
||||
batch
|
||||
.index(Property::Size, self.size.serialize())
|
||||
.index(Property::ReceivedAt, (self.received_at).serialize());
|
||||
.index(EmailField::Size, self.size.serialize())
|
||||
.index(EmailField::ReceivedAt, (self.received_at).serialize());
|
||||
} else {
|
||||
// Delete metadata
|
||||
batch
|
||||
.clear(Property::BodyStructure)
|
||||
.unindex(Property::Size, self.size.serialize())
|
||||
.unindex(Property::ReceivedAt, (self.received_at).serialize());
|
||||
.clear(EmailField::Metadata)
|
||||
.unindex(EmailField::Size, self.size.serialize())
|
||||
.unindex(EmailField::ReceivedAt, (self.received_at).serialize());
|
||||
}
|
||||
|
||||
// Index properties
|
||||
@@ -76,9 +75,9 @@ impl MessageMetadata {
|
||||
|
||||
if self.has_attachments {
|
||||
if set {
|
||||
batch.tag(Property::HasAttachment, ());
|
||||
batch.tag(EmailField::HasAttachment, ());
|
||||
} else {
|
||||
batch.untag(Property::HasAttachment, ());
|
||||
batch.untag(EmailField::HasAttachment, ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +99,7 @@ impl MessageMetadata {
|
||||
}
|
||||
|
||||
if set {
|
||||
batch.set(Property::BodyStructure, Archiver::new(self).serialize()?);
|
||||
batch.set(EmailField::Metadata, Archiver::new(self).serialize()?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -119,9 +118,9 @@ impl MessageMetadata {
|
||||
// Add ids to inverted index
|
||||
if id.len() < MAX_ID_LENGTH {
|
||||
if set {
|
||||
batch.index(Property::References, encode_message_id(id));
|
||||
batch.index(EmailField::References, encode_message_id(id));
|
||||
} else {
|
||||
batch.unindex(Property::References, encode_message_id(id));
|
||||
batch.unindex(EmailField::References, encode_message_id(id));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -131,9 +130,9 @@ impl MessageMetadata {
|
||||
// Add ids to inverted index
|
||||
if id.len() < MAX_ID_LENGTH {
|
||||
if set {
|
||||
batch.index(Property::References, id.serialize());
|
||||
batch.index(EmailField::References, id.serialize());
|
||||
} else {
|
||||
batch.unindex(Property::References, id.serialize());
|
||||
batch.unindex(EmailField::References, id.serialize());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -161,9 +160,9 @@ impl MessageMetadata {
|
||||
|
||||
// Add address to inverted index
|
||||
if set {
|
||||
batch.index(u8::from(&property), sort_text.build());
|
||||
batch.index(property, sort_text.build());
|
||||
} else {
|
||||
batch.unindex(u8::from(&property), sort_text.build());
|
||||
batch.unindex(property, sort_text.build());
|
||||
}
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
}
|
||||
@@ -173,9 +172,9 @@ impl MessageMetadata {
|
||||
if let HeaderValue::DateTime(datetime) = &header.value {
|
||||
let value = (datetime.to_timestamp() as u64).serialize();
|
||||
if set {
|
||||
batch.index(Property::SentAt, value);
|
||||
batch.index(EmailField::SentAt, value);
|
||||
} else {
|
||||
batch.unindex(Property::SentAt, value);
|
||||
batch.unindex(EmailField::SentAt, value);
|
||||
}
|
||||
}
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
@@ -202,9 +201,9 @@ impl MessageMetadata {
|
||||
.serialize();
|
||||
|
||||
if set {
|
||||
batch.index(Property::Subject, thread_name);
|
||||
batch.index(EmailField::Subject, thread_name);
|
||||
} else {
|
||||
batch.unindex(Property::Subject, thread_name);
|
||||
batch.unindex(EmailField::Subject, thread_name);
|
||||
}
|
||||
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
@@ -218,9 +217,9 @@ impl MessageMetadata {
|
||||
// Add subject to index if missing
|
||||
if !seen_headers[HeaderName::Subject.id() as usize] {
|
||||
if set {
|
||||
batch.index(Property::Subject, "!".serialize());
|
||||
batch.index(EmailField::Subject, "!".serialize());
|
||||
} else {
|
||||
batch.unindex(Property::Subject, "!".serialize());
|
||||
batch.unindex(EmailField::Subject, "!".serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,18 +248,18 @@ impl ArchivedMessageMetadata {
|
||||
if set {
|
||||
// Serialize metadata
|
||||
batch
|
||||
.index(Property::Size, u32::from(self.size).serialize())
|
||||
.index(EmailField::Size, u32::from(self.size).serialize())
|
||||
.index(
|
||||
Property::ReceivedAt,
|
||||
EmailField::ReceivedAt,
|
||||
u64::from(self.received_at).serialize(),
|
||||
);
|
||||
} else {
|
||||
// Delete metadata
|
||||
batch
|
||||
.clear(Property::BodyStructure)
|
||||
.unindex(Property::Size, u32::from(self.size).serialize())
|
||||
.clear(EmailField::Metadata)
|
||||
.unindex(EmailField::Size, u32::from(self.size).serialize())
|
||||
.unindex(
|
||||
Property::ReceivedAt,
|
||||
EmailField::ReceivedAt,
|
||||
u64::from(self.received_at).serialize(),
|
||||
);
|
||||
}
|
||||
@@ -278,9 +277,9 @@ impl ArchivedMessageMetadata {
|
||||
|
||||
if self.has_attachments {
|
||||
if set {
|
||||
batch.tag(Property::HasAttachment, ());
|
||||
batch.tag(EmailField::HasAttachment, ());
|
||||
} else {
|
||||
batch.untag(Property::HasAttachment, ());
|
||||
batch.untag(EmailField::HasAttachment, ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,9 +310,9 @@ impl ArchivedMessageMetadata {
|
||||
// Add ids to inverted index
|
||||
if id.len() < MAX_ID_LENGTH {
|
||||
if set {
|
||||
batch.index(Property::References, encode_message_id(id));
|
||||
batch.index(EmailField::References, encode_message_id(id));
|
||||
} else {
|
||||
batch.unindex(Property::References, encode_message_id(id));
|
||||
batch.unindex(EmailField::References, encode_message_id(id));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -325,9 +324,9 @@ impl ArchivedMessageMetadata {
|
||||
// Add ids to inverted index
|
||||
if id.len() < MAX_ID_LENGTH {
|
||||
if set {
|
||||
batch.index(Property::References, id.serialize());
|
||||
batch.index(EmailField::References, id.serialize());
|
||||
} else {
|
||||
batch.unindex(Property::References, id.serialize());
|
||||
batch.unindex(EmailField::References, id.serialize());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -358,9 +357,9 @@ impl ArchivedMessageMetadata {
|
||||
|
||||
// Add address to inverted index
|
||||
if set {
|
||||
batch.index(u8::from(&property), sort_text.build());
|
||||
batch.index(property, sort_text.build());
|
||||
} else {
|
||||
batch.unindex(u8::from(&property), sort_text.build());
|
||||
batch.unindex(property, sort_text.build());
|
||||
}
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
}
|
||||
@@ -372,9 +371,9 @@ impl ArchivedMessageMetadata {
|
||||
as u64)
|
||||
.serialize();
|
||||
if set {
|
||||
batch.index(Property::SentAt, value);
|
||||
batch.index(EmailField::SentAt, value);
|
||||
} else {
|
||||
batch.unindex(Property::SentAt, value);
|
||||
batch.unindex(EmailField::SentAt, value);
|
||||
}
|
||||
}
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
@@ -401,9 +400,9 @@ impl ArchivedMessageMetadata {
|
||||
.serialize();
|
||||
|
||||
if set {
|
||||
batch.index(Property::Subject, thread_name);
|
||||
batch.index(EmailField::Subject, thread_name);
|
||||
} else {
|
||||
batch.unindex(Property::Subject, thread_name);
|
||||
batch.unindex(EmailField::Subject, thread_name);
|
||||
}
|
||||
|
||||
seen_headers[header.name.id() as usize] = true;
|
||||
@@ -417,9 +416,9 @@ impl ArchivedMessageMetadata {
|
||||
// Add subject to index if missing
|
||||
if !seen_headers[HeaderName::Subject.id() as usize] {
|
||||
if set {
|
||||
batch.index(Property::Subject, "!".serialize());
|
||||
batch.index(EmailField::Subject, "!".serialize());
|
||||
} else {
|
||||
batch.unindex(Property::Subject, "!".serialize());
|
||||
batch.unindex(EmailField::Subject, "!".serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +464,7 @@ impl IndexMessage for BatchBuilder {
|
||||
) -> trc::Result<&mut Self> {
|
||||
// Index size
|
||||
self.index(
|
||||
Property::Size,
|
||||
EmailField::Size,
|
||||
(message.raw_message.len() as u32).serialize(),
|
||||
)
|
||||
.add(
|
||||
@@ -480,7 +479,7 @@ impl IndexMessage for BatchBuilder {
|
||||
}
|
||||
|
||||
// Index receivedAt
|
||||
self.index(Property::ReceivedAt, received_at.serialize());
|
||||
self.index(EmailField::ReceivedAt, received_at.serialize());
|
||||
|
||||
let mut has_attachments = false;
|
||||
let mut preview = None;
|
||||
@@ -549,7 +548,7 @@ impl IndexMessage for BatchBuilder {
|
||||
|
||||
// Store and index hasAttachment property
|
||||
if has_attachments {
|
||||
self.tag(Property::HasAttachment, ());
|
||||
self.tag(EmailField::HasAttachment, ());
|
||||
}
|
||||
|
||||
// Link blob
|
||||
@@ -566,7 +565,7 @@ impl IndexMessage for BatchBuilder {
|
||||
|
||||
// Store message metadata
|
||||
self.set(
|
||||
Property::BodyStructure,
|
||||
EmailField::Metadata,
|
||||
Archiver::new(metadata)
|
||||
.serialize()
|
||||
.caused_by(trc::location!())?,
|
||||
@@ -580,15 +579,15 @@ impl IndexableObject for MessageData {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
prefix: self.thread_id.into(),
|
||||
},
|
||||
IndexValue::LogContainerProperty {
|
||||
sync_collection: SyncCollection::Thread.into(),
|
||||
sync_collection: SyncCollection::Thread,
|
||||
ids: vec![self.thread_id],
|
||||
},
|
||||
IndexValue::LogContainerProperty {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
ids: self.mailboxes.iter().map(|m| m.mailbox_id).collect(),
|
||||
},
|
||||
]
|
||||
@@ -600,15 +599,15 @@ impl IndexableObject for &ArchivedMessageData {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
prefix: self.thread_id.to_native().into(),
|
||||
},
|
||||
IndexValue::LogContainerProperty {
|
||||
sync_collection: SyncCollection::Thread.into(),
|
||||
sync_collection: SyncCollection::Thread,
|
||||
ids: vec![self.thread_id.to_native()],
|
||||
},
|
||||
IndexValue::LogContainerProperty {
|
||||
sync_collection: SyncCollection::Email.into(),
|
||||
sync_collection: SyncCollection::Email,
|
||||
ids: self
|
||||
.mailboxes
|
||||
.iter()
|
||||
@@ -1037,38 +1036,38 @@ impl<T: TrimTextValue> TrimTextValue for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn property_from_header(header: &HeaderName) -> Property {
|
||||
pub fn property_from_header(header: &HeaderName) -> EmailField {
|
||||
match header {
|
||||
HeaderName::Subject => Property::Subject,
|
||||
HeaderName::From => Property::From,
|
||||
HeaderName::To => Property::To,
|
||||
HeaderName::Cc => Property::Cc,
|
||||
HeaderName::Date => Property::SentAt,
|
||||
HeaderName::Bcc => Property::Bcc,
|
||||
HeaderName::ReplyTo => Property::ReplyTo,
|
||||
HeaderName::Sender => Property::Sender,
|
||||
HeaderName::InReplyTo => Property::InReplyTo,
|
||||
HeaderName::MessageId => Property::MessageId,
|
||||
HeaderName::References => Property::References,
|
||||
HeaderName::ResentMessageId => Property::EmailIds,
|
||||
HeaderName::Subject => EmailField::Subject,
|
||||
HeaderName::From => EmailField::From,
|
||||
HeaderName::To => EmailField::To,
|
||||
HeaderName::Cc => EmailField::Cc,
|
||||
HeaderName::Date => EmailField::SentAt,
|
||||
HeaderName::Bcc => EmailField::Bcc,
|
||||
HeaderName::ReplyTo => EmailField::ReplyTo,
|
||||
HeaderName::Sender => EmailField::Sender,
|
||||
HeaderName::InReplyTo => EmailField::InReplyTo,
|
||||
HeaderName::MessageId => EmailField::MessageId,
|
||||
HeaderName::References => EmailField::References,
|
||||
HeaderName::ResentMessageId => EmailField::EmailIds,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn property_from_archived_header(header: &ArchivedHeaderName) -> Property {
|
||||
pub fn property_from_archived_header(header: &ArchivedHeaderName) -> EmailField {
|
||||
match header {
|
||||
ArchivedHeaderName::Subject => Property::Subject,
|
||||
ArchivedHeaderName::From => Property::From,
|
||||
ArchivedHeaderName::To => Property::To,
|
||||
ArchivedHeaderName::Cc => Property::Cc,
|
||||
ArchivedHeaderName::Date => Property::SentAt,
|
||||
ArchivedHeaderName::Bcc => Property::Bcc,
|
||||
ArchivedHeaderName::ReplyTo => Property::ReplyTo,
|
||||
ArchivedHeaderName::Sender => Property::Sender,
|
||||
ArchivedHeaderName::InReplyTo => Property::InReplyTo,
|
||||
ArchivedHeaderName::MessageId => Property::MessageId,
|
||||
ArchivedHeaderName::References => Property::References,
|
||||
ArchivedHeaderName::ResentMessageId => Property::EmailIds,
|
||||
ArchivedHeaderName::Subject => EmailField::Subject,
|
||||
ArchivedHeaderName::From => EmailField::From,
|
||||
ArchivedHeaderName::To => EmailField::To,
|
||||
ArchivedHeaderName::Cc => EmailField::Cc,
|
||||
ArchivedHeaderName::Date => EmailField::SentAt,
|
||||
ArchivedHeaderName::Bcc => EmailField::Bcc,
|
||||
ArchivedHeaderName::ReplyTo => EmailField::ReplyTo,
|
||||
ArchivedHeaderName::Sender => EmailField::Sender,
|
||||
ArchivedHeaderName::InReplyTo => EmailField::InReplyTo,
|
||||
ArchivedHeaderName::MessageId => EmailField::MessageId,
|
||||
ArchivedHeaderName::References => EmailField::References,
|
||||
ArchivedHeaderName::ResentMessageId => EmailField::EmailIds,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,12 @@ use crate::{
|
||||
metadata::MessageData,
|
||||
},
|
||||
};
|
||||
use common::{IDX_EMAIL, Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use directory::Permission;
|
||||
use groupware::{
|
||||
calendar::itip::{ItipIngest, ItipIngestError},
|
||||
scheduling::{ItipError, ItipMessages},
|
||||
};
|
||||
use jmap_proto::types::{
|
||||
blob::BlobId,
|
||||
collection::{Collection, SyncCollection},
|
||||
id::Id,
|
||||
keyword::Keyword,
|
||||
property::Property,
|
||||
value::{Object, Value},
|
||||
};
|
||||
use mail_parser::{
|
||||
Header, HeaderName, HeaderValue, Message, MessageParser, MimeHeaders, PartType,
|
||||
parsers::fields::thread::thread_name,
|
||||
@@ -45,7 +37,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use store::{
|
||||
BlobClass, IndexKey, IndexKeyPrefix, IterateParams, U32_LEN,
|
||||
IndexKey, IndexKeyPrefix, IterateParams, U32_LEN,
|
||||
ahash::AHashMap,
|
||||
query::Filter,
|
||||
roaring::RoaringBitmap,
|
||||
@@ -53,11 +45,18 @@ use store::{
|
||||
};
|
||||
use store::{SerializeInfallible, rand::Rng};
|
||||
use trc::{AddContext, MessageIngestEvent};
|
||||
use types::{
|
||||
blob::{BlobClass, BlobId},
|
||||
collection::{Collection, SyncCollection},
|
||||
field::{ContactField, EmailField, MailboxField, PrincipalField},
|
||||
keyword::Keyword,
|
||||
};
|
||||
use utils::sanitize_email;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IngestedEmail {
|
||||
pub id: Id,
|
||||
pub document_id: u32,
|
||||
pub thread_id: u32,
|
||||
pub change_id: u64,
|
||||
pub blob_id: BlobId,
|
||||
pub size: usize,
|
||||
@@ -219,7 +218,7 @@ impl EmailIngest for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::ContactCard,
|
||||
vec![Filter::eq(IDX_EMAIL, sender.into_bytes())],
|
||||
vec![Filter::eq(ContactField::Email, sender.into_bytes())],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
@@ -473,7 +472,8 @@ impl EmailIngest for Server {
|
||||
);
|
||||
|
||||
return Ok(IngestedEmail {
|
||||
id: Id::default(),
|
||||
document_id: 0,
|
||||
thread_id: 0,
|
||||
change_id: u64::MAX,
|
||||
blob_id: BlobId::default(),
|
||||
imap_uids: Vec::new(),
|
||||
@@ -543,7 +543,12 @@ impl EmailIngest for Server {
|
||||
if do_encrypt
|
||||
&& !message.is_encrypted()
|
||||
&& let Some(encrypt_params_) = self
|
||||
.get_archive_by_property(account_id, Collection::Principal, 0, Property::Parameters)
|
||||
.get_archive_by_property(
|
||||
account_id,
|
||||
Collection::Principal,
|
||||
0,
|
||||
PrincipalField::EncryptionKeys.into(),
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
{
|
||||
@@ -682,7 +687,6 @@ impl EmailIngest for Server {
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.last_change_id(account_id)?;
|
||||
let id = Id::from_parts(thread_id, document_id);
|
||||
|
||||
// Request FTS index
|
||||
self.notify_task_queue();
|
||||
@@ -710,7 +714,8 @@ impl EmailIngest for Server {
|
||||
);
|
||||
|
||||
Ok(IngestedEmail {
|
||||
id,
|
||||
document_id,
|
||||
thread_id,
|
||||
change_id,
|
||||
blob_id: BlobId {
|
||||
hash: blob_id.hash,
|
||||
@@ -758,14 +763,14 @@ impl EmailIngest for Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: 0,
|
||||
field: Property::Subject.into(),
|
||||
field: EmailField::Subject.into(),
|
||||
key: thread_name.clone(),
|
||||
},
|
||||
IndexKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: u32::MAX,
|
||||
field: Property::Subject.into(),
|
||||
field: EmailField::Subject.into(),
|
||||
key: thread_name.clone(),
|
||||
},
|
||||
)
|
||||
@@ -802,14 +807,14 @@ impl EmailIngest for Server {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: 0,
|
||||
field: Property::References.into(),
|
||||
field: EmailField::References.into(),
|
||||
key: references.first().unwrap().to_vec(),
|
||||
},
|
||||
IndexKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: u32::MAX,
|
||||
field: Property::References.into(),
|
||||
field: EmailField::References.into(),
|
||||
key: references.last().unwrap().to_vec(),
|
||||
},
|
||||
)
|
||||
@@ -952,7 +957,7 @@ impl EmailIngest for Server {
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::Mailbox)
|
||||
.update_document(mailbox_id)
|
||||
.add_and_get(Property::EmailIds, 1);
|
||||
.add_and_get(MailboxField::UidCounter, 1);
|
||||
self.core
|
||||
.storage
|
||||
.data
|
||||
@@ -973,13 +978,3 @@ impl IngestSource<'_> {
|
||||
matches!(self, Self::Smtp { .. })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IngestedEmail> for Object<Value> {
|
||||
fn from(email: IngestedEmail) -> Self {
|
||||
Object::with_capacity(3)
|
||||
.with_property(Property::Id, email.id)
|
||||
.with_property(Property::ThreadId, Id::from(email.id.prefix_id()))
|
||||
.with_property(Property::BlobId, email.blob_id)
|
||||
.with_property(Property::Size, email.size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
use crate::mailbox::{ArchivedUidMailbox, UidMailbox};
|
||||
use common::storage::index::IndexableAndSerializableObject;
|
||||
use jmap_proto::types::keyword::{ArchivedKeyword, Keyword};
|
||||
use mail_parser::{
|
||||
ArchivedContentType, ArchivedEncoding, ArchivedHeaderName, ArchivedHeaderValue, DateTime,
|
||||
Encoding, Header, HeaderName, HeaderValue, PartType,
|
||||
@@ -21,7 +20,10 @@ use rkyv::{
|
||||
vec::ArchivedVec,
|
||||
};
|
||||
use std::{borrow::Cow, collections::VecDeque};
|
||||
use utils::BlobHash;
|
||||
use types::{
|
||||
blob_hash::BlobHash,
|
||||
keyword::{ArchivedKeyword, Keyword},
|
||||
};
|
||||
|
||||
#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Default)]
|
||||
pub struct MessageData {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use jmap_proto::types::type_state::DataType;
|
||||
use types::type_state::DataType;
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
#[derive(
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
use common::{Server, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use store::{query::Filter, write::BatchBuilder};
|
||||
use trc::AddContext;
|
||||
use types::{collection::Collection, field::SieveField};
|
||||
|
||||
use super::SieveScript;
|
||||
|
||||
@@ -32,7 +32,7 @@ impl SieveScriptActivate for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::SieveScript,
|
||||
vec![Filter::eq(Property::IsActive, vec![1u8])],
|
||||
vec![Filter::eq(SieveField::IsActive, vec![1u8])],
|
||||
)
|
||||
.await?
|
||||
.results;
|
||||
@@ -65,7 +65,7 @@ impl SieveScriptActivate for Server {
|
||||
new_sieve.is_active = false;
|
||||
batch
|
||||
.update_document(document_id)
|
||||
.clear(Property::EmailIds)
|
||||
.clear(SieveField::Ids)
|
||||
.custom(
|
||||
ObjectIndexBuilder::new()
|
||||
.with_changes(new_sieve)
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::SieveScript;
|
||||
use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use store::write::BatchBuilder;
|
||||
use trc::AddContext;
|
||||
|
||||
use super::SieveScript;
|
||||
use types::{collection::Collection, field::SieveField};
|
||||
|
||||
pub trait SieveScriptDelete: Sync + Send {
|
||||
fn sieve_script_delete(
|
||||
@@ -54,7 +53,7 @@ impl SieveScriptDelete for Server {
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::SieveScript)
|
||||
.delete_document(document_id)
|
||||
.clear(Property::EmailIds)
|
||||
.clear(SieveField::Ids)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<_, ()>::new()
|
||||
.with_current(obj)
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use jmap_proto::types::{collection::SyncCollection, property::Property};
|
||||
|
||||
use super::{ArchivedSieveScript, SieveScript};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use types::{collection::SyncCollection, field::SieveField};
|
||||
|
||||
impl IndexableObject for SieveScript {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: Property::Name.into(),
|
||||
field: SieveField::Name.into(),
|
||||
value: self.name.as_str().to_lowercase().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::IsActive.into(),
|
||||
field: SieveField::IsActive.into(),
|
||||
value: if self.is_active { &[1u8] } else { &[0u8] }
|
||||
.as_slice()
|
||||
.into(),
|
||||
@@ -26,7 +25,7 @@ impl IndexableObject for SieveScript {
|
||||
value: self.blob_hash.clone(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::SieveScript.into(),
|
||||
sync_collection: SyncCollection::SieveScript,
|
||||
prefix: None,
|
||||
},
|
||||
IndexValue::Quota { used: self.size },
|
||||
@@ -45,11 +44,11 @@ impl IndexableObject for &ArchivedSieveScript {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: Property::Name.into(),
|
||||
field: SieveField::Name.into(),
|
||||
value: self.name.to_lowercase().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::IsActive.into(),
|
||||
field: SieveField::IsActive.into(),
|
||||
value: if self.is_active { &[1u8] } else { &[0u8] }
|
||||
.as_slice()
|
||||
.into(),
|
||||
@@ -58,7 +57,7 @@ impl IndexableObject for &ArchivedSieveScript {
|
||||
value: (&self.blob_hash).into(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::SieveScript.into(),
|
||||
sync_collection: SyncCollection::SieveScript,
|
||||
prefix: None,
|
||||
},
|
||||
IndexValue::Quota {
|
||||
|
||||
@@ -17,7 +17,6 @@ use common::{
|
||||
Server, auth::AccessToken, config::jmap::settings::SpecialUse, scripts::plugins::PluginContext,
|
||||
};
|
||||
use directory::{Permission, QueryParams};
|
||||
use jmap_proto::types::{collection::Collection, id::Id, keyword::Keyword, property::Property};
|
||||
use mail_parser::MessageParser;
|
||||
use sieve::{Envelope, Event, Input, Mailbox, Recipient, Sieve};
|
||||
use std::future::Future;
|
||||
@@ -30,6 +29,7 @@ use store::{
|
||||
write::{AlignedBytes, Archive, ArchiveVersion, Archiver, BatchBuilder, BlobOp},
|
||||
};
|
||||
use trc::{AddContext, SieveEvent};
|
||||
use types::{collection::Collection, field::SieveField, id::Id, keyword::Keyword};
|
||||
use utils::config::utils::ParseValue;
|
||||
|
||||
struct SieveMessage<'x> {
|
||||
@@ -142,7 +142,8 @@ impl SieveScriptIngest for Server {
|
||||
did_file_into: false,
|
||||
}];
|
||||
let mut ingested_message = IngestedEmail {
|
||||
id: Id::default(),
|
||||
document_id: 0,
|
||||
thread_id: 0,
|
||||
change_id: u64::MAX,
|
||||
blob_id: Default::default(),
|
||||
size: raw_message.len(),
|
||||
@@ -560,7 +561,7 @@ impl SieveScriptIngest for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::SieveScript,
|
||||
vec![Filter::eq(Property::IsActive, vec![1u8])],
|
||||
vec![Filter::eq(SieveField::IsActive, vec![1u8])],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
@@ -591,7 +592,7 @@ impl SieveScriptIngest for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::SieveScript,
|
||||
vec![Filter::eq(Property::Name, name.serialize())],
|
||||
vec![Filter::eq(SieveField::Name, name.serialize())],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
@@ -693,9 +694,9 @@ impl SieveScriptIngest for Server {
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::SieveScript)
|
||||
.update_document(document_id)
|
||||
.assert_value(Property::Value, &script_object)
|
||||
.assert_value(SieveField::Archive, &script_object)
|
||||
.set(
|
||||
Property::Value,
|
||||
SieveField::Archive,
|
||||
new_archive.serialize().caused_by(trc::location!())?,
|
||||
)
|
||||
.clear(BlobOp::Link { hash: blob_hash })
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::KV_SIEVE_ID;
|
||||
|
||||
use sieve::Sieve;
|
||||
use std::sync::Arc;
|
||||
use store::{blake3, write::ArchiveVersion};
|
||||
use utils::BlobHash;
|
||||
use types::blob_hash::BlobHash;
|
||||
|
||||
pub mod activate;
|
||||
pub mod delete;
|
||||
|
||||
@@ -4,36 +4,35 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use jmap_proto::types::{collection::SyncCollection, property::Property};
|
||||
|
||||
use super::{ArchivedEmailSubmission, EmailSubmission};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use types::{collection::SyncCollection, field::EmailSubmissionField};
|
||||
|
||||
impl IndexableObject for EmailSubmission {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: Property::UndoStatus.into(),
|
||||
field: EmailSubmissionField::UndoStatus.into(),
|
||||
value: self.undo_status.as_index().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::EmailId.into(),
|
||||
field: EmailSubmissionField::EmailId.into(),
|
||||
value: self.email_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::ThreadId.into(),
|
||||
field: EmailSubmissionField::ThreadId.into(),
|
||||
value: self.thread_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::IdentityId.into(),
|
||||
field: EmailSubmissionField::IdentityId.into(),
|
||||
value: self.identity_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::SendAt.into(),
|
||||
field: EmailSubmissionField::SendAt.into(),
|
||||
value: self.send_at.into(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::EmailSubmission.into(),
|
||||
sync_collection: SyncCollection::EmailSubmission,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
@@ -45,27 +44,27 @@ impl IndexableObject for &ArchivedEmailSubmission {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: Property::UndoStatus.into(),
|
||||
field: EmailSubmissionField::UndoStatus.into(),
|
||||
value: self.undo_status.as_index().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::EmailId.into(),
|
||||
field: EmailSubmissionField::EmailId.into(),
|
||||
value: self.email_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::ThreadId.into(),
|
||||
field: EmailSubmissionField::ThreadId.into(),
|
||||
value: self.thread_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::IdentityId.into(),
|
||||
field: EmailSubmissionField::IdentityId.into(),
|
||||
value: self.identity_id.into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: Property::SendAt.into(),
|
||||
field: EmailSubmissionField::SendAt.into(),
|
||||
value: self.send_at.into(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::EmailSubmission.into(),
|
||||
sync_collection: SyncCollection::EmailSubmission,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@ resolver = "2"
|
||||
utils = { path = "../utils" }
|
||||
store = { path = "../store" }
|
||||
common = { path = "../common" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
types = { path = "../types" }
|
||||
trc = { path = "../trc" }
|
||||
directory = { path = "../directory" }
|
||||
dav-proto = { path = "../dav-proto" }
|
||||
|
||||
14
crates/groupware/src/cache/calcard.rs
vendored
14
crates/groupware/src/cache/calcard.rs
vendored
@@ -18,14 +18,14 @@ use common::{
|
||||
DavName, DavPath, DavResource, DavResourceMetadata, DavResources, Server, auth::AccessToken,
|
||||
};
|
||||
use directory::backend::internal::manage::ManageDirectory;
|
||||
use jmap_proto::types::{
|
||||
collection::{Collection, SyncCollection},
|
||||
value::AclGrant,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use store::ahash::{AHashMap, AHashSet};
|
||||
use tokio::sync::Semaphore;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::AclGrant,
|
||||
collection::{Collection, SyncCollection},
|
||||
};
|
||||
use utils::map::bitmap::Bitmap;
|
||||
|
||||
pub(super) async fn build_calcard_resources(
|
||||
@@ -41,7 +41,7 @@ pub(super) async fn build_calcard_resources(
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.get_last_change_id(account_id, sync_collection)
|
||||
.get_last_change_id(account_id, sync_collection.into())
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.unwrap_or_default();
|
||||
@@ -73,7 +73,7 @@ pub(super) async fn build_calcard_resources(
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.get_last_change_id(account_id, sync_collection)
|
||||
.get_last_change_id(account_id, sync_collection.into())
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.unwrap_or_default();
|
||||
@@ -183,7 +183,7 @@ pub(super) async fn build_scheduling_resources(
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.get_last_change_id(account_id, SyncCollection::CalendarScheduling)
|
||||
.get_last_change_id(account_id, SyncCollection::CalendarScheduling.into())
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.unwrap_or_default();
|
||||
|
||||
16
crates/groupware/src/cache/file.rs
vendored
16
crates/groupware/src/cache/file.rs
vendored
@@ -10,11 +10,6 @@ use crate::{
|
||||
};
|
||||
use common::{DavPath, DavResource, DavResourceMetadata, DavResources, Server};
|
||||
use directory::backend::internal::manage::ManageDirectory;
|
||||
use jmap_proto::types::{
|
||||
collection::{Collection, SyncCollection},
|
||||
property::Property,
|
||||
value::AclGrant,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use store::{
|
||||
Deserialize, IterateParams, U32_LEN, ValueKey,
|
||||
@@ -23,6 +18,11 @@ use store::{
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::AclGrant,
|
||||
collection::{Collection, SyncCollection},
|
||||
field::Field,
|
||||
};
|
||||
use utils::{map::bitmap::Bitmap, topological::TopologicalSort};
|
||||
|
||||
pub(super) async fn build_file_resources(
|
||||
@@ -34,7 +34,7 @@ pub(super) async fn build_file_resources(
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.get_last_change_id(account_id, SyncCollection::FileNode)
|
||||
.get_last_change_id(account_id, SyncCollection::FileNode.into())
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.unwrap_or_default();
|
||||
@@ -130,13 +130,13 @@ async fn fetch_files(server: &Server, account_id: u32) -> trc::Result<Vec<DavRes
|
||||
account_id,
|
||||
collection: Collection::FileNode.into(),
|
||||
document_id: 0,
|
||||
class: ValueClass::Property(Property::Value.into()),
|
||||
class: ValueClass::from(Field::ARCHIVE),
|
||||
},
|
||||
ValueKey {
|
||||
account_id,
|
||||
collection: Collection::FileNode.into(),
|
||||
document_id: u32::MAX,
|
||||
class: ValueClass::Property(Property::Value.into()),
|
||||
class: ValueClass::from(Field::ARCHIVE),
|
||||
},
|
||||
),
|
||||
|key, value| {
|
||||
|
||||
4
crates/groupware/src/cache/mod.rs
vendored
4
crates/groupware/src/cache/mod.rs
vendored
@@ -17,7 +17,6 @@ use calcard::{
|
||||
};
|
||||
use common::{CacheSwap, DavResource, DavResources, Server, auth::AccessToken};
|
||||
use file::{build_file_resources, build_nested_hierarchy, resource_from_file};
|
||||
use jmap_proto::types::collection::{Collection, SyncCollection};
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
@@ -26,6 +25,7 @@ use store::{
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use trc::{AddContext, StoreEvent};
|
||||
use types::collection::{Collection, SyncCollection};
|
||||
|
||||
pub mod calcard;
|
||||
pub mod file;
|
||||
@@ -119,7 +119,7 @@ impl GroupwareCache for Server {
|
||||
.data
|
||||
.changes(
|
||||
account_id,
|
||||
collection,
|
||||
collection.into(),
|
||||
Query::Since(cache.highest_change_id),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -11,8 +11,7 @@ use super::{
|
||||
ArchivedTimezone, Calendar, CalendarEvent, CalendarPreferences, DefaultAlert, Timezone,
|
||||
};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use common::{IDX_CREATED, IDX_UID};
|
||||
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
|
||||
use types::{acl::AclGrant, collection::SyncCollection, field::CalendarField};
|
||||
|
||||
impl IndexableObject for Calendar {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
@@ -27,7 +26,7 @@ impl IndexableObject for Calendar {
|
||||
+ self.name.len() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::Calendar.into(),
|
||||
sync_collection: SyncCollection::Calendar,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
@@ -52,7 +51,7 @@ impl IndexableObject for &ArchivedCalendar {
|
||||
+ self.name.len() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::Calendar.into(),
|
||||
sync_collection: SyncCollection::Calendar,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
@@ -69,7 +68,7 @@ impl IndexableObject for CalendarEvent {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: IDX_UID,
|
||||
field: CalendarField::Uid.into(),
|
||||
value: self.data.event.uids().next().into(),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
@@ -79,7 +78,7 @@ impl IndexableObject for CalendarEvent {
|
||||
+ self.size,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Calendar.into(),
|
||||
sync_collection: SyncCollection::Calendar,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
@@ -91,7 +90,7 @@ impl IndexableObject for &ArchivedCalendarEvent {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: IDX_UID,
|
||||
field: CalendarField::Uid.into(),
|
||||
value: self.data.event.uids().next().into(),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
@@ -101,7 +100,7 @@ impl IndexableObject for &ArchivedCalendarEvent {
|
||||
+ self.size,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::Calendar.into(),
|
||||
sync_collection: SyncCollection::Calendar,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
@@ -120,11 +119,11 @@ impl IndexableObject for CalendarScheduling {
|
||||
[
|
||||
IndexValue::Quota { used: self.size },
|
||||
IndexValue::Index {
|
||||
field: IDX_CREATED,
|
||||
field: CalendarField::Created.into(),
|
||||
value: self.created.into(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::CalendarScheduling.into(),
|
||||
sync_collection: SyncCollection::CalendarScheduling,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
@@ -139,11 +138,11 @@ impl IndexableObject for &ArchivedCalendarScheduling {
|
||||
used: self.size.to_native(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: IDX_CREATED,
|
||||
field: CalendarField::Created.into(),
|
||||
value: self.created.to_native().into(),
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::CalendarScheduling.into(),
|
||||
sync_collection: SyncCollection::CalendarScheduling,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -24,18 +24,21 @@ use calcard::{
|
||||
},
|
||||
};
|
||||
use common::{
|
||||
DavName, IDX_EMAIL, IDX_UID, Server,
|
||||
DavName, Server,
|
||||
auth::{AccessToken, ResourceToken, oauth::GrantType},
|
||||
config::groupware::CalendarTemplateVariable,
|
||||
i18n,
|
||||
};
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::{
|
||||
query::Filter,
|
||||
rand,
|
||||
write::{BatchBuilder, now},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
collection::Collection,
|
||||
field::{CalendarField, ContactField},
|
||||
};
|
||||
use utils::{template::Variables, url_params::UrlParams};
|
||||
|
||||
pub enum ItipIngestError {
|
||||
@@ -142,7 +145,10 @@ impl ItipIngest for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::CalendarEvent,
|
||||
vec![Filter::eq(IDX_UID, itip_snapshots.uid.as_bytes().to_vec())],
|
||||
vec![Filter::eq(
|
||||
CalendarField::Uid,
|
||||
itip_snapshots.uid.as_bytes().to_vec(),
|
||||
)],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
@@ -261,7 +267,7 @@ impl ItipIngest for Server {
|
||||
.filter(
|
||||
account_id,
|
||||
Collection::ContactCard,
|
||||
vec![Filter::eq(IDX_EMAIL, sender.as_bytes().to_vec())],
|
||||
vec![Filter::eq(ContactField::Email, sender.as_bytes().to_vec())],
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
|
||||
@@ -14,7 +14,7 @@ pub mod storage;
|
||||
use calcard::icalendar::ICalendar;
|
||||
use common::DavName;
|
||||
use dav_proto::schema::request::DeadProperty;
|
||||
use jmap_proto::types::{acl::Acl, value::AclGrant};
|
||||
use types::acl::{Acl, AclGrant};
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{
|
||||
ArchivedCalendar, ArchivedCalendarEvent, Calendar, CalendarEvent, CalendarPreferences,
|
||||
alarm::CalendarAlarm,
|
||||
};
|
||||
use crate::{
|
||||
DavResourceName, DestroyArchive, RFC_3986,
|
||||
calendar::{ArchivedCalendarScheduling, CalendarScheduling},
|
||||
scheduling::{ItipMessages, event_cancel::itip_cancel},
|
||||
};
|
||||
use calcard::common::timezone::Tz;
|
||||
use common::{IDX_CREATED, Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::collection::{Collection, VanishedCollection};
|
||||
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use store::{
|
||||
IndexKey, IterateParams, SerializeInfallible, U16_LEN, U32_LEN, U64_LEN,
|
||||
roaring::RoaringBitmap,
|
||||
@@ -22,10 +25,9 @@ use store::{
|
||||
},
|
||||
};
|
||||
use trc::AddContext;
|
||||
|
||||
use super::{
|
||||
ArchivedCalendar, ArchivedCalendarEvent, Calendar, CalendarEvent, CalendarPreferences,
|
||||
alarm::CalendarAlarm,
|
||||
use types::{
|
||||
collection::{Collection, VanishedCollection},
|
||||
field::CalendarField,
|
||||
};
|
||||
|
||||
pub trait ItipAutoExpunge: Sync + Send {
|
||||
@@ -47,14 +49,14 @@ impl ItipAutoExpunge for Server {
|
||||
account_id,
|
||||
collection: Collection::CalendarScheduling.into(),
|
||||
document_id: 0,
|
||||
field: IDX_CREATED,
|
||||
field: CalendarField::Created.into(),
|
||||
key: 0u64.serialize(),
|
||||
},
|
||||
IndexKey {
|
||||
account_id,
|
||||
collection: Collection::CalendarScheduling.into(),
|
||||
document_id: u32::MAX,
|
||||
field: IDX_CREATED,
|
||||
field: CalendarField::Created.into(),
|
||||
key: now().saturating_sub(hold_period).serialize(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -9,9 +9,8 @@ use calcard::vcard::VCardProperty;
|
||||
use common::storage::index::{
|
||||
IndexItem, IndexValue, IndexableAndSerializableObject, IndexableObject,
|
||||
};
|
||||
use common::{IDX_EMAIL, IDX_UID};
|
||||
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
|
||||
use std::collections::HashSet;
|
||||
use types::{acl::AclGrant, collection::SyncCollection, field::ContactField};
|
||||
use utils::sanitize_email;
|
||||
|
||||
impl IndexableObject for AddressBook {
|
||||
@@ -27,7 +26,7 @@ impl IndexableObject for AddressBook {
|
||||
+ self.name.len() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::AddressBook.into(),
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
@@ -52,7 +51,7 @@ impl IndexableObject for &ArchivedAddressBook {
|
||||
+ self.name.len() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::AddressBook.into(),
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
@@ -69,11 +68,11 @@ impl IndexableObject for ContactCard {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: IDX_UID,
|
||||
field: ContactField::Uid.into(),
|
||||
value: self.card.uid().into(),
|
||||
},
|
||||
IndexValue::IndexList {
|
||||
field: IDX_EMAIL,
|
||||
field: ContactField::Email.into(),
|
||||
value: self
|
||||
.emails()
|
||||
.map(Into::into)
|
||||
@@ -88,7 +87,7 @@ impl IndexableObject for ContactCard {
|
||||
+ self.size,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::AddressBook.into(),
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
@@ -100,11 +99,11 @@ impl IndexableObject for &ArchivedContactCard {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: IDX_UID,
|
||||
field: ContactField::Uid.into(),
|
||||
value: self.card.uid().into(),
|
||||
},
|
||||
IndexValue::IndexList {
|
||||
field: IDX_EMAIL,
|
||||
field: ContactField::Email.into(),
|
||||
value: self
|
||||
.emails()
|
||||
.map(Into::into)
|
||||
@@ -119,7 +118,7 @@ impl IndexableObject for &ArchivedContactCard {
|
||||
+ self.size,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::AddressBook.into(),
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod storage;
|
||||
use calcard::vcard::VCard;
|
||||
use common::DavName;
|
||||
use dav_proto::schema::request::DeadProperty;
|
||||
use jmap_proto::types::{acl::Acl, value::AclGrant};
|
||||
use types::acl::{Acl, AclGrant};
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard};
|
||||
use crate::DestroyArchive;
|
||||
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::collection::{Collection, VanishedCollection};
|
||||
use store::write::{Archive, BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::DestroyArchive;
|
||||
|
||||
use super::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard};
|
||||
use types::collection::{Collection, VanishedCollection};
|
||||
|
||||
impl ContactCard {
|
||||
pub fn update<'x>(
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use super::{ArchivedFileNode, FileNode};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
|
||||
use types::{acl::AclGrant, collection::SyncCollection};
|
||||
|
||||
impl IndexableObject for FileNode {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
@@ -18,7 +18,7 @@ impl IndexableObject for FileNode {
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
prefix: None,
|
||||
sync_collection: SyncCollection::FileNode.into(),
|
||||
sync_collection: SyncCollection::FileNode,
|
||||
},
|
||||
IndexValue::Quota { used: self.size() },
|
||||
]);
|
||||
@@ -48,7 +48,7 @@ impl IndexableObject for &ArchivedFileNode {
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
prefix: None,
|
||||
sync_collection: SyncCollection::FileNode.into(),
|
||||
sync_collection: SyncCollection::FileNode,
|
||||
},
|
||||
IndexValue::Quota { used: self.size() },
|
||||
]);
|
||||
|
||||
@@ -8,8 +8,7 @@ pub mod index;
|
||||
pub mod storage;
|
||||
|
||||
use dav_proto::schema::request::DeadProperty;
|
||||
use jmap_proto::types::value::AclGrant;
|
||||
use utils::BlobHash;
|
||||
use types::{acl::AclGrant, blob_hash::BlobHash};
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{ArchivedFileNode, FileNode};
|
||||
use crate::DestroyArchive;
|
||||
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
|
||||
use jmap_proto::types::collection::{Collection, VanishedCollection};
|
||||
use store::write::{Archive, BatchBuilder, now};
|
||||
use trc::AddContext;
|
||||
|
||||
use crate::DestroyArchive;
|
||||
|
||||
use super::{ArchivedFileNode, FileNode};
|
||||
use types::collection::{Collection, VanishedCollection};
|
||||
|
||||
impl FileNode {
|
||||
pub fn insert<'x>(
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
use calcard::common::timezone::Tz;
|
||||
use common::DavResources;
|
||||
use jmap_proto::types::collection::{Collection, SyncCollection};
|
||||
use percent_encoding::{AsciiSet, CONTROLS};
|
||||
use types::collection::{Collection, SyncCollection};
|
||||
|
||||
pub mod cache;
|
||||
pub mod calendar;
|
||||
|
||||
@@ -17,6 +17,7 @@ groupware = { path = "../groupware" }
|
||||
spam-filter = { path = "../spam-filter" }
|
||||
http_proto = { path = "../http-proto" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
types = { path = "../types" }
|
||||
directory = { path = "../directory" }
|
||||
services = { path = "../services" }
|
||||
smtp-proto = { version = "0.2" }
|
||||
|
||||
@@ -30,7 +30,7 @@ use store::{
|
||||
write::{BatchBuilder, BlobOp, now},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use utils::BlobHash;
|
||||
use types::blob_hash::BlobHash;
|
||||
use x509_parser::nom::AsBytes;
|
||||
|
||||
pub trait FormHandler: Sync + Send {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use common::{Server, auth::AccessToken};
|
||||
use directory::backend::internal::manage;
|
||||
use email::message::crypto::{
|
||||
@@ -13,15 +11,16 @@ use email::message::crypto::{
|
||||
EncryptionMethod, EncryptionParams, EncryptionType, try_parse_certs,
|
||||
};
|
||||
use http_proto::*;
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use mail_builder::encoders::base64::base64_encode_mime;
|
||||
use mail_parser::MessageParser;
|
||||
use serde_json::json;
|
||||
use std::{future::Future, sync::Arc};
|
||||
use store::{
|
||||
Deserialize, Serialize,
|
||||
write::{AlignedBytes, Archive, Archiver, BatchBuilder},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{collection::Collection, field::PrincipalField};
|
||||
|
||||
pub trait CryptoHandler: Sync + Send {
|
||||
fn handle_crypto_get(
|
||||
@@ -43,7 +42,7 @@ impl CryptoHandler for Server {
|
||||
access_token.primary_id(),
|
||||
Collection::Principal,
|
||||
0,
|
||||
Property::Parameters,
|
||||
PrincipalField::EncryptionKeys.into(),
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -96,7 +95,7 @@ impl CryptoHandler for Server {
|
||||
.with_account_id(access_token.primary_id())
|
||||
.with_collection(Collection::Principal)
|
||||
.update_document(0)
|
||||
.clear(Property::Parameters);
|
||||
.clear(PrincipalField::EncryptionKeys);
|
||||
self.core.storage.data.write(batch.build_all()).await?;
|
||||
return Ok(JsonResponse::new(json!({
|
||||
"data": (),
|
||||
@@ -146,7 +145,7 @@ impl CryptoHandler for Server {
|
||||
.with_account_id(access_token.primary_id())
|
||||
.with_collection(Collection::Principal)
|
||||
.update_document(0)
|
||||
.set(Property::Parameters, params);
|
||||
.set(PrincipalField::EncryptionKeys, params);
|
||||
self.core.storage.data.write(batch.build_all()).await?;
|
||||
|
||||
Ok(JsonResponse::new(json!({
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use common::{Server, enterprise::undelete::DeletedBlob};
|
||||
use directory::backend::internal::manage::ManageDirectory;
|
||||
@@ -18,13 +16,14 @@ use email::{
|
||||
message::ingest::{EmailIngest, IngestEmail, IngestSource},
|
||||
};
|
||||
use hyper::Method;
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use mail_parser::{DateTime, MessageParser};
|
||||
use serde_json::json;
|
||||
use std::future::Future;
|
||||
use std::str::FromStr;
|
||||
use store::write::{BatchBuilder, BlobOp, ValueClass};
|
||||
use trc::AddContext;
|
||||
use utils::{BlobHash, url_params::UrlParams};
|
||||
use types::{blob_hash::BlobHash, collection::Collection};
|
||||
use utils::url_params::UrlParams;
|
||||
|
||||
use http_proto::{request::decode_path_element, *};
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ use directory::{
|
||||
use email::message::{ingest::EmailIngest, metadata::MessageData};
|
||||
use http_proto::{request::decode_path_element, *};
|
||||
use hyper::Method;
|
||||
use jmap_proto::types::{collection::Collection, property::Property};
|
||||
use serde_json::json;
|
||||
use services::task_manager::fts::FtsIndexTask;
|
||||
use std::future::Future;
|
||||
@@ -28,6 +27,10 @@ use store::{
|
||||
write::{Archiver, BatchBuilder, ValueClass},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
collection::Collection,
|
||||
field::{EmailField, MailboxField},
|
||||
};
|
||||
use utils::url_params::UrlParams;
|
||||
|
||||
// SPDX-SnippetBegin
|
||||
@@ -354,7 +357,7 @@ pub async fn reset_imap_uids(server: &Server, account_id: u32) -> trc::Result<(u
|
||||
.with_changes(new_mailbox),
|
||||
)
|
||||
.caused_by(trc::location!())?
|
||||
.clear(Property::EmailIds);
|
||||
.clear(MailboxField::UidCounter);
|
||||
server
|
||||
.store()
|
||||
.write(batch.build_all())
|
||||
@@ -399,9 +402,9 @@ pub async fn reset_imap_uids(server: &Server, account_id: u32) -> trc::Result<(u
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::Email)
|
||||
.update_document(message_id)
|
||||
.assert_value(ValueClass::Property(Property::Value.into()), &data)
|
||||
.assert_value(ValueClass::Property(EmailField::Archive.into()), &data)
|
||||
.set(
|
||||
Property::Value,
|
||||
EmailField::Archive,
|
||||
Archiver::new(new_data)
|
||||
.serialize()
|
||||
.caused_by(trc::location!())?,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user