mirror of
https://github.com/stalwartlabs/stalwart.git
synced 2026-03-17 14:34:03 +00:00
Moved shared data types to 'types' subcrate
This commit is contained in:
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -1285,6 +1285,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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},
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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, ¤t);
|
batch.assert_value(Field::ARCHIVE, ¤t);
|
||||||
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, ¤t);
|
batch.assert_value(Field::ARCHIVE, ¤t);
|
||||||
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!(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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!())?;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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!())?;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
6
crates/email/src/cache/email.rs
vendored
6
crates/email/src/cache/email.rs
vendored
@@ -9,13 +9,13 @@ use common::{
|
|||||||
MessageCache, MessageStoreCache, MessageUidCache, MessagesCache, Server, auth::AccessToken,
|
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(
|
||||||
|
|||||||
5
crates/email/src/cache/mailbox.rs
vendored
5
crates/email/src/cache/mailbox.rs
vendored
@@ -9,9 +9,12 @@ use common::{
|
|||||||
MailboxCache, MailboxesCache, MessageStoreCache, Server, auth::AccessToken,
|
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(
|
||||||
|
|||||||
9
crates/email/src/cache/mod.rs
vendored
9
crates/email/src/cache/mod.rs
vendored
@@ -4,18 +4,17 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
* 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();
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(|| {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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?
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
14
crates/groupware/src/cache/calcard.rs
vendored
14
crates/groupware/src/cache/calcard.rs
vendored
@@ -18,14 +18,14 @@ use common::{
|
|||||||
DavName, DavPath, DavResource, DavResourceMetadata, DavResources, Server, auth::AccessToken,
|
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();
|
||||||
|
|||||||
16
crates/groupware/src/cache/file.rs
vendored
16
crates/groupware/src/cache/file.rs
vendored
@@ -10,11 +10,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{DavPath, DavResource, DavResourceMetadata, DavResources, Server};
|
use 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| {
|
||||||
|
|||||||
4
crates/groupware/src/cache/mod.rs
vendored
4
crates/groupware/src/cache/mod.rs
vendored
@@ -17,7 +17,6 @@ use calcard::{
|
|||||||
};
|
};
|
||||||
use common::{CacheSwap, DavResource, DavResources, Server, auth::AccessToken};
|
use 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
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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!())?
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>(
|
||||||
|
|||||||
@@ -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() },
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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!({
|
||||||
|
|||||||
@@ -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, *};
|
||||||
|
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user