Moved shared data types to 'types' subcrate

This commit is contained in:
mdecimus
2025-09-22 10:01:31 +02:00
parent b49202a2b3
commit 71cc30c7be
325 changed files with 4132 additions and 3540 deletions

40
Cargo.lock generated
View File

@@ -1285,6 +1285,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"unicode-security", "unicode-security",
"utils", "utils",
"whatlang", "whatlang",
@@ -1737,11 +1738,11 @@ dependencies = [
"hashify", "hashify",
"http_proto", "http_proto",
"hyper 1.7.0", "hyper 1.7.0",
"jmap_proto",
"percent-encoding", "percent-encoding",
"rkyv", "rkyv",
"store", "store",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -1949,7 +1950,6 @@ dependencies = [
"compact_str", "compact_str",
"deadpool 0.10.0", "deadpool 0.10.0",
"futures", "futures",
"jmap_proto",
"ldap3", "ldap3",
"mail-builder", "mail-builder",
"mail-parser", "mail-parser",
@@ -1976,6 +1976,7 @@ dependencies = [
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"totp-rs", "totp-rs",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -2230,7 +2231,6 @@ dependencies = [
"directory", "directory",
"groupware", "groupware",
"hashify", "hashify",
"jmap_proto",
"mail-builder", "mail-builder",
"mail-parser", "mail-parser",
"nlp", "nlp",
@@ -2249,6 +2249,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -2848,12 +2849,12 @@ dependencies = [
"dav-proto", "dav-proto",
"directory", "directory",
"hashify", "hashify",
"jmap_proto",
"percent-encoding", "percent-encoding",
"rkyv", "rkyv",
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -3171,6 +3172,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
"x509-parser 0.17.0", "x509-parser 0.17.0",
] ]
@@ -3558,7 +3560,6 @@ dependencies = [
"email", "email",
"imap_proto", "imap_proto",
"indexmap 2.11.1", "indexmap 2.11.1",
"jmap_proto",
"mail-parser", "mail-parser",
"mail-send", "mail-send",
"md5 0.8.0", "md5 0.8.0",
@@ -3571,6 +3572,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -3582,11 +3584,11 @@ dependencies = [
"chrono", "chrono",
"compact_str", "compact_str",
"hashify", "hashify",
"jmap_proto",
"mail-parser", "mail-parser",
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
] ]
[[package]] [[package]]
@@ -3891,6 +3893,7 @@ dependencies = [
"tokio-tungstenite 0.27.0", "tokio-tungstenite 0.27.0",
"trc", "trc",
"tungstenite 0.27.0", "tungstenite 0.27.0",
"types",
"utils", "utils",
] ]
@@ -3931,6 +3934,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -4426,6 +4430,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -4523,7 +4528,6 @@ dependencies = [
"directory", "directory",
"email", "email",
"groupware", "groupware",
"jmap_proto",
"lz4_flex", "lz4_flex",
"mail-auth", "mail-auth",
"mail-parser", "mail-parser",
@@ -4536,6 +4540,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -5521,7 +5526,6 @@ dependencies = [
"directory", "directory",
"email", "email",
"imap", "imap",
"jmap_proto",
"mail-parser", "mail-parser",
"mail-send", "mail-send",
"rustls 0.23.31", "rustls 0.23.31",
@@ -5529,6 +5533,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -7411,6 +7416,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -7633,6 +7639,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
"webpki-roots 1.0.2", "webpki-roots 1.0.2",
"x509-parser 0.17.0", "x509-parser 0.17.0",
@@ -7768,7 +7775,6 @@ dependencies = [
"imap", "imap",
"jemallocator", "jemallocator",
"jmap", "jmap",
"jmap_proto",
"managesieve", "managesieve",
"migration", "migration",
"pop3", "pop3",
@@ -7778,6 +7784,7 @@ dependencies = [
"store", "store",
"tokio", "tokio",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -7861,6 +7868,7 @@ dependencies = [
"tokio-postgres", "tokio-postgres",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
"xxhash-rust", "xxhash-rust",
"zenoh", "zenoh",
@@ -8082,6 +8090,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls 0.26.2", "tokio-rustls 0.26.2",
"trc", "trc",
"types",
"utils", "utils",
] ]
@@ -8690,6 +8699,19 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "types"
version = "0.13.3"
dependencies = [
"blake3",
"compact_str",
"hashify",
"rkyv",
"serde",
"trc",
"utils",
]
[[package]] [[package]]
name = "typewit" name = "typewit"
version = "1.14.2" version = "1.14.2"

View File

@@ -2,6 +2,7 @@
resolver = "2" resolver = "2"
members = [ members = [
"crates/main", "crates/main",
"crates/types",
"crates/http", "crates/http",
"crates/http-proto", "crates/http-proto",
"crates/jmap", "crates/jmap",

View File

@@ -11,6 +11,7 @@ nlp = { path = "../nlp" }
store = { path = "../store" } store = { path = "../store" }
trc = { path = "../trc" } trc = { path = "../trc" }
directory = { path = "../directory" } directory = { path = "../directory" }
types = { path = "../types" }
jmap_proto = { path = "../jmap-proto" } jmap_proto = { path = "../jmap-proto" }
imap_proto = { path = "../imap-proto" } imap_proto = { path = "../imap-proto" }
sieve-rs = { version = "0.7", features = ["rkyv", "serde"] } sieve-rs = { version = "0.7", features = ["rkyv", "serde"] }

View File

@@ -18,16 +18,13 @@ use directory::{
manage::{ChangedPrincipals, ManageDirectory}, manage::{ChangedPrincipals, ManageDirectory},
}, },
}; };
use jmap_proto::{
request::RequestMethod,
types::{acl::Acl, collection::Collection, id::Id},
};
use std::{ use std::{
hash::{DefaultHasher, Hash, Hasher}, hash::{DefaultHasher, Hash, Hasher},
sync::Arc, sync::Arc,
}; };
use store::{query::acl::AclQuery, rand}; use store::{query::acl::AclQuery, rand};
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::Collection};
use utils::map::{ use utils::map::{
bitmap::{Bitmap, BitmapItem}, bitmap::{Bitmap, BitmapItem},
vec_map::VecMap, vec_map::VecMap,
@@ -163,7 +160,7 @@ impl Server {
{ {
if !access_token.is_member(acl_item.to_account_id) { if !access_token.is_member(acl_item.to_account_id) {
let acl = Bitmap::<Acl>::from(acl_item.permissions); 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() { if !collection.is_valid() {
return Err(trc::StoreEvent::DataCorruption return Err(trc::StoreEvent::DataCorruption
.ctx(trc::Key::Reason, "Corrupted collection found in ACL key.") .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) !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> { pub fn shared_accounts(&self, collection: Collection) -> impl Iterator<Item = &u32> {
let collection = collection.into();
self.member_of self.member_of
.iter() .iter()
.chain(self.access_to.iter().filter_map(move |(id, cols)| { .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) 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 { pub fn as_resource_token(&self) -> ResourceToken {
ResourceToken { ResourceToken {
account_id: self.primary_id, account_id: self.primary_id,

View File

@@ -9,10 +9,10 @@ use directory::{
Directory, FALLBACK_ADMIN_ID, Permission, Permissions, Principal, QueryParams, Type, Directory, FALLBACK_ADMIN_ID, Permission, Permissions, Principal, QueryParams, Type,
backend::internal::lookup::DirectoryStore, core::secret::verify_secret_hash, backend::internal::lookup::DirectoryStore, core::secret::verify_secret_hash,
}; };
use jmap_proto::types::collection::Collection;
use mail_send::Credentials; use mail_send::Credentials;
use oauth::GrantType; use oauth::GrantType;
use std::{net::IpAddr, sync::Arc}; use std::{net::IpAddr, sync::Arc};
use types::collection::Collection;
use utils::{ use utils::{
cache::CacheItemWeight, cache::CacheItemWeight,
map::{bitmap::Bitmap, vec_map::VecMap}, map::{bitmap::Bitmap, vec_map::VecMap},

View File

@@ -4,18 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 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 { impl JmapConfig {
pub fn add_capabilities(&mut self, config: &mut Config) { pub fn add_capabilities(&mut self, config: &mut Config) {

View File

@@ -4,10 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use std::{str::FromStr, time::Duration};
use jmap_proto::request::capability::BaseCapabilities; use jmap_proto::request::capability::BaseCapabilities;
use nlp::language::Language; use nlp::language::Language;
use std::{str::FromStr, time::Duration};
use utils::config::{Config, Rate, cron::SimpleCron, utils::ParseValue}; use utils::config::{Config, Rate, cron::SimpleCron, utils::ParseValue};
#[derive(Default, Clone)] #[derive(Default, Clone)]

View File

@@ -17,13 +17,6 @@ use crate::{
ipc::{BroadcastEvent, StateEvent}, ipc::{BroadcastEvent, StateEvent},
}; };
use directory::{Directory, QueryParams, Type, backend::internal::manage::ManageDirectory}; 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 mail_auth::IpLookupStrategy;
use sieve::Sieve; use sieve::Sieve;
use std::{ use std::{
@@ -31,8 +24,8 @@ use std::{
time::Duration, time::Duration,
}; };
use store::{ use store::{
BitmapKey, BlobClass, BlobStore, Deserialize, FtsStore, InMemoryStore, IndexKey, IterateParams, BitmapKey, BlobStore, Deserialize, FtsStore, InMemoryStore, IndexKey, IterateParams, Key,
Key, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN, U64_LEN, ValueKey, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN, U64_LEN, ValueKey,
dispatch::DocumentSet, dispatch::DocumentSet,
roaring::RoaringBitmap, roaring::RoaringBitmap,
write::{ write::{
@@ -41,7 +34,13 @@ use store::{
}, },
}; };
use trc::AddContext; 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 { impl Server {
#[inline(always)] #[inline(always)]
@@ -353,14 +352,14 @@ impl Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: 0, document_id: 0,
field: Property::Size.into(), field: EmailField::Size.into(),
key: 0u32.serialize(), key: 0u32.serialize(),
}, },
IndexKey { IndexKey {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: u32::MAX, document_id: u32::MAX,
field: Property::Size.into(), field: EmailField::Size.into(),
key: u32::MAX.serialize(), key: u32::MAX.serialize(),
}, },
) )
@@ -503,7 +502,7 @@ impl Server {
account_id, account_id,
collection: collection.into(), collection: collection.into(),
document_id, document_id,
class: ValueClass::Property(Property::Value.into()), class: ValueClass::Property(Field::ARCHIVE.into()),
}) })
.await .await
.add_context(|err| { .add_context(|err| {
@@ -520,9 +519,8 @@ impl Server {
account_id: u32, account_id: u32,
collection: Collection, collection: Collection,
document_id: u32, document_id: u32,
property: impl AsRef<Property> + Sync + Send, property: Field,
) -> trc::Result<Option<Archive<AlignedBytes>>> { ) -> trc::Result<Option<Archive<AlignedBytes>>> {
let property = property.as_ref();
self.core self.core
.storage .storage
.data .data
@@ -563,13 +561,13 @@ impl Server {
account_id, account_id,
collection, collection,
document_id: documents.min(), document_id: documents.min(),
class: ValueClass::Property(Property::Value.into()), class: ValueClass::Property(Field::ARCHIVE.into()),
}, },
ValueKey { ValueKey {
account_id, account_id,
collection, collection,
document_id: documents.max(), document_id: documents.max(),
class: ValueClass::Property(Property::Value.into()), class: ValueClass::Property(Field::ARCHIVE.into()),
}, },
), ),
|key, value| { |key, value| {
@@ -654,12 +652,12 @@ impl Server {
let mut state_change = let mut state_change =
StateChange::new(account_id, assigned_ids.last_change_id(account_id)?); StateChange::new(account_id, assigned_ids.last_change_id(account_id)?);
for changed_collection in changed_collections.changed_containers { 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); state_change.set_change(data_type);
} }
} }
for changed_collection in changed_collections.changed_items { 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); state_change.set_change(data_type);
} }
} }
@@ -753,21 +751,18 @@ impl Server {
// Write truncation entry for cache // Write truncation entry for cache
let mut batch = BatchBuilder::new(); let mut batch = BatchBuilder::new();
batch batch.with_account_id(account_id).set(
.with_account_id(account_id) ValueClass::Any(AnyClass {
.with_collection(collection) subspace: SUBSPACE_LOGS,
.set( key: LogKey {
ValueClass::Any(AnyClass { account_id,
subspace: SUBSPACE_LOGS, collection,
key: LogKey { change_id: first_change_id,
account_id, }
collection, .serialize(0),
change_id: first_change_id, }),
} Vec::new(),
.serialize(0), );
}),
Vec::new(),
);
self.store() self.store()
.write(batch.build_all()) .write(batch.build_all())
.await .await

View File

@@ -8,6 +8,7 @@
* *
*/ */
use crate::Core;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use store::{ use store::{
IterateParams, U32_LEN, U64_LEN, ValueKey, IterateParams, U32_LEN, U64_LEN, ValueKey,
@@ -18,9 +19,7 @@ use store::{
}, },
}; };
use trc::AddContext; use trc::AddContext;
use utils::{BLOB_HASH_LEN, BlobHash}; use types::blob_hash::{BLOB_HASH_LEN, BlobHash};
use crate::Core;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct DeletedBlob<H, T, C> { pub struct DeletedBlob<H, T, C> {

View File

@@ -4,24 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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::{ use crate::config::smtp::{
queue::QueueName, queue::QueueName,
report::AggregateFrequency, report::AggregateFrequency,
resolver::{Policy, Tlsa}, 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 { pub enum HousekeeperEvent {
AcmeReschedule { AcmeReschedule {

View File

@@ -25,7 +25,6 @@ use config::{
telemetry::Metrics, telemetry::Metrics,
}; };
use ipc::{BroadcastEvent, HousekeeperEvent, QueueEvent, ReportingEvent, StateEvent}; use ipc::{BroadcastEvent, HousekeeperEvent, QueueEvent, ReportingEvent, StateEvent};
use jmap_proto::types::value::AclGrant;
use listener::{asn::AsnGeoLookupData, blocked::Security, tls::AcmeProviders}; use listener::{asn::AsnGeoLookupData, blocked::Security, tls::AcmeProviders};
use mail_auth::{MX, Txt}; use mail_auth::{MX, Txt};
use manager::webadmin::{Resource, WebAdminManager}; use manager::webadmin::{Resource, WebAdminManager};
@@ -41,6 +40,7 @@ use std::{
use tinyvec::TinyVec; use tinyvec::TinyVec;
use tokio::sync::{Notify, Semaphore, mpsc}; use tokio::sync::{Notify, Semaphore, mpsc};
use tokio_rustls::TlsConnector; use tokio_rustls::TlsConnector;
use types::acl::AclGrant;
use utils::{ use utils::{
cache::{Cache, CacheItemWeight, CacheWithTtl}, cache::{Cache, CacheItemWeight, CacheWithTtl},
snowflake::SnowflakeIdGenerator, snowflake::SnowflakeIdGenerator,
@@ -123,10 +123,6 @@ pub const KV_LOCK_HOUSEKEEPER: u8 = 24;
pub const KV_LOCK_DAV: u8 = 25; pub const KV_LOCK_DAV: u8 = 25;
pub const KV_SIEVE_ID: u8 = 26; 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)] #[derive(Clone)]
pub struct Server { pub struct Server {
pub inner: Arc<Inner>, pub inner: Arc<Inner>,

View File

@@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use crate::Core;
use ahash::{AHashMap, AHashSet};
use std::{ use std::{
collections::BTreeSet, collections::BTreeSet,
io::{BufWriter, Write}, io::{BufWriter, Write},
@@ -11,9 +13,6 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::{self, SyncSender}, sync::mpsc::{self, SyncSender},
}; };
use ahash::{AHashMap, AHashSet};
use jmap_proto::types::{collection::Collection, property::Property};
use store::{ use store::{
BitmapKey, Deserialize, IndexKey, IterateParams, LogKey, SUBSPACE_BITMAP_ID, BitmapKey, Deserialize, IndexKey, IterateParams, LogKey, SUBSPACE_BITMAP_ID,
SUBSPACE_BITMAP_TAG, SUBSPACE_BITMAP_TEXT, SerializeInfallible, U32_LEN, U64_LEN, ValueKey, SUBSPACE_BITMAP_TAG, SUBSPACE_BITMAP_TEXT, SerializeInfallible, U32_LEN, U64_LEN, ValueKey,
@@ -22,15 +21,17 @@ use store::{
QueueEvent, TagValue, ValueClass, key::DeserializeBigEndian, QueueEvent, TagValue, ValueClass, key::DeserializeBigEndian,
}, },
}; };
use types::{
blob_hash::{BLOB_HASH_LEN, BlobHash},
collection::Collection,
field::{Field, MailboxField},
};
use utils::{ use utils::{
BLOB_HASH_LEN, BlobHash, UnwrapFailure, UnwrapFailure,
codec::leb128::{Leb128_, Leb128Reader}, codec::leb128::{Leb128_, Leb128Reader},
failed, failed,
}; };
use crate::Core;
pub(super) const MAGIC_MARKER: u8 = 123; pub(super) const MAGIC_MARKER: u8 = 123;
pub(super) const FILE_VERSION: u8 = 2; pub(super) const FILE_VERSION: u8 = 2;
@@ -196,21 +197,21 @@ impl Core {
// Obtain UID counter // Obtain UID counter
if collection == u8::from(Collection::Mailbox) if collection == u8::from(Collection::Mailbox)
&& u8::from(Property::Value) == field && u8::from(Field::ARCHIVE) == field
{ {
let value = store let value = store
.get_counter(ValueKey { .get_counter(ValueKey {
account_id, account_id,
collection, collection,
document_id, document_id,
class: ValueClass::Property(Property::EmailIds.into()), class: MailboxField::UidCounter.into(),
}) })
.await .await
.failed("Failed to get counter"); .failed("Failed to get counter");
if value != 0 { if value != 0 {
writer writer
.send(Op::KeyValue(( .send(Op::KeyValue((
vec![u8::from(Property::EmailIds)], vec![u8::from(MailboxField::UidCounter)],
value.serialize(), value.serialize(),
))) )))
.failed("Failed to send key value"); .failed("Failed to send key value");

View File

@@ -17,8 +17,8 @@ use store::{
write::{BatchBuilder, ValueClass}, write::{BatchBuilder, ValueClass},
}; };
use trc::AddContext; use trc::AddContext;
use types::semver::Semver;
use utils::{ use utils::{
Semver,
config::{Config, ConfigKey}, config::{Config, ConfigKey},
glob::GlobPattern, glob::GlobPattern,
}; };

View File

@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use crate::Core;
use ahash::AHashMap;
use std::{ use std::{
io::ErrorKind, io::ErrorKind,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use crate::Core;
use ahash::AHashMap;
use jmap_proto::types::{collection::Collection, property::Property};
use store::{ use store::{
BlobStore, Key, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN, BlobStore, Key, LogKey, SUBSPACE_LOGS, SerializeInfallible, Store, U32_LEN,
roaring::RoaringBitmap, roaring::RoaringBitmap,
@@ -28,7 +26,8 @@ use tokio::{
fs::File, fs::File,
io::{AsyncReadExt, BufReader}, 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}; 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 reader = OpReader::new(path).await;
let mut account_id = u32::MAX; let mut account_id = u32::MAX;
let mut document_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 mut family = Family::None;
let email_collection = u8::from(Collection::Email);
let mut due = now(); let mut due = now();
let mut batch_size = 0; 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); batch.with_account_id(account_id);
} }
Op::Collection(c) => { Op::Collection(c) => {
collection = c; collection_raw = c;
collection = Collection::from(c);
batch.with_collection(collection); batch.with_collection(collection);
} }
Op::DocumentId(d) => { Op::DocumentId(d) => {
@@ -99,8 +99,8 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
.as_slice() .as_slice()
.deserialize_u8(0) .deserialize_u8(0)
.expect("Failed to deserialize field"); .expect("Failed to deserialize field");
if collection == u8::from(Collection::Mailbox) if collection == Collection::Mailbox
&& u8::from(Property::EmailIds) == field && u8::from(MailboxField::UidCounter) == field
{ {
batch.add( batch.add(
ValueClass::Property(field), 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"); let hash = BlobHash::try_from_hash_slice(&key).expect("Invalid blob hash");
if account_id != u32::MAX && document_id != u32::MAX { 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( batch.set(
ValueClass::TaskQueue(TaskQueueClass::IndexEmail { ValueClass::TaskQueue(TaskQueueClass::IndexEmail {
due, due,
@@ -301,7 +301,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
), ),
}, },
4 => { 4 => {
if reader.version == 1 && collection == email_collection { if reader.version == 1 && collection == Collection::Email {
continue; continue;
} }
@@ -357,7 +357,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
subspace: SUBSPACE_LOGS, subspace: SUBSPACE_LOGS,
key: LogKey { key: LogKey {
account_id, account_id,
collection, collection: collection_raw,
change_id, change_id,
} }
.serialize(0), .serialize(0),

View File

@@ -4,116 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use crate::{Server, auth::AccessToken}; use crate::Server;
use directory::{ use directory::{
QueryParams, Type, Type,
backend::internal::{ backend::internal::{PrincipalField, manage::ChangedPrincipals},
PrincipalField,
manage::{ChangedPrincipals, ManageDirectory},
},
}; };
use jmap_proto::{ use types::acl::{AclGrant, ArchivedAclGrant};
error::set::SetError,
types::{
acl::Acl,
property::Property,
value::{AclGrant, ArchivedAclGrant, MaybePatchValue, Value},
},
};
use utils::map::bitmap::Bitmap;
impl Server { 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]>) { pub async fn refresh_acls(&self, acl_changes: &[AclGrant], current: Option<&[AclGrant]>) {
let mut changed_principals = ChangedPrincipals::new(); let mut changed_principals = ChangedPrincipals::new();
if let Some(acl_current) = current { if let Some(acl_current) = current {
@@ -205,77 +103,4 @@ impl Server {
self.invalidate_principal_caches(changed_principals).await; 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."))
}
}
} }

View File

@@ -4,13 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 store::{ValueKey, query::acl::AclQuery, roaring::RoaringBitmap, write::ValueClass};
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::Collection};
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
use crate::{Server, auth::AccessToken};
impl Server { impl Server {
pub async fn shared_containers( pub async fn shared_containers(
&self, &self,

View File

@@ -5,11 +5,8 @@
*/ */
use crate::auth::AccessToken; use crate::auth::AccessToken;
use jmap_proto::types::{
acl::Acl,
value::{AclGrant, ArchivedAclGrant},
};
use rkyv::vec::ArchivedVec; use rkyv::vec::ArchivedVec;
use types::acl::{Acl, AclGrant, ArchivedAclGrant};
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
pub mod acl; pub mod acl;

View File

@@ -5,8 +5,8 @@
*/ */
use crate::{DavResources, auth::AccessToken}; use crate::{DavResources, auth::AccessToken};
use jmap_proto::types::acl::Acl;
use store::roaring::RoaringBitmap; use store::roaring::RoaringBitmap;
use types::acl::Acl;
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
impl DavResources { impl DavResources {

View File

@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use jmap_proto::types::blob::BlobSection; use crate::Server;
use mail_parser::{ use mail_parser::{
Encoding, Encoding,
decoders::{base64::base64_decode, quoted_printable::quoted_printable_decode}, decoders::{base64::base64_decode, quoted_printable::quoted_printable_decode},
}; };
use utils::BlobHash; use types::{blob::BlobSection, blob_hash::BlobHash};
use crate::Server;
impl Server { impl Server {
pub async fn get_blob_section( pub async fn get_blob_section(

View File

@@ -4,8 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use crate::auth::AsTenantId;
use ahash::AHashSet; use ahash::AHashSet;
use jmap_proto::types::{property::Property, value::AclGrant};
use rkyv::{ use rkyv::{
option::ArchivedOption, option::ArchivedOption,
primitive::{ArchivedU32, ArchivedU64}, primitive::{ArchivedU32, ArchivedU64},
@@ -16,22 +16,20 @@ use store::{
Serialize, SerializeInfallible, Serialize, SerializeInfallible,
write::{Archive, Archiver, BatchBuilder, BlobOp, DirectoryClass, IntoOperations, TagValue}, write::{Archive, Archiver, BatchBuilder, BlobOp, DirectoryClass, IntoOperations, TagValue},
}; };
use utils::BlobHash; use types::{acl::AclGrant, blob_hash::BlobHash, collection::SyncCollection, field::Field};
use crate::auth::AsTenantId;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum IndexValue<'x> { pub enum IndexValue<'x> {
Index { Index {
field: u8, field: Field,
value: IndexItem<'x>, value: IndexItem<'x>,
}, },
IndexList { IndexList {
field: u8, field: Field,
value: Vec<IndexItem<'x>>, value: Vec<IndexItem<'x>>,
}, },
Tag { Tag {
field: u8, field: Field,
value: Vec<TagValue>, value: Vec<TagValue>,
}, },
Blob { Blob {
@@ -41,14 +39,14 @@ pub enum IndexValue<'x> {
used: u32, used: u32,
}, },
LogContainer { LogContainer {
sync_collection: u8, sync_collection: SyncCollection,
}, },
LogContainerProperty { LogContainerProperty {
sync_collection: u8, sync_collection: SyncCollection,
ids: Vec<u32>, ids: Vec<u32>,
}, },
LogItem { LogItem {
sync_collection: u8, sync_collection: SyncCollection,
prefix: Option<u32>, prefix: Option<u32>,
}, },
Acl { Acl {
@@ -297,14 +295,14 @@ impl<C: IndexableObject, N: IndexableAndSerializableObject> IntoOperations
} }
if N::is_versioned() { if N::is_versioned() {
let (offset, bytes) = Archiver::new(changes).serialize_versioned()?; let (offset, bytes) = Archiver::new(changes).serialize_versioned()?;
batch.set_versioned(Property::Value, bytes, offset); batch.set_versioned(Field::ARCHIVE, bytes, offset);
} else { } else {
batch.set(Property::Value, Archiver::new(changes).serialize()?); batch.set(Field::ARCHIVE, Archiver::new(changes).serialize()?);
} }
} }
(Some(current), Some(changes)) => { (Some(current), Some(changes)) => {
// Update // Update
batch.assert_value(Property::Value, &current); batch.assert_value(Field::ARCHIVE, &current);
for (current, change) in current.inner.index_values().zip(changes.index_values()) { for (current, change) in current.inner.index_values().zip(changes.index_values()) {
if current != change { if current != change {
merge_index(batch, current, change, self.tenant_id)?; merge_index(batch, current, change, self.tenant_id)?;
@@ -325,19 +323,19 @@ impl<C: IndexableObject, N: IndexableAndSerializableObject> IntoOperations
} }
if N::is_versioned() { if N::is_versioned() {
let (offset, bytes) = Archiver::new(changes).serialize_versioned()?; let (offset, bytes) = Archiver::new(changes).serialize_versioned()?;
batch.set_versioned(Property::Value, bytes, offset); batch.set_versioned(Field::ARCHIVE, bytes, offset);
} else { } else {
batch.set(Property::Value, Archiver::new(changes).serialize()?); batch.set(Field::ARCHIVE, Archiver::new(changes).serialize()?);
} }
} }
(Some(current), None) => { (Some(current), None) => {
// Deletion // Deletion
batch.assert_value(Property::Value, &current); batch.assert_value(Field::ARCHIVE, &current);
for item in current.inner.index_values() { for item in current.inner.index_values() {
build_index(batch, item, self.tenant_id, false); build_index(batch, item, self.tenant_id, false);
} }
batch.clear(Property::Value); batch.clear(Field::ARCHIVE);
} }
(None, None) => unreachable!(), (None, None) => unreachable!(),
} }

View File

@@ -4,11 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 crate::{IPC_CHANNEL_BUFFER, Server, ipc::StateEvent};
use tokio::sync::mpsc;
use types::type_state::{DataType, StateChange};
use utils::map::bitmap::Bitmap;
impl Server { impl Server {
pub async fn subscribe_state_manager( pub async fn subscribe_state_manager(

View File

@@ -12,7 +12,7 @@ utils = { path = "../utils" }
groupware = { path = "../groupware" } groupware = { path = "../groupware" }
directory = { path = "../directory" } directory = { path = "../directory" }
http_proto = { path = "../http-proto" } http_proto = { path = "../http-proto" }
jmap_proto = { path = "../jmap-proto" } types = { path = "../types" }
trc = { path = "../trc" } trc = { path = "../trc" }
calcard = { version = "0.1.3", features = ["rkyv"] } calcard = { version = "0.1.3", features = ["rkyv"] }
hashify = { version = "0.2" } hashify = { version = "0.2" }

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 calcard::common::timezone::Tz;
use common::{DavName, Server, auth::AccessToken}; use common::{DavName, Server, auth::AccessToken};
use dav_proto::{Depth, RequestHeaders}; use dav_proto::{Depth, RequestHeaders};
@@ -14,23 +23,12 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{ use store::write::{BatchBuilder, now};
use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection}, 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 { pub(crate) trait CalendarCopyMoveRequestHandler: Sync + Send {
fn handle_calendar_copy_move_request( fn handle_calendar_copy_move_request(

View File

@@ -22,12 +22,12 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{ use store::write::BatchBuilder;
use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::{Collection, SyncCollection}, collection::{Collection, SyncCollection},
}; };
use store::write::BatchBuilder;
use trc::AddContext;
pub(crate) trait CalendarDeleteRequestHandler: Sync + Send { pub(crate) trait CalendarDeleteRequestHandler: Sync + Send {
fn handle_calendar_delete_request( fn handle_calendar_delete_request(

View File

@@ -24,16 +24,16 @@ use dav_proto::{
use groupware::{cache::GroupwareCache, calendar::CalendarEvent}; use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use std::str::FromStr; use std::str::FromStr;
use store::{ use store::{
ahash::AHashMap, ahash::AHashMap,
write::{now, serialize::rkyv_deserialize}, write::{now, serialize::rkyv_deserialize},
}; };
use trc::AddContext; use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CalendarFreebusyRequestHandler: Sync + Send { pub(crate) trait CalendarFreebusyRequestHandler: Sync + Send {
fn handle_calendar_freebusy_request( fn handle_calendar_freebusy_request(

View File

@@ -4,17 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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::{ use crate::{
DavError, DavMethod, DavError, DavMethod,
common::{ common::{
@@ -23,6 +12,16 @@ use crate::{
uri::DavUriResource, 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 { pub(crate) trait CalendarGetRequestHandler: Sync + Send {
fn handle_calendar_get_request( fn handle_calendar_get_request(

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken};
use dav_proto::{ use dav_proto::{
RequestHeaders, Return, RequestHeaders, Return,
@@ -15,20 +24,9 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::{Collection, SyncCollection};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::collection::{Collection, SyncCollection};
use crate::{
DavError, DavMethod, PropStatBuilder,
common::{
ExtractETag,
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
};
use super::proppatch::CalendarPropPatchRequestHandler;
pub(crate) trait CalendarMkColRequestHandler: Sync + Send { pub(crate) trait CalendarMkColRequestHandler: Sync + Send {
fn handle_calendar_mkcol_request( fn handle_calendar_mkcol_request(

View File

@@ -15,16 +15,15 @@ pub mod scheduling;
pub mod update; pub mod update;
use crate::{DavError, DavErrorCondition}; use crate::{DavError, DavErrorCondition};
use common::IDX_UID;
use common::{DavResources, Server}; use common::{DavResources, Server};
use dav_proto::schema::{ use dav_proto::schema::{
property::{CalDavProperty, CalendarData, DavProperty, WebDavProperty}, property::{CalDavProperty, CalendarData, DavProperty, WebDavProperty},
response::CalCondition, response::CalCondition,
}; };
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use store::query::Filter; use store::query::Filter;
use trc::AddContext; use trc::AddContext;
use types::{collection::Collection, field::CalendarField};
pub(crate) static CALENDAR_CONTAINER_PROPS: [DavProperty; 31] = [ pub(crate) static CALENDAR_CONTAINER_PROPS: [DavProperty; 31] = [
DavProperty::WebDav(WebDavProperty::CreationDate), DavProperty::WebDav(WebDavProperty::CreationDate),
@@ -101,7 +100,7 @@ pub(crate) async fn assert_is_unique_uid(
.filter( .filter(
account_id, account_id,
Collection::CalendarEvent, Collection::CalendarEvent,
vec![Filter::eq(IDX_UID, uid.as_bytes().to_vec())], vec![Filter::eq(CalendarField::Uid, uid.as_bytes().to_vec())],
) )
.await .await
.caused_by(trc::location!())?; .caused_by(trc::location!())?;

View File

@@ -29,13 +29,13 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use std::str::FromStr; use std::str::FromStr;
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CalendarPropPatchRequestHandler: Sync + Send { pub(crate) trait CalendarPropPatchRequestHandler: Sync + Send {
fn handle_calendar_proppatch_request( fn handle_calendar_proppatch_request(

View File

@@ -34,13 +34,13 @@ use dav_proto::{
use groupware::{cache::GroupwareCache, calendar::ArchivedCalendarEvent}; use groupware::{cache::GroupwareCache, calendar::ArchivedCalendarEvent};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
use std::{fmt::Write, slice::Iter, str::FromStr}; use std::{fmt::Write, slice::Iter, str::FromStr};
use store::{ use store::{
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
write::serialize::rkyv_deserialize, write::serialize::rkyv_deserialize,
}; };
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::SyncCollection};
pub(crate) trait CalendarQueryRequestHandler: Sync + Send { pub(crate) trait CalendarQueryRequestHandler: Sync + Send {
fn handle_calendar_query_request( fn handle_calendar_query_request(

View File

@@ -32,9 +32,9 @@ use dav_proto::{
use groupware::{DestroyArchive, cache::GroupwareCache, calendar::CalendarScheduling}; use groupware::{DestroyArchive, cache::GroupwareCache, calendar::CalendarScheduling};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::{Collection, SyncCollection};
use store::{ahash::AHashMap, write::BatchBuilder}; use store::{ahash::AHashMap, write::BatchBuilder};
use trc::AddContext; use trc::AddContext;
use types::collection::{Collection, SyncCollection};
use utils::sanitize_email; use utils::sanitize_email;
pub(crate) trait CalendarSchedulingHandler: Sync + Send { pub(crate) trait CalendarSchedulingHandler: Sync + Send {

View File

@@ -33,13 +33,13 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use std::collections::HashSet; use std::collections::HashSet;
use store::write::{BatchBuilder, now}; use store::write::{BatchBuilder, now};
use trc::AddContext; use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CalendarUpdateRequestHandler: Sync + Send { pub(crate) trait CalendarUpdateRequestHandler: Sync + Send {
fn handle_calendar_update_request( fn handle_calendar_update_request(

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{DavName, Server, auth::AccessToken};
use dav_proto::{Depth, RequestHeaders}; use dav_proto::{Depth, RequestHeaders};
use groupware::{ use groupware::{
@@ -13,23 +22,12 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{ use store::write::BatchBuilder;
use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection}, 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 { pub(crate) trait CardCopyMoveRequestHandler: Sync + Send {
fn handle_card_copy_move_request( fn handle_card_copy_move_request(

View File

@@ -4,6 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken, sharing::EffectiveAcl};
use dav_proto::RequestHeaders; use dav_proto::RequestHeaders;
use groupware::{ use groupware::{
@@ -13,20 +21,11 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::{
use crate::{ acl::Acl,
DavError, DavMethod, collection::{Collection, SyncCollection},
common::{
ETag,
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
}; };
pub(crate) trait CardDeleteRequestHandler: Sync + Send { pub(crate) trait CardDeleteRequestHandler: Sync + Send {

View File

@@ -4,17 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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::{ use crate::{
DavError, DavMethod, DavError, DavMethod,
common::{ common::{
@@ -23,6 +12,16 @@ use crate::{
uri::DavUriResource, 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 { pub(crate) trait CardGetRequestHandler: Sync + Send {
fn handle_card_get_request( fn handle_card_get_request(

View File

@@ -21,9 +21,9 @@ use dav_proto::{
use groupware::{cache::GroupwareCache, contact::AddressBook}; use groupware::{cache::GroupwareCache, contact::AddressBook};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::{Collection, SyncCollection};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::collection::{Collection, SyncCollection};
pub(crate) trait CardMkColRequestHandler: Sync + Send { pub(crate) trait CardMkColRequestHandler: Sync + Send {
fn handle_card_mkcol_request( fn handle_card_mkcol_request(

View File

@@ -4,18 +4,16 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use common::IDX_UID; use crate::{DavError, DavErrorCondition};
use common::{DavResources, Server}; use common::{DavResources, Server};
use dav_proto::schema::{ use dav_proto::schema::{
property::{CardDavProperty, DavProperty, WebDavProperty}, property::{CardDavProperty, DavProperty, WebDavProperty},
response::CardCondition, response::CardCondition,
}; };
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use store::query::Filter; use store::query::Filter;
use trc::AddContext; use trc::AddContext;
use types::{collection::Collection, field::ContactField};
use crate::{DavError, DavErrorCondition};
pub mod copy_move; pub mod copy_move;
pub mod delete; pub mod delete;
@@ -87,7 +85,7 @@ pub(crate) async fn assert_is_unique_uid(
.filter( .filter(
account_id, account_id,
Collection::ContactCard, Collection::ContactCard,
vec![Filter::eq(IDX_UID, uid.as_bytes().to_vec())], vec![Filter::eq(ContactField::Uid, uid.as_bytes().to_vec())],
) )
.await .await
.caused_by(trc::location!())?; .caused_by(trc::location!())?;

View File

@@ -4,6 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken};
use dav_proto::{ use dav_proto::{
RequestHeaders, Return, RequestHeaders, Return,
@@ -20,20 +28,11 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::{
use crate::{ acl::Acl,
DavError, DavMethod, PropStatBuilder, collection::{Collection, SyncCollection},
common::{
ETag, ExtractETag,
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
}; };
pub(crate) trait CardPropPatchRequestHandler: Sync + Send { pub(crate) trait CardPropPatchRequestHandler: Sync + Send {

View File

@@ -28,9 +28,9 @@ use dav_proto::{
use groupware::cache::GroupwareCache; use groupware::cache::GroupwareCache;
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
use std::fmt::Write; use std::fmt::Write;
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::SyncCollection};
pub(crate) trait CardQueryRequestHandler: Sync + Send { pub(crate) trait CardQueryRequestHandler: Sync + Send {
fn handle_card_query_request( fn handle_card_query_request(

View File

@@ -4,22 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use calcard::{Entry, Parser}; use super::assert_is_unique_uid;
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 crate::{ use crate::{
DavError, DavErrorCondition, DavMethod, DavError, DavErrorCondition, DavMethod,
common::{ common::{
@@ -30,8 +15,21 @@ use crate::{
file::DavFileResource, file::DavFileResource,
fix_percent_encoding, fix_percent_encoding,
}; };
use calcard::{Entry, Parser};
use super::assert_is_unique_uid; 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 { pub(crate) trait CardUpdateRequestHandler: Sync + Send {
fn handle_card_update_request( fn handle_card_update_request(

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use super::ArchivedResource;
use crate::{ use crate::{
DavError, DavErrorCondition, DavResourceName, common::uri::DavUriResource, DavError, DavErrorCondition, DavResourceName, common::uri::DavUriResource,
principal::propfind::PrincipalPropFind, principal::propfind::PrincipalPropFind,
@@ -22,18 +23,15 @@ use groupware::RFC_3986;
use groupware::{cache::GroupwareCache, calendar::Calendar, contact::AddressBook, file::FileNode}; use groupware::{cache::GroupwareCache, calendar::Calendar, contact::AddressBook, file::FileNode};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::Collection,
value::{AclGrant, ArchivedAclGrant},
};
use rkyv::vec::ArchivedVec; use rkyv::vec::ArchivedVec;
use store::{ahash::AHashSet, roaring::RoaringBitmap, write::BatchBuilder}; use store::{ahash::AHashSet, roaring::RoaringBitmap, write::BatchBuilder};
use trc::AddContext; use trc::AddContext;
use types::{
acl::{Acl, AclGrant, ArchivedAclGrant},
collection::Collection,
};
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
use super::ArchivedResource;
pub(crate) trait DavAclHandler: Sync + Send { pub(crate) trait DavAclHandler: Sync + Send {
fn handle_acl_request( fn handle_acl_request(
&self, &self,

View File

@@ -4,6 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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::KV_LOCK_DAV;
use common::{Server, auth::AccessToken}; use common::{Server, auth::AccessToken};
use dav_proto::schema::property::{ActiveLock, LockScope, WebDavProperty}; 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::schema::response::{BaseCondition, List, PropResponse};
use dav_proto::{Condition, Depth, Timeout}; use dav_proto::{Condition, Depth, Timeout};
use dav_proto::{RequestHeaders, schema::request::LockInfo}; use dav_proto::{RequestHeaders, schema::request::LockInfo};
use groupware::cache::GroupwareCache; use groupware::cache::GroupwareCache;
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use std::collections::HashMap; use std::collections::HashMap;
use store::dispatch::lookup::KeyValue; use store::dispatch::lookup::KeyValue;
use store::write::serialize::rkyv_deserialize; use store::write::serialize::rkyv_deserialize;
use store::write::{AlignedBytes, Archive, Archiver, now}; use store::write::{AlignedBytes, Archive, Archiver, now};
use store::{Serialize, U32_LEN}; use store::{Serialize, U32_LEN};
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
use super::ETag;
use super::uri::{DavUriResource, OwnedUri, UriResource, Urn};
use crate::{DavError, DavErrorCondition, DavMethod};
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct ResourceState<'x> { pub struct ResourceState<'x> {

View File

@@ -27,10 +27,10 @@ use groupware::{
contact::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard}, contact::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard},
file::{ArchivedFileNode, FileNode}, file::{ArchivedFileNode, FileNode},
}; };
use jmap_proto::types::{collection::Collection, property::Property, value::ArchivedAclGrant};
use propfind::PropFindItem; use propfind::PropFindItem;
use rkyv::vec::ArchivedVec; use rkyv::vec::ArchivedVec;
use store::write::{AlignedBytes, Archive, BatchBuilder, Operation, ValueClass, ValueOp}; use store::write::{AlignedBytes, Archive, BatchBuilder, Operation, ValueClass, ValueOp};
use types::{acl::ArchivedAclGrant, collection::Collection, field::Field};
use uri::{OwnedUri, Urn}; use uri::{OwnedUri, Urn};
pub mod acl; pub mod acl;
@@ -109,7 +109,7 @@ impl<T> ETag for Archive<T> {
impl ExtractETag for BatchBuilder { impl ExtractETag for BatchBuilder {
fn etag(&self) -> Option<String> { 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() { for op in self.ops().iter().rev() {
match op { match op {
Operation::Value { Operation::Value {

View File

@@ -56,10 +56,6 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use std::sync::Arc; use std::sync::Arc;
use store::{ use store::{
ahash::AHashMap, ahash::AHashMap,
@@ -68,6 +64,10 @@ use store::{
write::{AlignedBytes, Archive}, write::{AlignedBytes, Archive},
}; };
use trc::AddContext; use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait PropFindRequestHandler: Sync + Send { pub(crate) trait PropFindRequestHandler: Sync + Send {
fn handle_propfind_request( fn handle_propfind_request(
@@ -1182,7 +1182,7 @@ async fn get(
SyncType::From { id, seq } => { SyncType::From { id, seq } => {
let changes = server let changes = server
.store() .store()
.changes(account_id, sync_collection, Query::Since(id)) .changes(account_id, sync_collection.into(), Query::Since(id))
.await .await
.caused_by(trc::location!())?; .caused_by(trc::location!())?;
let mut vanished: Vec<String> = Vec::new(); let mut vanished: Vec<String> = Vec::new();
@@ -1258,7 +1258,7 @@ async fn get(
{ {
vanished = server vanished = server
.store() .store()
.vanished(account_id, vanished_collection, Query::Since(id)) .vanished(account_id, vanished_collection.into(), Query::Since(id))
.await .await
.caused_by(trc::location!())?; .caused_by(trc::location!())?;
total_changes += vanished.len(); total_changes += vanished.len();

View File

@@ -4,19 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use std::fmt::Display; use crate::{DavError, DavResourceName};
use common::{Server, auth::AccessToken}; use common::{Server, auth::AccessToken};
use directory::backend::internal::manage::ManageDirectory; use directory::backend::internal::manage::ManageDirectory;
use groupware::cache::GroupwareCache; use groupware::cache::GroupwareCache;
use http_proto::request::decode_path_element; use http_proto::request::decode_path_element;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection; use std::fmt::Display;
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
use crate::{DavError, DavResourceName};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct UriResource<A, R> { pub(crate) struct UriResource<A, R> {

View File

@@ -21,16 +21,16 @@ use dav_proto::{Depth, RequestHeaders};
use groupware::{DestroyArchive, cache::GroupwareCache, file::FileNode}; use groupware::{DestroyArchive, cache::GroupwareCache, file::FileNode};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection},
};
use std::sync::Arc; use std::sync::Arc;
use store::{ use store::{
ahash::AHashMap, ahash::AHashMap,
write::{BatchBuilder, now}, write::{BatchBuilder, now},
}; };
use trc::AddContext; use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection},
};
pub(crate) trait FileCopyMoveRequestHandler: Sync + Send { pub(crate) trait FileCopyMoveRequestHandler: Sync + Send {
fn handle_file_copy_move_request( fn handle_file_copy_move_request(

View File

@@ -16,8 +16,8 @@ use dav_proto::RequestHeaders;
use groupware::{DestroyArchive, cache::GroupwareCache}; use groupware::{DestroyArchive, cache::GroupwareCache};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{acl::Acl, collection::SyncCollection};
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::SyncCollection};
pub(crate) trait FileDeleteRequestHandler: Sync + Send { pub(crate) trait FileDeleteRequestHandler: Sync + Send {
fn handle_file_delete_request( fn handle_file_delete_request(

View File

@@ -9,11 +9,11 @@ use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
use groupware::{cache::GroupwareCache, file::FileNode}; use groupware::{cache::GroupwareCache, file::FileNode};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{ use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::{Collection, SyncCollection}, collection::{Collection, SyncCollection},
}; };
use trc::AddContext;
use crate::{ use crate::{
DavError, DavMethod, DavError, DavMethod,

View File

@@ -23,12 +23,12 @@ use dav_proto::{
use groupware::{cache::GroupwareCache, file::FileNode}; use groupware::{cache::GroupwareCache, file::FileNode};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{ use store::write::{BatchBuilder, now};
use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::{Collection, SyncCollection}, collection::{Collection, SyncCollection},
}; };
use store::write::{BatchBuilder, now};
use trc::AddContext;
pub(crate) trait FileMkColRequestHandler: Sync + Send { pub(crate) trait FileMkColRequestHandler: Sync + Send {
fn handle_file_mkcol_request( fn handle_file_mkcol_request(

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken, sharing::EffectiveAcl};
use dav_proto::{ use dav_proto::{
RequestHeaders, Return, RequestHeaders, Return,
@@ -16,21 +25,11 @@ use dav_proto::{
use groupware::{cache::GroupwareCache, file::FileNode}; use groupware::{cache::GroupwareCache, file::FileNode};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::{
use crate::{ acl::Acl,
DavError, DavMethod, PropStatBuilder, collection::{Collection, SyncCollection},
common::{
ETag, ExtractETag,
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
file::DavFileResource,
}; };
pub(crate) trait FilePropPatchRequestHandler: Sync + Send { pub(crate) trait FilePropPatchRequestHandler: Sync + Send {

View File

@@ -24,13 +24,13 @@ use groupware::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use store::write::{BatchBuilder, now}; use store::write::{BatchBuilder, now};
use trc::AddContext; use trc::AddContext;
use utils::BlobHash; use types::{
acl::Acl,
blob_hash::BlobHash,
collection::{Collection, SyncCollection},
};
pub(crate) trait FileUpdateRequestHandler: Sync + Send { pub(crate) trait FileUpdateRequestHandler: Sync + Send {
fn handle_file_update_request( fn handle_file_update_request(

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken};
use dav_proto::{ use dav_proto::{
RequestHeaders, RequestHeaders,
@@ -15,19 +24,8 @@ use dav_proto::{
}; };
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use store::roaring::RoaringBitmap; use store::roaring::RoaringBitmap;
use types::collection::Collection;
use crate::{
DavError,
common::{
DavQuery, DavQueryResource,
propfind::PropFindRequestHandler,
uri::{DavUriResource, UriResource},
},
};
use super::propfind::PrincipalPropFind;
pub(crate) trait PrincipalMatching: Sync + Send { pub(crate) trait PrincipalMatching: Sync + Send {
fn handle_principal_match( fn handle_principal_match(

View File

@@ -23,9 +23,9 @@ use directory::{QueryParams, Type, backend::internal::manage::ManageDirectory};
use groupware::RFC_3986; use groupware::RFC_3986;
use groupware::cache::GroupwareCache; use groupware::cache::GroupwareCache;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use std::borrow::Cow; use std::borrow::Cow;
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
pub(crate) trait PrincipalPropFind: Sync + Send { pub(crate) trait PrincipalPropFind: Sync + Send {
fn prepare_principal_propfind_response( fn prepare_principal_propfind_response(

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use super::propfind::PrincipalPropFind;
use common::{ use common::{
Server, Server,
auth::{AccessToken, AsTenantId}, auth::{AccessToken, AsTenantId},
@@ -16,11 +17,9 @@ use dav_proto::schema::{
use directory::{Type, backend::internal::manage::ManageDirectory}; use directory::{Type, backend::internal::manage::ManageDirectory};
use http_proto::HttpResponse; use http_proto::HttpResponse;
use hyper::StatusCode; use hyper::StatusCode;
use jmap_proto::types::collection::Collection;
use store::roaring::RoaringBitmap; use store::roaring::RoaringBitmap;
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
use super::propfind::PrincipalPropFind;
pub(crate) trait PrincipalPropSearch: Sync + Send { pub(crate) trait PrincipalPropSearch: Sync + Send {
fn handle_principal_property_search( fn handle_principal_property_search(

View File

@@ -50,9 +50,9 @@ use dav_proto::{
use directory::Permission; use directory::Permission;
use http_proto::{HttpRequest, HttpResponse, HttpSessionData, request::fetch_body}; use http_proto::{HttpRequest, HttpResponse, HttpSessionData, request::fetch_body};
use hyper::{StatusCode, header}; use hyper::{StatusCode, header};
use jmap_proto::types::collection::Collection;
use std::{sync::Arc, time::Instant}; use std::{sync::Arc, time::Instant};
use trc::{EventType, LimitEvent, StoreEvent, WebDavEvent}; use trc::{EventType, LimitEvent, StoreEvent, WebDavEvent};
use types::collection::Collection;
pub trait DavRequestHandler: Sync + Send { pub trait DavRequestHandler: Sync + Send {
fn handle_dav_request( fn handle_dav_request(

View File

@@ -10,7 +10,7 @@ proc_macros = { path = "../utils/proc-macros" }
store = { path = "../store" } store = { path = "../store" }
trc = { path = "../trc" } trc = { path = "../trc" }
nlp = { path = "../nlp" } nlp = { path = "../nlp" }
jmap_proto = { path = "../jmap-proto" } types = { path = "../types" }
smtp-proto = { version = "0.2" } smtp-proto = { version = "0.2" }
mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] } mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] }
mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }

View File

@@ -15,7 +15,6 @@ use crate::{
}; };
use ahash::{AHashMap, AHashSet}; use ahash::{AHashMap, AHashSet};
use compact_str::CompactString; use compact_str::CompactString;
use jmap_proto::types::collection::Collection;
use nlp::tokenizers::word::WordTokenizer; use nlp::tokenizers::word::WordTokenizer;
use store::{ use store::{
Deserialize, IterateParams, Serialize, SerializeInfallible, Store, U32_LEN, ValueKey, Deserialize, IterateParams, Serialize, SerializeInfallible, Store, U32_LEN, ValueKey,
@@ -27,6 +26,7 @@ use store::{
}, },
}; };
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
use utils::sanitize_email; use utils::sanitize_email;
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] #[derive(Debug, Default, serde::Serialize, serde::Deserialize)]

View File

@@ -9,7 +9,7 @@ utils = { path = "../utils" }
nlp = { path = "../nlp" } nlp = { path = "../nlp" }
store = { path = "../store" } store = { path = "../store" }
trc = { path = "../trc" } trc = { path = "../trc" }
jmap_proto = { path = "../jmap-proto" } types = { path = "../types" }
common = { path = "../common" } common = { path = "../common" }
directory = { path = "../directory" } directory = { path = "../directory" }
groupware = { path = "../groupware" } groupware = { path = "../groupware" }

View File

@@ -9,13 +9,13 @@ use common::{
MessageCache, MessageStoreCache, MessageUidCache, MessagesCache, Server, auth::AccessToken, MessageCache, MessageStoreCache, MessageUidCache, MessagesCache, Server, auth::AccessToken,
sharing::EffectiveAcl, sharing::EffectiveAcl,
}; };
use jmap_proto::types::{ use store::{ahash::AHashMap, roaring::RoaringBitmap, write::Archive};
use trc::AddContext;
use types::{
acl::Acl, acl::Acl,
collection::Collection, collection::Collection,
keyword::{Keyword, OTHER}, keyword::{Keyword, OTHER},
}; };
use store::{ahash::AHashMap, roaring::RoaringBitmap, write::Archive};
use trc::AddContext;
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
pub(crate) async fn update_email_cache( pub(crate) async fn update_email_cache(

View File

@@ -9,9 +9,12 @@ use common::{
MailboxCache, MailboxesCache, MessageStoreCache, Server, auth::AccessToken, MailboxCache, MailboxesCache, MessageStoreCache, Server, auth::AccessToken,
config::jmap::settings::SpecialUse, sharing::EffectiveAcl, config::jmap::settings::SpecialUse, sharing::EffectiveAcl,
}; };
use jmap_proto::types::{acl::Acl, collection::Collection, value::AclGrant};
use store::{ahash::AHashMap, roaring::RoaringBitmap}; use store::{ahash::AHashMap, roaring::RoaringBitmap};
use trc::AddContext; use trc::AddContext;
use types::{
acl::{Acl, AclGrant},
collection::Collection,
};
use utils::{map::bitmap::Bitmap, topological::TopologicalSort}; use utils::{map::bitmap::Bitmap, topological::TopologicalSort};
pub(crate) async fn update_mailbox_cache( pub(crate) async fn update_mailbox_cache(

View File

@@ -4,18 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{CacheSwap, MessageStoreCache, Server};
use email::{full_email_cache_build, update_email_cache}; 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 mailbox::{full_mailbox_cache_build, update_mailbox_cache};
use std::{collections::hash_map::Entry, sync::Arc, time::Instant};
use store::{ use store::{
ahash::AHashMap, ahash::AHashMap,
query::log::{Change, Query}, query::log::{Change, Query},
}; };
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use trc::{AddContext, StoreEvent}; use trc::{AddContext, StoreEvent};
use types::collection::SyncCollection;
pub mod email; pub mod email;
pub mod mailbox; pub mod mailbox;
@@ -70,7 +69,7 @@ impl MessageCacheFetch for Server {
.data .data
.changes( .changes(
account_id, account_id,
SyncCollection::Email, SyncCollection::Email.into(),
Query::Since(cache.last_change_id), Query::Since(cache.last_change_id),
) )
.await .await
@@ -203,7 +202,7 @@ async fn full_cache_build(
.core .core
.storage .storage
.data .data
.get_last_change_id(account_id, SyncCollection::Email) .get_last_change_id(account_id, SyncCollection::Email.into())
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.unwrap_or_default(); .unwrap_or_default();

View File

@@ -4,15 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 super::{ArchivedIdentity, Identity};
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
use types::collection::SyncCollection;
impl IndexableObject for Identity { impl IndexableObject for Identity {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[IndexValue::LogItem { [IndexValue::LogItem {
sync_collection: SyncCollection::Identity.into(), sync_collection: SyncCollection::Identity,
prefix: None, prefix: None,
}] }]
.into_iter() .into_iter()
@@ -22,7 +21,7 @@ impl IndexableObject for Identity {
impl IndexableObject for &ArchivedIdentity { impl IndexableObject for &ArchivedIdentity {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[IndexValue::LogItem { [IndexValue::LogItem {
sync_collection: SyncCollection::Identity.into(), sync_collection: SyncCollection::Identity,
prefix: None, prefix: None,
}] }]
.into_iter() .into_iter()

View File

@@ -13,12 +13,9 @@ use common::{
Server, auth::AccessToken, sharing::EffectiveAcl, storage::index::ObjectIndexBuilder, Server, auth::AccessToken, sharing::EffectiveAcl, storage::index::ObjectIndexBuilder,
}; };
use directory::Permission; use directory::Permission;
use jmap_proto::{
error::set::{SetError, SetErrorType},
types::{acl::Acl, collection::Collection, property::Property},
};
use store::{roaring::RoaringBitmap, write::BatchBuilder}; use store::{roaring::RoaringBitmap, write::BatchBuilder};
use trc::AddContext; use trc::AddContext;
use types::{acl::Acl, collection::Collection, field::MailboxField};
pub trait MailboxDestroy: Sync + Send { pub trait MailboxDestroy: Sync + Send {
fn mailbox_destroy( fn mailbox_destroy(
@@ -27,7 +24,16 @@ pub trait MailboxDestroy: Sync + Send {
document_id: u32, document_id: u32,
access_token: &AccessToken, access_token: &AccessToken,
remove_emails: bool, 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 { impl MailboxDestroy for Server {
@@ -37,24 +43,20 @@ impl MailboxDestroy for Server {
document_id: u32, document_id: u32,
access_token: &AccessToken, access_token: &AccessToken,
remove_emails: bool, remove_emails: bool,
) -> trc::Result<Result<Option<u64>, SetError>> { ) -> trc::Result<Result<Option<u64>, MailboxDestroyError>> {
// Internal folders cannot be deleted // Internal folders cannot be deleted
#[cfg(feature = "test_mode")] #[cfg(feature = "test_mode")]
if [INBOX_ID, TRASH_ID].contains(&document_id) if [INBOX_ID, TRASH_ID].contains(&document_id)
&& !access_token.has_permission(Permission::DeleteSystemFolders) && !access_token.has_permission(Permission::DeleteSystemFolders)
{ {
return Ok(Err(SetError::forbidden().with_description( return Ok(Err(MailboxDestroyError::CannotDestroy));
"You are not allowed to delete Inbox, Junk or Trash folders.",
)));
} }
#[cfg(not(feature = "test_mode"))] #[cfg(not(feature = "test_mode"))]
if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id) if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id)
&& !access_token.has_permission(Permission::DeleteSystemFolders) && !access_token.has_permission(Permission::DeleteSystemFolders)
{ {
return Ok(Err(SetError::forbidden().with_description( return Ok(Err(MailboxDestroyError::CannotDestroy));
"You are not allowed to delete Inbox, Junk or Trash folders.",
)));
} }
// Verify that this mailbox does not have sub-mailboxes // Verify that this mailbox does not have sub-mailboxes
@@ -68,8 +70,7 @@ impl MailboxDestroy for Server {
.iter() .iter()
.any(|item| item.parent_id == document_id) .any(|item| item.parent_id == document_id)
{ {
return Ok(Err(SetError::new(SetErrorType::MailboxHasChild) return Ok(Err(MailboxDestroyError::HasChildren));
.with_description("Mailbox has at least one children.")));
} }
// Verify that the mailbox is empty // Verify that the mailbox is empty
@@ -142,8 +143,7 @@ impl MailboxDestroy for Server {
.await?; .await?;
} }
} else { } else {
return Ok(Err(SetError::new(SetErrorType::MailboxHasEmail) return Ok(Err(MailboxDestroyError::HasEmails));
.with_description("Mailbox is not empty.")));
} }
} }
@@ -159,26 +159,22 @@ impl MailboxDestroy for Server {
// Validate ACLs // Validate ACLs
if access_token.is_shared(account_id) { if access_token.is_shared(account_id) {
let acl = mailbox.inner.acls.effective_acl(access_token); let acl = mailbox.inner.acls.effective_acl(access_token);
if !acl.contains(Acl::Administer) { if !acl.contains(Acl::Administer)
if !acl.contains(Acl::Delete) { && (!acl.contains(Acl::Delete)
return Ok(Err(SetError::forbidden() || (remove_emails && !acl.contains(Acl::RemoveItems)))
.with_description("You are not allowed to delete this mailbox."))); {
} else if remove_emails && !acl.contains(Acl::RemoveItems) { return Ok(Err(MailboxDestroyError::Forbidden));
return Ok(Err(SetError::forbidden().with_description(
"You are not allowed to delete emails from this mailbox.",
)));
}
} }
} }
batch batch
.with_account_id(account_id) .with_account_id(account_id)
.with_collection(Collection::Mailbox) .with_collection(Collection::Mailbox)
.delete_document(document_id) .delete_document(document_id)
.clear(Property::EmailIds) .clear(MailboxField::UidCounter)
.custom(ObjectIndexBuilder::<_, ()>::new().with_current(mailbox)) .custom(ObjectIndexBuilder::<_, ()>::new().with_current(mailbox))
.caused_by(trc::location!())?; .caused_by(trc::location!())?;
} else { } else {
return Ok(Err(SetError::not_found())); return Ok(Err(MailboxDestroyError::NotFound));
}; };
if !batch.is_empty() { if !batch.is_empty() {
@@ -188,11 +184,9 @@ impl MailboxDestroy for Server {
.and_then(|ids| ids.last_change_id(account_id)) .and_then(|ids| ids.last_change_id(account_id))
{ {
Ok(change_id) => Ok(Ok(Some(change_id))), Ok(change_id) => Ok(Ok(Some(change_id))),
Err(err) if err.is_assertion_failure() => Ok(Err(SetError::forbidden() Err(err) if err.is_assertion_failure() => {
.with_description(concat!( Ok(Err(MailboxDestroyError::AssertionFailed))
"Another process modified a message in this mailbox ", }
"while deleting it, please try again."
)))),
Err(err) => Err(err.caused_by(trc::location!())), Err(err) => Err(err.caused_by(trc::location!())),
} }
} else { } else {

View File

@@ -4,16 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 super::{ArchivedMailbox, Mailbox};
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
use types::{acl::AclGrant, collection::SyncCollection};
impl IndexableObject for Mailbox { impl IndexableObject for Mailbox {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
}, },
IndexValue::Acl { IndexValue::Acl {
value: (&self.acls).into(), value: (&self.acls).into(),
@@ -27,7 +26,7 @@ impl IndexableObject for &ArchivedMailbox {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
}, },
IndexValue::Acl { IndexValue::Acl {
value: self value: self

View File

@@ -7,10 +7,10 @@
use super::*; use super::*;
use crate::cache::MessageCacheFetch; use crate::cache::MessageCacheFetch;
use common::{Server, config::jmap::settings::SpecialUse, storage::index::ObjectIndexBuilder}; use common::{Server, config::jmap::settings::SpecialUse, storage::index::ObjectIndexBuilder};
use jmap_proto::types::collection::Collection;
use std::future::Future; use std::future::Future;
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::collection::Collection;
pub trait MailboxFnc: Sync + Send { pub trait MailboxFnc: Sync + Send {
fn create_system_folders( fn create_system_folders(

View File

@@ -5,7 +5,7 @@
*/ */
use common::config::jmap::settings::SpecialUse; use common::config::jmap::settings::SpecialUse;
use jmap_proto::types::value::AclGrant; use types::acl::AclGrant;
pub mod destroy; pub mod destroy;
pub mod index; pub mod index;

View File

@@ -4,19 +4,16 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use std::future::Future; use super::metadata::MessageMetadata;
use common::Server; use common::Server;
use jmap_proto::types::{collection::Collection, property::Property};
use mail_parser::Message; use mail_parser::Message;
use spam_filter::{ use spam_filter::{
SpamFilterInput, analysis::init::SpamFilterInit, modules::bayes::BayesClassifier, SpamFilterInput, analysis::init::SpamFilterInit, modules::bayes::BayesClassifier,
}; };
use std::future::Future;
use store::write::{TaskQueueClass, now}; use store::write::{TaskQueueClass, now};
use trc::StoreEvent; use trc::StoreEvent;
use utils::BlobHash; use types::{blob_hash::BlobHash, collection::Collection, field::EmailField};
use super::metadata::MessageMetadata;
pub trait EmailBayesTrain: Sync + Send { pub trait EmailBayesTrain: Sync + Send {
fn email_bayes_train( fn email_bayes_train(
@@ -63,7 +60,7 @@ impl EmailBayesTrain for Server {
account_id, account_id,
Collection::Email, Collection::Email,
document_id, document_id,
Property::BodyStructure, EmailField::Metadata.into(),
) )
.await? .await?
.ok_or_else(|| { .ok_or_else(|| {

View File

@@ -11,23 +11,20 @@ use super::{
}; };
use crate::mailbox::UidMailbox; use crate::mailbox::UidMailbox;
use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder}; 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 mail_parser::{HeaderName, HeaderValue, parsers::fields::thread::thread_name};
use store::{ use store::write::{BatchBuilder, TaskQueueClass, ValueClass, now};
BlobClass,
write::{BatchBuilder, TaskQueueClass, ValueClass, now},
};
use trc::AddContext; 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 { pub trait EmailCopy: Sync + Send {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -38,9 +35,9 @@ pub trait EmailCopy: Sync + Send {
resource_token: &ResourceToken, resource_token: &ResourceToken,
mailboxes: Vec<u32>, mailboxes: Vec<u32>,
keywords: Vec<Keyword>, keywords: Vec<Keyword>,
received_at: Option<UTCDate>, received_at: Option<u64>,
session_id: 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 { impl EmailCopy for Server {
@@ -52,9 +49,9 @@ impl EmailCopy for Server {
resource_token: &ResourceToken, resource_token: &ResourceToken,
mailboxes: Vec<u32>, mailboxes: Vec<u32>,
keywords: Vec<Keyword>, keywords: Vec<Keyword>,
received_at: Option<UTCDate>, received_at: Option<u64>,
session_id: u64, session_id: u64,
) -> trc::Result<Result<IngestedEmail, SetError>> { ) -> trc::Result<Result<IngestedEmail, CopyMessageError>> {
// Obtain metadata // Obtain metadata
let account_id = resource_token.account_id; let account_id = resource_token.account_id;
let mut metadata = if let Some(metadata) = self let mut metadata = if let Some(metadata) = self
@@ -62,7 +59,7 @@ impl EmailCopy for Server {
from_account_id, from_account_id,
Collection::Email, Collection::Email,
from_message_id, from_message_id,
Property::BodyStructure, EmailField::Metadata.into(),
) )
.await? .await?
{ {
@@ -70,10 +67,7 @@ impl EmailCopy for Server {
.deserialize::<MessageMetadata>() .deserialize::<MessageMetadata>()
.caused_by(trc::location!())? .caused_by(trc::location!())?
} else { } else {
return Ok(Err(SetError::not_found().with_description(format!( return Ok(Err(CopyMessageError::NotFound));
"Message not found not found in account {}.",
Id::from(from_account_id)
))));
}; };
// Check quota // Check quota
@@ -87,7 +81,7 @@ impl EmailCopy for Server {
|| err.matches(trc::EventType::Limit(trc::LimitEvent::TenantQuota)) || err.matches(trc::EventType::Limit(trc::LimitEvent::TenantQuota))
{ {
trc::error!(err.account_id(account_id).span_id(session_id)); trc::error!(err.account_id(account_id).span_id(session_id));
return Ok(Err(SetError::over_quota())); return Ok(Err(CopyMessageError::OverQuota));
} else { } else {
return Err(err); return Err(err);
} }
@@ -96,7 +90,7 @@ impl EmailCopy for Server {
// Set receivedAt // Set receivedAt
if let Some(received_at) = received_at { if let Some(received_at) = received_at {
metadata.received_at = received_at.timestamp() as u64; metadata.received_at = received_at;
} }
// Obtain threadId // Obtain threadId
@@ -226,7 +220,8 @@ impl EmailCopy for Server {
self.notify_task_queue(); self.notify_task_queue();
// Update response // 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.change_id = change_id;
email.blob_id = BlobId::new( email.blob_id = BlobId::new(
blob_hash, blob_hash,

View File

@@ -8,8 +8,6 @@ use super::metadata::MessageData;
use crate::{cache::MessageCacheFetch, mailbox::*, message::metadata::MessageMetadata}; use crate::{cache::MessageCacheFetch, mailbox::*, message::metadata::MessageMetadata};
use common::{KV_LOCK_PURGE_ACCOUNT, Server, storage::index::ObjectIndexBuilder}; use common::{KV_LOCK_PURGE_ACCOUNT, Server, storage::index::ObjectIndexBuilder};
use groupware::calendar::storage::ItipAutoExpunge; use groupware::calendar::storage::ItipAutoExpunge;
use jmap_proto::types::collection::VanishedCollection;
use jmap_proto::types::{collection::Collection, property::Property};
use std::future::Future; use std::future::Future;
use store::rand::prelude::SliceRandom; use store::rand::prelude::SliceRandom;
use store::write::key::DeserializeBigEndian; use store::write::key::DeserializeBigEndian;
@@ -21,7 +19,10 @@ use store::{
}; };
use store::{IndexKey, IterateParams, SerializeInfallible, U32_LEN}; use store::{IndexKey, IterateParams, SerializeInfallible, U32_LEN};
use trc::AddContext; 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 { pub trait EmailDeletion: Sync + Send {
fn emails_tombstone( fn emails_tombstone(
@@ -78,7 +79,7 @@ impl EmailDeletion for Server {
.update_document(document_id) .update_document(document_id)
.custom(ObjectIndexBuilder::<_, ()>::new().with_current(metadata)) .custom(ObjectIndexBuilder::<_, ()>::new().with_current(metadata))
.caused_by(trc::location!())? .caused_by(trc::location!())?
.tag(Property::MailboxIds, TagValue::Id(TOMBSTONE_ID)) .tag(EmailField::MailboxIds, TagValue::Id(TOMBSTONE_ID))
.commit_point(); .commit_point();
deleted_ids.insert(document_id); deleted_ids.insert(document_id);
@@ -211,14 +212,14 @@ impl EmailDeletion for Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: 0, document_id: 0,
field: Property::ReceivedAt.into(), field: EmailField::ReceivedAt.into(),
key: 0u64.serialize(), key: 0u64.serialize(),
}, },
IndexKey { IndexKey {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: u32::MAX, document_id: u32::MAX,
field: Property::ReceivedAt.into(), field: EmailField::ReceivedAt.into(),
key: now().saturating_sub(hold_period).serialize(), key: now().saturating_sub(hold_period).serialize(),
}, },
) )
@@ -269,7 +270,7 @@ impl EmailDeletion for Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
class: BitmapClass::Tag { class: BitmapClass::Tag {
field: Property::MailboxIds.into(), field: EmailField::MailboxIds.into(),
value: TagValue::Id(TOMBSTONE_ID), value: TagValue::Id(TOMBSTONE_ID),
}, },
document_id: 0, document_id: 0,
@@ -291,7 +292,7 @@ impl EmailDeletion for Server {
self.core self.core
.storage .storage
.fts .fts
.remove(account_id, Collection::Email.into(), &tombstoned_ids) .remove(account_id, Collection::Email, &tombstoned_ids)
.await?; .await?;
// Obtain tenant id // Obtain tenant id
@@ -310,8 +311,8 @@ impl EmailDeletion for Server {
batch batch
.with_collection(Collection::Email) .with_collection(Collection::Email)
.delete_document(document_id) .delete_document(document_id)
.clear(Property::Value) .clear(EmailField::Archive)
.untag(Property::MailboxIds, TagValue::Id(TOMBSTONE_ID)); .untag(EmailField::MailboxIds, TagValue::Id(TOMBSTONE_ID));
// Remove message metadata // Remove message metadata
if let Some(metadata_) = self if let Some(metadata_) = self
@@ -322,7 +323,7 @@ impl EmailDeletion for Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id, document_id,
class: ValueClass::Property(Property::BodyStructure.into()), class: ValueClass::Property(EmailField::Metadata.into()),
}) })
.await? .await?
{ {

View File

@@ -4,18 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::Server;
use directory::Permission; use directory::Permission;
use jmap_proto::types::{state::StateChange, type_state::DataType};
use mail_parser::MessageParser; use mail_parser::MessageParser;
use std::{borrow::Cow, future::Future}; use std::{borrow::Cow, future::Future};
use store::ahash::AHashMap; use store::ahash::AHashMap;
use utils::BlobHash; use types::{
blob_hash::BlobHash,
use crate::{mailbox::INBOX_ID, sieve::ingest::SieveScriptIngest}; type_state::{DataType, StateChange},
};
use super::ingest::{EmailIngest, IngestEmail, IngestSource};
#[derive(Debug)] #[derive(Debug)]
pub struct IngestMessage { pub struct IngestMessage {

View File

@@ -12,7 +12,6 @@ use super::metadata::{
MessageMetadata, MessageMetadataPart, MessageMetadata, MessageMetadataPart,
}; };
use common::storage::index::{IndexValue, IndexableObject, ObjectIndexBuilder}; use common::storage::index::{IndexValue, IndexableObject, ObjectIndexBuilder};
use jmap_proto::types::{collection::SyncCollection, property::Property};
use mail_parser::{ use mail_parser::{
Addr, Address, ArchivedAddress, ArchivedHeaderName, ArchivedHeaderValue, Group, HeaderName, Addr, Address, ArchivedAddress, ArchivedHeaderName, ArchivedHeaderValue, Group, HeaderName,
HeaderValue, HeaderValue,
@@ -29,7 +28,7 @@ use store::{
write::{Archiver, BatchBuilder, BlobOp, DirectoryClass}, write::{Archiver, BatchBuilder, BlobOp, DirectoryClass},
}; };
use trc::AddContext; 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_MESSAGE_PARTS: usize = 1000;
pub const MAX_ID_LENGTH: usize = 100; pub const MAX_ID_LENGTH: usize = 100;
@@ -53,14 +52,14 @@ impl MessageMetadata {
if set { if set {
// Serialize metadata // Serialize metadata
batch batch
.index(Property::Size, self.size.serialize()) .index(EmailField::Size, self.size.serialize())
.index(Property::ReceivedAt, (self.received_at).serialize()); .index(EmailField::ReceivedAt, (self.received_at).serialize());
} else { } else {
// Delete metadata // Delete metadata
batch batch
.clear(Property::BodyStructure) .clear(EmailField::Metadata)
.unindex(Property::Size, self.size.serialize()) .unindex(EmailField::Size, self.size.serialize())
.unindex(Property::ReceivedAt, (self.received_at).serialize()); .unindex(EmailField::ReceivedAt, (self.received_at).serialize());
} }
// Index properties // Index properties
@@ -76,9 +75,9 @@ impl MessageMetadata {
if self.has_attachments { if self.has_attachments {
if set { if set {
batch.tag(Property::HasAttachment, ()); batch.tag(EmailField::HasAttachment, ());
} else { } else {
batch.untag(Property::HasAttachment, ()); batch.untag(EmailField::HasAttachment, ());
} }
} }
@@ -100,7 +99,7 @@ impl MessageMetadata {
} }
if set { if set {
batch.set(Property::BodyStructure, Archiver::new(self).serialize()?); batch.set(EmailField::Metadata, Archiver::new(self).serialize()?);
} }
Ok(()) Ok(())
@@ -119,9 +118,9 @@ impl MessageMetadata {
// Add ids to inverted index // Add ids to inverted index
if id.len() < MAX_ID_LENGTH { if id.len() < MAX_ID_LENGTH {
if set { if set {
batch.index(Property::References, encode_message_id(id)); batch.index(EmailField::References, encode_message_id(id));
} else { } 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 // Add ids to inverted index
if id.len() < MAX_ID_LENGTH { if id.len() < MAX_ID_LENGTH {
if set { if set {
batch.index(Property::References, id.serialize()); batch.index(EmailField::References, id.serialize());
} else { } else {
batch.unindex(Property::References, id.serialize()); batch.unindex(EmailField::References, id.serialize());
} }
} }
}); });
@@ -161,9 +160,9 @@ impl MessageMetadata {
// Add address to inverted index // Add address to inverted index
if set { if set {
batch.index(u8::from(&property), sort_text.build()); batch.index(property, sort_text.build());
} else { } else {
batch.unindex(u8::from(&property), sort_text.build()); batch.unindex(property, sort_text.build());
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
} }
@@ -173,9 +172,9 @@ impl MessageMetadata {
if let HeaderValue::DateTime(datetime) = &header.value { if let HeaderValue::DateTime(datetime) = &header.value {
let value = (datetime.to_timestamp() as u64).serialize(); let value = (datetime.to_timestamp() as u64).serialize();
if set { if set {
batch.index(Property::SentAt, value); batch.index(EmailField::SentAt, value);
} else { } else {
batch.unindex(Property::SentAt, value); batch.unindex(EmailField::SentAt, value);
} }
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
@@ -202,9 +201,9 @@ impl MessageMetadata {
.serialize(); .serialize();
if set { if set {
batch.index(Property::Subject, thread_name); batch.index(EmailField::Subject, thread_name);
} else { } else {
batch.unindex(Property::Subject, thread_name); batch.unindex(EmailField::Subject, thread_name);
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
@@ -218,9 +217,9 @@ impl MessageMetadata {
// Add subject to index if missing // Add subject to index if missing
if !seen_headers[HeaderName::Subject.id() as usize] { if !seen_headers[HeaderName::Subject.id() as usize] {
if set { if set {
batch.index(Property::Subject, "!".serialize()); batch.index(EmailField::Subject, "!".serialize());
} else { } else {
batch.unindex(Property::Subject, "!".serialize()); batch.unindex(EmailField::Subject, "!".serialize());
} }
} }
} }
@@ -249,18 +248,18 @@ impl ArchivedMessageMetadata {
if set { if set {
// Serialize metadata // Serialize metadata
batch batch
.index(Property::Size, u32::from(self.size).serialize()) .index(EmailField::Size, u32::from(self.size).serialize())
.index( .index(
Property::ReceivedAt, EmailField::ReceivedAt,
u64::from(self.received_at).serialize(), u64::from(self.received_at).serialize(),
); );
} else { } else {
// Delete metadata // Delete metadata
batch batch
.clear(Property::BodyStructure) .clear(EmailField::Metadata)
.unindex(Property::Size, u32::from(self.size).serialize()) .unindex(EmailField::Size, u32::from(self.size).serialize())
.unindex( .unindex(
Property::ReceivedAt, EmailField::ReceivedAt,
u64::from(self.received_at).serialize(), u64::from(self.received_at).serialize(),
); );
} }
@@ -278,9 +277,9 @@ impl ArchivedMessageMetadata {
if self.has_attachments { if self.has_attachments {
if set { if set {
batch.tag(Property::HasAttachment, ()); batch.tag(EmailField::HasAttachment, ());
} else { } else {
batch.untag(Property::HasAttachment, ()); batch.untag(EmailField::HasAttachment, ());
} }
} }
@@ -311,9 +310,9 @@ impl ArchivedMessageMetadata {
// Add ids to inverted index // Add ids to inverted index
if id.len() < MAX_ID_LENGTH { if id.len() < MAX_ID_LENGTH {
if set { if set {
batch.index(Property::References, encode_message_id(id)); batch.index(EmailField::References, encode_message_id(id));
} else { } 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 // Add ids to inverted index
if id.len() < MAX_ID_LENGTH { if id.len() < MAX_ID_LENGTH {
if set { if set {
batch.index(Property::References, id.serialize()); batch.index(EmailField::References, id.serialize());
} else { } else {
batch.unindex(Property::References, id.serialize()); batch.unindex(EmailField::References, id.serialize());
} }
} }
}); });
@@ -358,9 +357,9 @@ impl ArchivedMessageMetadata {
// Add address to inverted index // Add address to inverted index
if set { if set {
batch.index(u8::from(&property), sort_text.build()); batch.index(property, sort_text.build());
} else { } else {
batch.unindex(u8::from(&property), sort_text.build()); batch.unindex(property, sort_text.build());
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
} }
@@ -372,9 +371,9 @@ impl ArchivedMessageMetadata {
as u64) as u64)
.serialize(); .serialize();
if set { if set {
batch.index(Property::SentAt, value); batch.index(EmailField::SentAt, value);
} else { } else {
batch.unindex(Property::SentAt, value); batch.unindex(EmailField::SentAt, value);
} }
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
@@ -401,9 +400,9 @@ impl ArchivedMessageMetadata {
.serialize(); .serialize();
if set { if set {
batch.index(Property::Subject, thread_name); batch.index(EmailField::Subject, thread_name);
} else { } else {
batch.unindex(Property::Subject, thread_name); batch.unindex(EmailField::Subject, thread_name);
} }
seen_headers[header.name.id() as usize] = true; seen_headers[header.name.id() as usize] = true;
@@ -417,9 +416,9 @@ impl ArchivedMessageMetadata {
// Add subject to index if missing // Add subject to index if missing
if !seen_headers[HeaderName::Subject.id() as usize] { if !seen_headers[HeaderName::Subject.id() as usize] {
if set { if set {
batch.index(Property::Subject, "!".serialize()); batch.index(EmailField::Subject, "!".serialize());
} else { } else {
batch.unindex(Property::Subject, "!".serialize()); batch.unindex(EmailField::Subject, "!".serialize());
} }
} }
} }
@@ -465,7 +464,7 @@ impl IndexMessage for BatchBuilder {
) -> trc::Result<&mut Self> { ) -> trc::Result<&mut Self> {
// Index size // Index size
self.index( self.index(
Property::Size, EmailField::Size,
(message.raw_message.len() as u32).serialize(), (message.raw_message.len() as u32).serialize(),
) )
.add( .add(
@@ -480,7 +479,7 @@ impl IndexMessage for BatchBuilder {
} }
// Index receivedAt // Index receivedAt
self.index(Property::ReceivedAt, received_at.serialize()); self.index(EmailField::ReceivedAt, received_at.serialize());
let mut has_attachments = false; let mut has_attachments = false;
let mut preview = None; let mut preview = None;
@@ -549,7 +548,7 @@ impl IndexMessage for BatchBuilder {
// Store and index hasAttachment property // Store and index hasAttachment property
if has_attachments { if has_attachments {
self.tag(Property::HasAttachment, ()); self.tag(EmailField::HasAttachment, ());
} }
// Link blob // Link blob
@@ -566,7 +565,7 @@ impl IndexMessage for BatchBuilder {
// Store message metadata // Store message metadata
self.set( self.set(
Property::BodyStructure, EmailField::Metadata,
Archiver::new(metadata) Archiver::new(metadata)
.serialize() .serialize()
.caused_by(trc::location!())?, .caused_by(trc::location!())?,
@@ -580,15 +579,15 @@ impl IndexableObject for MessageData {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
prefix: self.thread_id.into(), prefix: self.thread_id.into(),
}, },
IndexValue::LogContainerProperty { IndexValue::LogContainerProperty {
sync_collection: SyncCollection::Thread.into(), sync_collection: SyncCollection::Thread,
ids: vec![self.thread_id], ids: vec![self.thread_id],
}, },
IndexValue::LogContainerProperty { IndexValue::LogContainerProperty {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
ids: self.mailboxes.iter().map(|m| m.mailbox_id).collect(), 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<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
prefix: self.thread_id.to_native().into(), prefix: self.thread_id.to_native().into(),
}, },
IndexValue::LogContainerProperty { IndexValue::LogContainerProperty {
sync_collection: SyncCollection::Thread.into(), sync_collection: SyncCollection::Thread,
ids: vec![self.thread_id.to_native()], ids: vec![self.thread_id.to_native()],
}, },
IndexValue::LogContainerProperty { IndexValue::LogContainerProperty {
sync_collection: SyncCollection::Email.into(), sync_collection: SyncCollection::Email,
ids: self ids: self
.mailboxes .mailboxes
.iter() .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 { match header {
HeaderName::Subject => Property::Subject, HeaderName::Subject => EmailField::Subject,
HeaderName::From => Property::From, HeaderName::From => EmailField::From,
HeaderName::To => Property::To, HeaderName::To => EmailField::To,
HeaderName::Cc => Property::Cc, HeaderName::Cc => EmailField::Cc,
HeaderName::Date => Property::SentAt, HeaderName::Date => EmailField::SentAt,
HeaderName::Bcc => Property::Bcc, HeaderName::Bcc => EmailField::Bcc,
HeaderName::ReplyTo => Property::ReplyTo, HeaderName::ReplyTo => EmailField::ReplyTo,
HeaderName::Sender => Property::Sender, HeaderName::Sender => EmailField::Sender,
HeaderName::InReplyTo => Property::InReplyTo, HeaderName::InReplyTo => EmailField::InReplyTo,
HeaderName::MessageId => Property::MessageId, HeaderName::MessageId => EmailField::MessageId,
HeaderName::References => Property::References, HeaderName::References => EmailField::References,
HeaderName::ResentMessageId => Property::EmailIds, HeaderName::ResentMessageId => EmailField::EmailIds,
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn property_from_archived_header(header: &ArchivedHeaderName) -> Property { pub fn property_from_archived_header(header: &ArchivedHeaderName) -> EmailField {
match header { match header {
ArchivedHeaderName::Subject => Property::Subject, ArchivedHeaderName::Subject => EmailField::Subject,
ArchivedHeaderName::From => Property::From, ArchivedHeaderName::From => EmailField::From,
ArchivedHeaderName::To => Property::To, ArchivedHeaderName::To => EmailField::To,
ArchivedHeaderName::Cc => Property::Cc, ArchivedHeaderName::Cc => EmailField::Cc,
ArchivedHeaderName::Date => Property::SentAt, ArchivedHeaderName::Date => EmailField::SentAt,
ArchivedHeaderName::Bcc => Property::Bcc, ArchivedHeaderName::Bcc => EmailField::Bcc,
ArchivedHeaderName::ReplyTo => Property::ReplyTo, ArchivedHeaderName::ReplyTo => EmailField::ReplyTo,
ArchivedHeaderName::Sender => Property::Sender, ArchivedHeaderName::Sender => EmailField::Sender,
ArchivedHeaderName::InReplyTo => Property::InReplyTo, ArchivedHeaderName::InReplyTo => EmailField::InReplyTo,
ArchivedHeaderName::MessageId => Property::MessageId, ArchivedHeaderName::MessageId => EmailField::MessageId,
ArchivedHeaderName::References => Property::References, ArchivedHeaderName::References => EmailField::References,
ArchivedHeaderName::ResentMessageId => Property::EmailIds, ArchivedHeaderName::ResentMessageId => EmailField::EmailIds,
_ => unreachable!(), _ => unreachable!(),
} }
} }

View File

@@ -17,20 +17,12 @@ use crate::{
metadata::MessageData, metadata::MessageData,
}, },
}; };
use common::{IDX_EMAIL, Server, auth::AccessToken, storage::index::ObjectIndexBuilder}; use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use directory::Permission; use directory::Permission;
use groupware::{ use groupware::{
calendar::itip::{ItipIngest, ItipIngestError}, calendar::itip::{ItipIngest, ItipIngestError},
scheduling::{ItipError, ItipMessages}, scheduling::{ItipError, ItipMessages},
}; };
use jmap_proto::types::{
blob::BlobId,
collection::{Collection, SyncCollection},
id::Id,
keyword::Keyword,
property::Property,
value::{Object, Value},
};
use mail_parser::{ use mail_parser::{
Header, HeaderName, HeaderValue, Message, MessageParser, MimeHeaders, PartType, Header, HeaderName, HeaderValue, Message, MessageParser, MimeHeaders, PartType,
parsers::fields::thread::thread_name, parsers::fields::thread::thread_name,
@@ -45,7 +37,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use store::{ use store::{
BlobClass, IndexKey, IndexKeyPrefix, IterateParams, U32_LEN, IndexKey, IndexKeyPrefix, IterateParams, U32_LEN,
ahash::AHashMap, ahash::AHashMap,
query::Filter, query::Filter,
roaring::RoaringBitmap, roaring::RoaringBitmap,
@@ -53,11 +45,18 @@ use store::{
}; };
use store::{SerializeInfallible, rand::Rng}; use store::{SerializeInfallible, rand::Rng};
use trc::{AddContext, MessageIngestEvent}; use trc::{AddContext, MessageIngestEvent};
use types::{
blob::{BlobClass, BlobId},
collection::{Collection, SyncCollection},
field::{ContactField, EmailField, MailboxField, PrincipalField},
keyword::Keyword,
};
use utils::sanitize_email; use utils::sanitize_email;
#[derive(Default)] #[derive(Default)]
pub struct IngestedEmail { pub struct IngestedEmail {
pub id: Id, pub document_id: u32,
pub thread_id: u32,
pub change_id: u64, pub change_id: u64,
pub blob_id: BlobId, pub blob_id: BlobId,
pub size: usize, pub size: usize,
@@ -219,7 +218,7 @@ impl EmailIngest for Server {
.filter( .filter(
account_id, account_id,
Collection::ContactCard, Collection::ContactCard,
vec![Filter::eq(IDX_EMAIL, sender.into_bytes())], vec![Filter::eq(ContactField::Email, sender.into_bytes())],
) )
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
@@ -473,7 +472,8 @@ impl EmailIngest for Server {
); );
return Ok(IngestedEmail { return Ok(IngestedEmail {
id: Id::default(), document_id: 0,
thread_id: 0,
change_id: u64::MAX, change_id: u64::MAX,
blob_id: BlobId::default(), blob_id: BlobId::default(),
imap_uids: Vec::new(), imap_uids: Vec::new(),
@@ -543,7 +543,12 @@ impl EmailIngest for Server {
if do_encrypt if do_encrypt
&& !message.is_encrypted() && !message.is_encrypted()
&& let Some(encrypt_params_) = self && 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 .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
{ {
@@ -682,7 +687,6 @@ impl EmailIngest for Server {
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.last_change_id(account_id)?; .last_change_id(account_id)?;
let id = Id::from_parts(thread_id, document_id);
// Request FTS index // Request FTS index
self.notify_task_queue(); self.notify_task_queue();
@@ -710,7 +714,8 @@ impl EmailIngest for Server {
); );
Ok(IngestedEmail { Ok(IngestedEmail {
id, document_id,
thread_id,
change_id, change_id,
blob_id: BlobId { blob_id: BlobId {
hash: blob_id.hash, hash: blob_id.hash,
@@ -758,14 +763,14 @@ impl EmailIngest for Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: 0, document_id: 0,
field: Property::Subject.into(), field: EmailField::Subject.into(),
key: thread_name.clone(), key: thread_name.clone(),
}, },
IndexKey { IndexKey {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: u32::MAX, document_id: u32::MAX,
field: Property::Subject.into(), field: EmailField::Subject.into(),
key: thread_name.clone(), key: thread_name.clone(),
}, },
) )
@@ -802,14 +807,14 @@ impl EmailIngest for Server {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: 0, document_id: 0,
field: Property::References.into(), field: EmailField::References.into(),
key: references.first().unwrap().to_vec(), key: references.first().unwrap().to_vec(),
}, },
IndexKey { IndexKey {
account_id, account_id,
collection: Collection::Email.into(), collection: Collection::Email.into(),
document_id: u32::MAX, document_id: u32::MAX,
field: Property::References.into(), field: EmailField::References.into(),
key: references.last().unwrap().to_vec(), key: references.last().unwrap().to_vec(),
}, },
) )
@@ -952,7 +957,7 @@ impl EmailIngest for Server {
.with_account_id(account_id) .with_account_id(account_id)
.with_collection(Collection::Mailbox) .with_collection(Collection::Mailbox)
.update_document(mailbox_id) .update_document(mailbox_id)
.add_and_get(Property::EmailIds, 1); .add_and_get(MailboxField::UidCounter, 1);
self.core self.core
.storage .storage
.data .data
@@ -973,13 +978,3 @@ impl IngestSource<'_> {
matches!(self, Self::Smtp { .. }) 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)
}
}

View File

@@ -6,7 +6,6 @@
use crate::mailbox::{ArchivedUidMailbox, UidMailbox}; use crate::mailbox::{ArchivedUidMailbox, UidMailbox};
use common::storage::index::IndexableAndSerializableObject; use common::storage::index::IndexableAndSerializableObject;
use jmap_proto::types::keyword::{ArchivedKeyword, Keyword};
use mail_parser::{ use mail_parser::{
ArchivedContentType, ArchivedEncoding, ArchivedHeaderName, ArchivedHeaderValue, DateTime, ArchivedContentType, ArchivedEncoding, ArchivedHeaderName, ArchivedHeaderValue, DateTime,
Encoding, Header, HeaderName, HeaderValue, PartType, Encoding, Header, HeaderName, HeaderValue, PartType,
@@ -21,7 +20,10 @@ use rkyv::{
vec::ArchivedVec, vec::ArchivedVec,
}; };
use std::{borrow::Cow, collections::VecDeque}; 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)] #[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Default)]
pub struct MessageData { pub struct MessageData {

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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; use utils::map::bitmap::Bitmap;
#[derive( #[derive(

View File

@@ -5,9 +5,9 @@
*/ */
use common::{Server, storage::index::ObjectIndexBuilder}; use common::{Server, storage::index::ObjectIndexBuilder};
use jmap_proto::types::{collection::Collection, property::Property};
use store::{query::Filter, write::BatchBuilder}; use store::{query::Filter, write::BatchBuilder};
use trc::AddContext; use trc::AddContext;
use types::{collection::Collection, field::SieveField};
use super::SieveScript; use super::SieveScript;
@@ -32,7 +32,7 @@ impl SieveScriptActivate for Server {
.filter( .filter(
account_id, account_id,
Collection::SieveScript, Collection::SieveScript,
vec![Filter::eq(Property::IsActive, vec![1u8])], vec![Filter::eq(SieveField::IsActive, vec![1u8])],
) )
.await? .await?
.results; .results;
@@ -65,7 +65,7 @@ impl SieveScriptActivate for Server {
new_sieve.is_active = false; new_sieve.is_active = false;
batch batch
.update_document(document_id) .update_document(document_id)
.clear(Property::EmailIds) .clear(SieveField::Ids)
.custom( .custom(
ObjectIndexBuilder::new() ObjectIndexBuilder::new()
.with_changes(new_sieve) .with_changes(new_sieve)

View File

@@ -4,12 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use super::SieveScript;
use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder}; use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder};
use jmap_proto::types::{collection::Collection, property::Property};
use store::write::BatchBuilder; use store::write::BatchBuilder;
use trc::AddContext; use trc::AddContext;
use types::{collection::Collection, field::SieveField};
use super::SieveScript;
pub trait SieveScriptDelete: Sync + Send { pub trait SieveScriptDelete: Sync + Send {
fn sieve_script_delete( fn sieve_script_delete(
@@ -54,7 +53,7 @@ impl SieveScriptDelete for Server {
.with_account_id(account_id) .with_account_id(account_id)
.with_collection(Collection::SieveScript) .with_collection(Collection::SieveScript)
.delete_document(document_id) .delete_document(document_id)
.clear(Property::EmailIds) .clear(SieveField::Ids)
.custom( .custom(
ObjectIndexBuilder::<_, ()>::new() ObjectIndexBuilder::<_, ()>::new()
.with_current(obj) .with_current(obj)

View File

@@ -4,20 +4,19 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 super::{ArchivedSieveScript, SieveScript};
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
use types::{collection::SyncCollection, field::SieveField};
impl IndexableObject for SieveScript { impl IndexableObject for SieveScript {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: Property::Name.into(), field: SieveField::Name.into(),
value: self.name.as_str().to_lowercase().into(), value: self.name.as_str().to_lowercase().into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::IsActive.into(), field: SieveField::IsActive.into(),
value: if self.is_active { &[1u8] } else { &[0u8] } value: if self.is_active { &[1u8] } else { &[0u8] }
.as_slice() .as_slice()
.into(), .into(),
@@ -26,7 +25,7 @@ impl IndexableObject for SieveScript {
value: self.blob_hash.clone(), value: self.blob_hash.clone(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::SieveScript.into(), sync_collection: SyncCollection::SieveScript,
prefix: None, prefix: None,
}, },
IndexValue::Quota { used: self.size }, IndexValue::Quota { used: self.size },
@@ -45,11 +44,11 @@ impl IndexableObject for &ArchivedSieveScript {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: Property::Name.into(), field: SieveField::Name.into(),
value: self.name.to_lowercase().into(), value: self.name.to_lowercase().into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::IsActive.into(), field: SieveField::IsActive.into(),
value: if self.is_active { &[1u8] } else { &[0u8] } value: if self.is_active { &[1u8] } else { &[0u8] }
.as_slice() .as_slice()
.into(), .into(),
@@ -58,7 +57,7 @@ impl IndexableObject for &ArchivedSieveScript {
value: (&self.blob_hash).into(), value: (&self.blob_hash).into(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::SieveScript.into(), sync_collection: SyncCollection::SieveScript,
prefix: None, prefix: None,
}, },
IndexValue::Quota { IndexValue::Quota {

View File

@@ -17,7 +17,6 @@ use common::{
Server, auth::AccessToken, config::jmap::settings::SpecialUse, scripts::plugins::PluginContext, Server, auth::AccessToken, config::jmap::settings::SpecialUse, scripts::plugins::PluginContext,
}; };
use directory::{Permission, QueryParams}; use directory::{Permission, QueryParams};
use jmap_proto::types::{collection::Collection, id::Id, keyword::Keyword, property::Property};
use mail_parser::MessageParser; use mail_parser::MessageParser;
use sieve::{Envelope, Event, Input, Mailbox, Recipient, Sieve}; use sieve::{Envelope, Event, Input, Mailbox, Recipient, Sieve};
use std::future::Future; use std::future::Future;
@@ -30,6 +29,7 @@ use store::{
write::{AlignedBytes, Archive, ArchiveVersion, Archiver, BatchBuilder, BlobOp}, write::{AlignedBytes, Archive, ArchiveVersion, Archiver, BatchBuilder, BlobOp},
}; };
use trc::{AddContext, SieveEvent}; use trc::{AddContext, SieveEvent};
use types::{collection::Collection, field::SieveField, id::Id, keyword::Keyword};
use utils::config::utils::ParseValue; use utils::config::utils::ParseValue;
struct SieveMessage<'x> { struct SieveMessage<'x> {
@@ -142,7 +142,8 @@ impl SieveScriptIngest for Server {
did_file_into: false, did_file_into: false,
}]; }];
let mut ingested_message = IngestedEmail { let mut ingested_message = IngestedEmail {
id: Id::default(), document_id: 0,
thread_id: 0,
change_id: u64::MAX, change_id: u64::MAX,
blob_id: Default::default(), blob_id: Default::default(),
size: raw_message.len(), size: raw_message.len(),
@@ -560,7 +561,7 @@ impl SieveScriptIngest for Server {
.filter( .filter(
account_id, account_id,
Collection::SieveScript, Collection::SieveScript,
vec![Filter::eq(Property::IsActive, vec![1u8])], vec![Filter::eq(SieveField::IsActive, vec![1u8])],
) )
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
@@ -591,7 +592,7 @@ impl SieveScriptIngest for Server {
.filter( .filter(
account_id, account_id,
Collection::SieveScript, Collection::SieveScript,
vec![Filter::eq(Property::Name, name.serialize())], vec![Filter::eq(SieveField::Name, name.serialize())],
) )
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
@@ -693,9 +694,9 @@ impl SieveScriptIngest for Server {
.with_account_id(account_id) .with_account_id(account_id)
.with_collection(Collection::SieveScript) .with_collection(Collection::SieveScript)
.update_document(document_id) .update_document(document_id)
.assert_value(Property::Value, &script_object) .assert_value(SieveField::Archive, &script_object)
.set( .set(
Property::Value, SieveField::Archive,
new_archive.serialize().caused_by(trc::location!())?, new_archive.serialize().caused_by(trc::location!())?,
) )
.clear(BlobOp::Link { hash: blob_hash }) .clear(BlobOp::Link { hash: blob_hash })

View File

@@ -4,13 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use std::sync::Arc;
use common::KV_SIEVE_ID; use common::KV_SIEVE_ID;
use sieve::Sieve; use sieve::Sieve;
use std::sync::Arc;
use store::{blake3, write::ArchiveVersion}; use store::{blake3, write::ArchiveVersion};
use utils::BlobHash; use types::blob_hash::BlobHash;
pub mod activate; pub mod activate;
pub mod delete; pub mod delete;

View File

@@ -4,36 +4,35 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 super::{ArchivedEmailSubmission, EmailSubmission};
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
use types::{collection::SyncCollection, field::EmailSubmissionField};
impl IndexableObject for EmailSubmission { impl IndexableObject for EmailSubmission {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: Property::UndoStatus.into(), field: EmailSubmissionField::UndoStatus.into(),
value: self.undo_status.as_index().into(), value: self.undo_status.as_index().into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::EmailId.into(), field: EmailSubmissionField::EmailId.into(),
value: self.email_id.into(), value: self.email_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::ThreadId.into(), field: EmailSubmissionField::ThreadId.into(),
value: self.thread_id.into(), value: self.thread_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::IdentityId.into(), field: EmailSubmissionField::IdentityId.into(),
value: self.identity_id.into(), value: self.identity_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::SendAt.into(), field: EmailSubmissionField::SendAt.into(),
value: self.send_at.into(), value: self.send_at.into(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::EmailSubmission.into(), sync_collection: SyncCollection::EmailSubmission,
prefix: None, prefix: None,
}, },
] ]
@@ -45,27 +44,27 @@ impl IndexableObject for &ArchivedEmailSubmission {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: Property::UndoStatus.into(), field: EmailSubmissionField::UndoStatus.into(),
value: self.undo_status.as_index().into(), value: self.undo_status.as_index().into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::EmailId.into(), field: EmailSubmissionField::EmailId.into(),
value: self.email_id.into(), value: self.email_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::ThreadId.into(), field: EmailSubmissionField::ThreadId.into(),
value: self.thread_id.into(), value: self.thread_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::IdentityId.into(), field: EmailSubmissionField::IdentityId.into(),
value: self.identity_id.into(), value: self.identity_id.into(),
}, },
IndexValue::Index { IndexValue::Index {
field: Property::SendAt.into(), field: EmailSubmissionField::SendAt.into(),
value: self.send_at.into(), value: self.send_at.into(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::EmailSubmission.into(), sync_collection: SyncCollection::EmailSubmission,
prefix: None, prefix: None,
}, },
] ]

View File

@@ -8,7 +8,7 @@ resolver = "2"
utils = { path = "../utils" } utils = { path = "../utils" }
store = { path = "../store" } store = { path = "../store" }
common = { path = "../common" } common = { path = "../common" }
jmap_proto = { path = "../jmap-proto" } types = { path = "../types" }
trc = { path = "../trc" } trc = { path = "../trc" }
directory = { path = "../directory" } directory = { path = "../directory" }
dav-proto = { path = "../dav-proto" } dav-proto = { path = "../dav-proto" }

View File

@@ -18,14 +18,14 @@ use common::{
DavName, DavPath, DavResource, DavResourceMetadata, DavResources, Server, auth::AccessToken, DavName, DavPath, DavResource, DavResourceMetadata, DavResources, Server, auth::AccessToken,
}; };
use directory::backend::internal::manage::ManageDirectory; use directory::backend::internal::manage::ManageDirectory;
use jmap_proto::types::{
collection::{Collection, SyncCollection},
value::AclGrant,
};
use std::sync::Arc; use std::sync::Arc;
use store::ahash::{AHashMap, AHashSet}; use store::ahash::{AHashMap, AHashSet};
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use trc::AddContext; use trc::AddContext;
use types::{
acl::AclGrant,
collection::{Collection, SyncCollection},
};
use utils::map::bitmap::Bitmap; use utils::map::bitmap::Bitmap;
pub(super) async fn build_calcard_resources( pub(super) async fn build_calcard_resources(
@@ -41,7 +41,7 @@ pub(super) async fn build_calcard_resources(
.core .core
.storage .storage
.data .data
.get_last_change_id(account_id, sync_collection) .get_last_change_id(account_id, sync_collection.into())
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.unwrap_or_default(); .unwrap_or_default();
@@ -73,7 +73,7 @@ pub(super) async fn build_calcard_resources(
.core .core
.storage .storage
.data .data
.get_last_change_id(account_id, sync_collection) .get_last_change_id(account_id, sync_collection.into())
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.unwrap_or_default(); .unwrap_or_default();
@@ -183,7 +183,7 @@ pub(super) async fn build_scheduling_resources(
.core .core
.storage .storage
.data .data
.get_last_change_id(account_id, SyncCollection::CalendarScheduling) .get_last_change_id(account_id, SyncCollection::CalendarScheduling.into())
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.unwrap_or_default(); .unwrap_or_default();

View File

@@ -10,11 +10,6 @@ use crate::{
}; };
use common::{DavPath, DavResource, DavResourceMetadata, DavResources, Server}; use common::{DavPath, DavResource, DavResourceMetadata, DavResources, Server};
use directory::backend::internal::manage::ManageDirectory; use directory::backend::internal::manage::ManageDirectory;
use jmap_proto::types::{
collection::{Collection, SyncCollection},
property::Property,
value::AclGrant,
};
use std::sync::Arc; use std::sync::Arc;
use store::{ use store::{
Deserialize, IterateParams, U32_LEN, ValueKey, Deserialize, IterateParams, U32_LEN, ValueKey,
@@ -23,6 +18,11 @@ use store::{
}; };
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use trc::AddContext; use trc::AddContext;
use types::{
acl::AclGrant,
collection::{Collection, SyncCollection},
field::Field,
};
use utils::{map::bitmap::Bitmap, topological::TopologicalSort}; use utils::{map::bitmap::Bitmap, topological::TopologicalSort};
pub(super) async fn build_file_resources( pub(super) async fn build_file_resources(
@@ -34,7 +34,7 @@ pub(super) async fn build_file_resources(
.core .core
.storage .storage
.data .data
.get_last_change_id(account_id, SyncCollection::FileNode) .get_last_change_id(account_id, SyncCollection::FileNode.into())
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.unwrap_or_default(); .unwrap_or_default();
@@ -130,13 +130,13 @@ async fn fetch_files(server: &Server, account_id: u32) -> trc::Result<Vec<DavRes
account_id, account_id,
collection: Collection::FileNode.into(), collection: Collection::FileNode.into(),
document_id: 0, document_id: 0,
class: ValueClass::Property(Property::Value.into()), class: ValueClass::from(Field::ARCHIVE),
}, },
ValueKey { ValueKey {
account_id, account_id,
collection: Collection::FileNode.into(), collection: Collection::FileNode.into(),
document_id: u32::MAX, document_id: u32::MAX,
class: ValueClass::Property(Property::Value.into()), class: ValueClass::from(Field::ARCHIVE),
}, },
), ),
|key, value| { |key, value| {

View File

@@ -17,7 +17,6 @@ use calcard::{
}; };
use common::{CacheSwap, DavResource, DavResources, Server, auth::AccessToken}; use common::{CacheSwap, DavResource, DavResources, Server, auth::AccessToken};
use file::{build_file_resources, build_nested_hierarchy, resource_from_file}; 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 std::{sync::Arc, time::Instant};
use store::{ use store::{
ahash::AHashMap, ahash::AHashMap,
@@ -26,6 +25,7 @@ use store::{
}; };
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use trc::{AddContext, StoreEvent}; use trc::{AddContext, StoreEvent};
use types::collection::{Collection, SyncCollection};
pub mod calcard; pub mod calcard;
pub mod file; pub mod file;
@@ -119,7 +119,7 @@ impl GroupwareCache for Server {
.data .data
.changes( .changes(
account_id, account_id,
collection, collection.into(),
Query::Since(cache.highest_change_id), Query::Since(cache.highest_change_id),
) )
.await .await

View File

@@ -11,8 +11,7 @@ use super::{
ArchivedTimezone, Calendar, CalendarEvent, CalendarPreferences, DefaultAlert, Timezone, ArchivedTimezone, Calendar, CalendarEvent, CalendarPreferences, DefaultAlert, Timezone,
}; };
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject}; use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
use common::{IDX_CREATED, IDX_UID}; use types::{acl::AclGrant, collection::SyncCollection, field::CalendarField};
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
impl IndexableObject for Calendar { impl IndexableObject for Calendar {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
@@ -27,7 +26,7 @@ impl IndexableObject for Calendar {
+ self.name.len() as u32, + self.name.len() as u32,
}, },
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::Calendar.into(), sync_collection: SyncCollection::Calendar,
}, },
] ]
.into_iter() .into_iter()
@@ -52,7 +51,7 @@ impl IndexableObject for &ArchivedCalendar {
+ self.name.len() as u32, + self.name.len() as u32,
}, },
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::Calendar.into(), sync_collection: SyncCollection::Calendar,
}, },
] ]
.into_iter() .into_iter()
@@ -69,7 +68,7 @@ impl IndexableObject for CalendarEvent {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: IDX_UID, field: CalendarField::Uid.into(),
value: self.data.event.uids().next().into(), value: self.data.event.uids().next().into(),
}, },
IndexValue::Quota { IndexValue::Quota {
@@ -79,7 +78,7 @@ impl IndexableObject for CalendarEvent {
+ self.size, + self.size,
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::Calendar.into(), sync_collection: SyncCollection::Calendar,
prefix: None, prefix: None,
}, },
] ]
@@ -91,7 +90,7 @@ impl IndexableObject for &ArchivedCalendarEvent {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: IDX_UID, field: CalendarField::Uid.into(),
value: self.data.event.uids().next().into(), value: self.data.event.uids().next().into(),
}, },
IndexValue::Quota { IndexValue::Quota {
@@ -101,7 +100,7 @@ impl IndexableObject for &ArchivedCalendarEvent {
+ self.size, + self.size,
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::Calendar.into(), sync_collection: SyncCollection::Calendar,
prefix: None, prefix: None,
}, },
] ]
@@ -120,11 +119,11 @@ impl IndexableObject for CalendarScheduling {
[ [
IndexValue::Quota { used: self.size }, IndexValue::Quota { used: self.size },
IndexValue::Index { IndexValue::Index {
field: IDX_CREATED, field: CalendarField::Created.into(),
value: self.created.into(), value: self.created.into(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::CalendarScheduling.into(), sync_collection: SyncCollection::CalendarScheduling,
prefix: None, prefix: None,
}, },
] ]
@@ -139,11 +138,11 @@ impl IndexableObject for &ArchivedCalendarScheduling {
used: self.size.to_native(), used: self.size.to_native(),
}, },
IndexValue::Index { IndexValue::Index {
field: IDX_CREATED, field: CalendarField::Created.into(),
value: self.created.to_native().into(), value: self.created.to_native().into(),
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::CalendarScheduling.into(), sync_collection: SyncCollection::CalendarScheduling,
prefix: None, prefix: None,
}, },
] ]

View File

@@ -24,18 +24,21 @@ use calcard::{
}, },
}; };
use common::{ use common::{
DavName, IDX_EMAIL, IDX_UID, Server, DavName, Server,
auth::{AccessToken, ResourceToken, oauth::GrantType}, auth::{AccessToken, ResourceToken, oauth::GrantType},
config::groupware::CalendarTemplateVariable, config::groupware::CalendarTemplateVariable,
i18n, i18n,
}; };
use jmap_proto::types::collection::Collection;
use store::{ use store::{
query::Filter, query::Filter,
rand, rand,
write::{BatchBuilder, now}, write::{BatchBuilder, now},
}; };
use trc::AddContext; use trc::AddContext;
use types::{
collection::Collection,
field::{CalendarField, ContactField},
};
use utils::{template::Variables, url_params::UrlParams}; use utils::{template::Variables, url_params::UrlParams};
pub enum ItipIngestError { pub enum ItipIngestError {
@@ -142,7 +145,10 @@ impl ItipIngest for Server {
.filter( .filter(
account_id, account_id,
Collection::CalendarEvent, 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 .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
@@ -261,7 +267,7 @@ impl ItipIngest for Server {
.filter( .filter(
account_id, account_id,
Collection::ContactCard, Collection::ContactCard,
vec![Filter::eq(IDX_EMAIL, sender.as_bytes().to_vec())], vec![Filter::eq(ContactField::Email, sender.as_bytes().to_vec())],
) )
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?

View File

@@ -14,7 +14,7 @@ pub mod storage;
use calcard::icalendar::ICalendar; use calcard::icalendar::ICalendar;
use common::DavName; use common::DavName;
use dav_proto::schema::request::DeadProperty; use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::{acl::Acl, value::AclGrant}; use types::acl::{Acl, AclGrant};
#[derive( #[derive(
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,

View File

@@ -4,14 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use super::{
ArchivedCalendar, ArchivedCalendarEvent, Calendar, CalendarEvent, CalendarPreferences,
alarm::CalendarAlarm,
};
use crate::{ use crate::{
DavResourceName, DestroyArchive, RFC_3986, DavResourceName, DestroyArchive, RFC_3986,
calendar::{ArchivedCalendarScheduling, CalendarScheduling}, calendar::{ArchivedCalendarScheduling, CalendarScheduling},
scheduling::{ItipMessages, event_cancel::itip_cancel}, scheduling::{ItipMessages, event_cancel::itip_cancel},
}; };
use calcard::common::timezone::Tz; use calcard::common::timezone::Tz;
use common::{IDX_CREATED, Server, auth::AccessToken, storage::index::ObjectIndexBuilder}; use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use jmap_proto::types::collection::{Collection, VanishedCollection};
use store::{ use store::{
IndexKey, IterateParams, SerializeInfallible, U16_LEN, U32_LEN, U64_LEN, IndexKey, IterateParams, SerializeInfallible, U16_LEN, U32_LEN, U64_LEN,
roaring::RoaringBitmap, roaring::RoaringBitmap,
@@ -22,10 +25,9 @@ use store::{
}, },
}; };
use trc::AddContext; use trc::AddContext;
use types::{
use super::{ collection::{Collection, VanishedCollection},
ArchivedCalendar, ArchivedCalendarEvent, Calendar, CalendarEvent, CalendarPreferences, field::CalendarField,
alarm::CalendarAlarm,
}; };
pub trait ItipAutoExpunge: Sync + Send { pub trait ItipAutoExpunge: Sync + Send {
@@ -47,14 +49,14 @@ impl ItipAutoExpunge for Server {
account_id, account_id,
collection: Collection::CalendarScheduling.into(), collection: Collection::CalendarScheduling.into(),
document_id: 0, document_id: 0,
field: IDX_CREATED, field: CalendarField::Created.into(),
key: 0u64.serialize(), key: 0u64.serialize(),
}, },
IndexKey { IndexKey {
account_id, account_id,
collection: Collection::CalendarScheduling.into(), collection: Collection::CalendarScheduling.into(),
document_id: u32::MAX, document_id: u32::MAX,
field: IDX_CREATED, field: CalendarField::Created.into(),
key: now().saturating_sub(hold_period).serialize(), key: now().saturating_sub(hold_period).serialize(),
}, },
) )

View File

@@ -9,9 +9,8 @@ use calcard::vcard::VCardProperty;
use common::storage::index::{ use common::storage::index::{
IndexItem, IndexValue, IndexableAndSerializableObject, IndexableObject, IndexItem, IndexValue, IndexableAndSerializableObject, IndexableObject,
}; };
use common::{IDX_EMAIL, IDX_UID};
use jmap_proto::types::{collection::SyncCollection, value::AclGrant};
use std::collections::HashSet; use std::collections::HashSet;
use types::{acl::AclGrant, collection::SyncCollection, field::ContactField};
use utils::sanitize_email; use utils::sanitize_email;
impl IndexableObject for AddressBook { impl IndexableObject for AddressBook {
@@ -27,7 +26,7 @@ impl IndexableObject for AddressBook {
+ self.name.len() as u32, + self.name.len() as u32,
}, },
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::AddressBook.into(), sync_collection: SyncCollection::AddressBook,
}, },
] ]
.into_iter() .into_iter()
@@ -52,7 +51,7 @@ impl IndexableObject for &ArchivedAddressBook {
+ self.name.len() as u32, + self.name.len() as u32,
}, },
IndexValue::LogContainer { IndexValue::LogContainer {
sync_collection: SyncCollection::AddressBook.into(), sync_collection: SyncCollection::AddressBook,
}, },
] ]
.into_iter() .into_iter()
@@ -69,11 +68,11 @@ impl IndexableObject for ContactCard {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: IDX_UID, field: ContactField::Uid.into(),
value: self.card.uid().into(), value: self.card.uid().into(),
}, },
IndexValue::IndexList { IndexValue::IndexList {
field: IDX_EMAIL, field: ContactField::Email.into(),
value: self value: self
.emails() .emails()
.map(Into::into) .map(Into::into)
@@ -88,7 +87,7 @@ impl IndexableObject for ContactCard {
+ self.size, + self.size,
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::AddressBook.into(), sync_collection: SyncCollection::AddressBook,
prefix: None, prefix: None,
}, },
] ]
@@ -100,11 +99,11 @@ impl IndexableObject for &ArchivedContactCard {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[ [
IndexValue::Index { IndexValue::Index {
field: IDX_UID, field: ContactField::Uid.into(),
value: self.card.uid().into(), value: self.card.uid().into(),
}, },
IndexValue::IndexList { IndexValue::IndexList {
field: IDX_EMAIL, field: ContactField::Email.into(),
value: self value: self
.emails() .emails()
.map(Into::into) .map(Into::into)
@@ -119,7 +118,7 @@ impl IndexableObject for &ArchivedContactCard {
+ self.size, + self.size,
}, },
IndexValue::LogItem { IndexValue::LogItem {
sync_collection: SyncCollection::AddressBook.into(), sync_collection: SyncCollection::AddressBook,
prefix: None, prefix: None,
}, },
] ]

View File

@@ -10,7 +10,7 @@ pub mod storage;
use calcard::vcard::VCard; use calcard::vcard::VCard;
use common::DavName; use common::DavName;
use dav_proto::schema::request::DeadProperty; use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::{acl::Acl, value::AclGrant}; use types::acl::{Acl, AclGrant};
#[derive( #[derive(
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,

View File

@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use jmap_proto::types::collection::{Collection, VanishedCollection};
use store::write::{Archive, BatchBuilder, now}; use store::write::{Archive, BatchBuilder, now};
use trc::AddContext; use trc::AddContext;
use types::collection::{Collection, VanishedCollection};
use crate::DestroyArchive;
use super::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard};
impl ContactCard { impl ContactCard {
pub fn update<'x>( pub fn update<'x>(

View File

@@ -6,7 +6,7 @@
use super::{ArchivedFileNode, FileNode}; use super::{ArchivedFileNode, FileNode};
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject}; 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 { impl IndexableObject for FileNode {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> { fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
@@ -18,7 +18,7 @@ impl IndexableObject for FileNode {
}, },
IndexValue::LogItem { IndexValue::LogItem {
prefix: None, prefix: None,
sync_collection: SyncCollection::FileNode.into(), sync_collection: SyncCollection::FileNode,
}, },
IndexValue::Quota { used: self.size() }, IndexValue::Quota { used: self.size() },
]); ]);
@@ -48,7 +48,7 @@ impl IndexableObject for &ArchivedFileNode {
}, },
IndexValue::LogItem { IndexValue::LogItem {
prefix: None, prefix: None,
sync_collection: SyncCollection::FileNode.into(), sync_collection: SyncCollection::FileNode,
}, },
IndexValue::Quota { used: self.size() }, IndexValue::Quota { used: self.size() },
]); ]);

View File

@@ -8,8 +8,7 @@ pub mod index;
pub mod storage; pub mod storage;
use dav_proto::schema::request::DeadProperty; use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::value::AclGrant; use types::{acl::AclGrant, blob_hash::BlobHash};
use utils::BlobHash;
#[derive( #[derive(
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,

View File

@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * 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 common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use jmap_proto::types::collection::{Collection, VanishedCollection};
use store::write::{Archive, BatchBuilder, now}; use store::write::{Archive, BatchBuilder, now};
use trc::AddContext; use trc::AddContext;
use types::collection::{Collection, VanishedCollection};
use crate::DestroyArchive;
use super::{ArchivedFileNode, FileNode};
impl FileNode { impl FileNode {
pub fn insert<'x>( pub fn insert<'x>(

View File

@@ -6,8 +6,8 @@
use calcard::common::timezone::Tz; use calcard::common::timezone::Tz;
use common::DavResources; use common::DavResources;
use jmap_proto::types::collection::{Collection, SyncCollection};
use percent_encoding::{AsciiSet, CONTROLS}; use percent_encoding::{AsciiSet, CONTROLS};
use types::collection::{Collection, SyncCollection};
pub mod cache; pub mod cache;
pub mod calendar; pub mod calendar;

View File

@@ -17,6 +17,7 @@ groupware = { path = "../groupware" }
spam-filter = { path = "../spam-filter" } spam-filter = { path = "../spam-filter" }
http_proto = { path = "../http-proto" } http_proto = { path = "../http-proto" }
jmap_proto = { path = "../jmap-proto" } jmap_proto = { path = "../jmap-proto" }
types = { path = "../types" }
directory = { path = "../directory" } directory = { path = "../directory" }
services = { path = "../services" } services = { path = "../services" }
smtp-proto = { version = "0.2" } smtp-proto = { version = "0.2" }

View File

@@ -30,7 +30,7 @@ use store::{
write::{BatchBuilder, BlobOp, now}, write::{BatchBuilder, BlobOp, now},
}; };
use trc::AddContext; use trc::AddContext;
use utils::BlobHash; use types::blob_hash::BlobHash;
use x509_parser::nom::AsBytes; use x509_parser::nom::AsBytes;
pub trait FormHandler: Sync + Send { pub trait FormHandler: Sync + Send {

View File

@@ -4,8 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use std::{future::Future, sync::Arc};
use common::{Server, auth::AccessToken}; use common::{Server, auth::AccessToken};
use directory::backend::internal::manage; use directory::backend::internal::manage;
use email::message::crypto::{ use email::message::crypto::{
@@ -13,15 +11,16 @@ use email::message::crypto::{
EncryptionMethod, EncryptionParams, EncryptionType, try_parse_certs, EncryptionMethod, EncryptionParams, EncryptionType, try_parse_certs,
}; };
use http_proto::*; use http_proto::*;
use jmap_proto::types::{collection::Collection, property::Property};
use mail_builder::encoders::base64::base64_encode_mime; use mail_builder::encoders::base64::base64_encode_mime;
use mail_parser::MessageParser; use mail_parser::MessageParser;
use serde_json::json; use serde_json::json;
use std::{future::Future, sync::Arc};
use store::{ use store::{
Deserialize, Serialize, Deserialize, Serialize,
write::{AlignedBytes, Archive, Archiver, BatchBuilder}, write::{AlignedBytes, Archive, Archiver, BatchBuilder},
}; };
use trc::AddContext; use trc::AddContext;
use types::{collection::Collection, field::PrincipalField};
pub trait CryptoHandler: Sync + Send { pub trait CryptoHandler: Sync + Send {
fn handle_crypto_get( fn handle_crypto_get(
@@ -43,7 +42,7 @@ impl CryptoHandler for Server {
access_token.primary_id(), access_token.primary_id(),
Collection::Principal, Collection::Principal,
0, 0,
Property::Parameters, PrincipalField::EncryptionKeys.into(),
) )
.await? .await?
{ {
@@ -96,7 +95,7 @@ impl CryptoHandler for Server {
.with_account_id(access_token.primary_id()) .with_account_id(access_token.primary_id())
.with_collection(Collection::Principal) .with_collection(Collection::Principal)
.update_document(0) .update_document(0)
.clear(Property::Parameters); .clear(PrincipalField::EncryptionKeys);
self.core.storage.data.write(batch.build_all()).await?; self.core.storage.data.write(batch.build_all()).await?;
return Ok(JsonResponse::new(json!({ return Ok(JsonResponse::new(json!({
"data": (), "data": (),
@@ -146,7 +145,7 @@ impl CryptoHandler for Server {
.with_account_id(access_token.primary_id()) .with_account_id(access_token.primary_id())
.with_collection(Collection::Principal) .with_collection(Collection::Principal)
.update_document(0) .update_document(0)
.set(Property::Parameters, params); .set(PrincipalField::EncryptionKeys, params);
self.core.storage.data.write(batch.build_all()).await?; self.core.storage.data.write(batch.build_all()).await?;
Ok(JsonResponse::new(json!({ Ok(JsonResponse::new(json!({

View File

@@ -8,8 +8,6 @@
* *
*/ */
use std::str::FromStr;
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use common::{Server, enterprise::undelete::DeletedBlob}; use common::{Server, enterprise::undelete::DeletedBlob};
use directory::backend::internal::manage::ManageDirectory; use directory::backend::internal::manage::ManageDirectory;
@@ -18,13 +16,14 @@ use email::{
message::ingest::{EmailIngest, IngestEmail, IngestSource}, message::ingest::{EmailIngest, IngestEmail, IngestSource},
}; };
use hyper::Method; use hyper::Method;
use jmap_proto::types::collection::Collection;
use mail_parser::{DateTime, MessageParser}; use mail_parser::{DateTime, MessageParser};
use serde_json::json; use serde_json::json;
use std::future::Future; use std::future::Future;
use std::str::FromStr;
use store::write::{BatchBuilder, BlobOp, ValueClass}; use store::write::{BatchBuilder, BlobOp, ValueClass};
use trc::AddContext; 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, *}; use http_proto::{request::decode_path_element, *};

View File

@@ -19,7 +19,6 @@ use directory::{
use email::message::{ingest::EmailIngest, metadata::MessageData}; use email::message::{ingest::EmailIngest, metadata::MessageData};
use http_proto::{request::decode_path_element, *}; use http_proto::{request::decode_path_element, *};
use hyper::Method; use hyper::Method;
use jmap_proto::types::{collection::Collection, property::Property};
use serde_json::json; use serde_json::json;
use services::task_manager::fts::FtsIndexTask; use services::task_manager::fts::FtsIndexTask;
use std::future::Future; use std::future::Future;
@@ -28,6 +27,10 @@ use store::{
write::{Archiver, BatchBuilder, ValueClass}, write::{Archiver, BatchBuilder, ValueClass},
}; };
use trc::AddContext; use trc::AddContext;
use types::{
collection::Collection,
field::{EmailField, MailboxField},
};
use utils::url_params::UrlParams; use utils::url_params::UrlParams;
// SPDX-SnippetBegin // SPDX-SnippetBegin
@@ -354,7 +357,7 @@ pub async fn reset_imap_uids(server: &Server, account_id: u32) -> trc::Result<(u
.with_changes(new_mailbox), .with_changes(new_mailbox),
) )
.caused_by(trc::location!())? .caused_by(trc::location!())?
.clear(Property::EmailIds); .clear(MailboxField::UidCounter);
server server
.store() .store()
.write(batch.build_all()) .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_account_id(account_id)
.with_collection(Collection::Email) .with_collection(Collection::Email)
.update_document(message_id) .update_document(message_id)
.assert_value(ValueClass::Property(Property::Value.into()), &data) .assert_value(ValueClass::Property(EmailField::Archive.into()), &data)
.set( .set(
Property::Value, EmailField::Archive,
Archiver::new(new_data) Archiver::new(new_data)
.serialize() .serialize()
.caused_by(trc::location!())?, .caused_by(trc::location!())?,

Some files were not shown because too many files have changed in this diff Show More