Moved shared data types to 'types' subcrate

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

40
Cargo.lock generated
View File

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

View File

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

View File

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

View File

@@ -18,16 +18,13 @@ use directory::{
manage::{ChangedPrincipals, ManageDirectory},
},
};
use jmap_proto::{
request::RequestMethod,
types::{acl::Acl, collection::Collection, id::Id},
};
use std::{
hash::{DefaultHasher, Hash, Hasher},
sync::Arc,
};
use store::{query::acl::AclQuery, rand};
use trc::AddContext;
use types::{acl::Acl, collection::Collection};
use utils::map::{
bitmap::{Bitmap, BitmapItem},
vec_map::VecMap,
@@ -163,7 +160,7 @@ impl Server {
{
if !access_token.is_member(acl_item.to_account_id) {
let acl = Bitmap::<Acl>::from(acl_item.permissions);
let collection = Collection::from(acl_item.to_collection);
let collection = acl_item.to_collection;
if !collection.is_valid() {
return Err(trc::StoreEvent::DataCorruption
.ctx(trc::Key::Reason, "Corrupted collection found in ACL key.")
@@ -485,8 +482,7 @@ impl AccessToken {
!self.is_member(account_id) && self.access_to.iter().any(|(id, _)| *id == account_id)
}
pub fn shared_accounts(&self, collection: impl Into<Collection>) -> impl Iterator<Item = &u32> {
let collection = collection.into();
pub fn shared_accounts(&self, collection: Collection) -> impl Iterator<Item = &u32> {
self.member_of
.iter()
.chain(self.access_to.iter().filter_map(move |(id, cols)| {
@@ -510,152 +506,6 @@ impl AccessToken {
self.is_member(to_account_id) || self.access_to.iter().any(|(id, _)| *id == to_account_id)
}
pub fn assert_has_access(
&self,
to_account_id: Id,
to_collection: Collection,
) -> trc::Result<&Self> {
if self.has_access(to_account_id.document_id(), to_collection) {
Ok(self)
} else {
Err(trc::JmapEvent::Forbidden.into_err().details(format!(
"You do not have access to account {}",
to_account_id
)))
}
}
pub fn assert_is_member(&self, account_id: Id) -> trc::Result<&Self> {
if self.is_member(account_id.document_id()) {
Ok(self)
} else {
Err(trc::JmapEvent::Forbidden
.into_err()
.details(format!("You are not an owner of account {}", account_id)))
}
}
pub fn assert_has_jmap_permission(&self, request: &RequestMethod) -> trc::Result<()> {
let permission = match request {
RequestMethod::Get(m) => match &m.arguments {
jmap_proto::method::get::RequestArguments::Email(_) => Permission::JmapEmailGet,
jmap_proto::method::get::RequestArguments::Mailbox => Permission::JmapMailboxGet,
jmap_proto::method::get::RequestArguments::Thread => Permission::JmapThreadGet,
jmap_proto::method::get::RequestArguments::Identity => Permission::JmapIdentityGet,
jmap_proto::method::get::RequestArguments::EmailSubmission => {
Permission::JmapEmailSubmissionGet
}
jmap_proto::method::get::RequestArguments::PushSubscription => {
Permission::JmapPushSubscriptionGet
}
jmap_proto::method::get::RequestArguments::SieveScript => {
Permission::JmapSieveScriptGet
}
jmap_proto::method::get::RequestArguments::VacationResponse => {
Permission::JmapVacationResponseGet
}
jmap_proto::method::get::RequestArguments::Principal => {
Permission::JmapPrincipalGet
}
jmap_proto::method::get::RequestArguments::Quota => Permission::JmapQuotaGet,
jmap_proto::method::get::RequestArguments::Blob(_) => Permission::JmapBlobGet,
},
RequestMethod::Set(m) => match &m.arguments {
jmap_proto::method::set::RequestArguments::Email => Permission::JmapEmailSet,
jmap_proto::method::set::RequestArguments::Mailbox(_) => Permission::JmapMailboxSet,
jmap_proto::method::set::RequestArguments::Identity => Permission::JmapIdentitySet,
jmap_proto::method::set::RequestArguments::EmailSubmission(_) => {
Permission::JmapEmailSubmissionSet
}
jmap_proto::method::set::RequestArguments::PushSubscription => {
Permission::JmapPushSubscriptionSet
}
jmap_proto::method::set::RequestArguments::SieveScript(_) => {
Permission::JmapSieveScriptSet
}
jmap_proto::method::set::RequestArguments::VacationResponse => {
Permission::JmapVacationResponseSet
}
},
RequestMethod::Changes(m) => match m.arguments {
jmap_proto::method::changes::RequestArguments::Email => {
Permission::JmapEmailChanges
}
jmap_proto::method::changes::RequestArguments::Mailbox => {
Permission::JmapMailboxChanges
}
jmap_proto::method::changes::RequestArguments::Thread => {
Permission::JmapThreadChanges
}
jmap_proto::method::changes::RequestArguments::Identity => {
Permission::JmapIdentityChanges
}
jmap_proto::method::changes::RequestArguments::EmailSubmission => {
Permission::JmapEmailSubmissionChanges
}
jmap_proto::method::changes::RequestArguments::Quota => {
Permission::JmapQuotaChanges
}
},
RequestMethod::Copy(m) => match m.arguments {
jmap_proto::method::copy::RequestArguments::Email => Permission::JmapEmailCopy,
},
RequestMethod::CopyBlob(_) => Permission::JmapBlobCopy,
RequestMethod::ImportEmail(_) => Permission::JmapEmailImport,
RequestMethod::ParseEmail(_) => Permission::JmapEmailParse,
RequestMethod::QueryChanges(m) => match m.arguments {
jmap_proto::method::query::RequestArguments::Email(_) => {
Permission::JmapEmailQueryChanges
}
jmap_proto::method::query::RequestArguments::Mailbox(_) => {
Permission::JmapMailboxQueryChanges
}
jmap_proto::method::query::RequestArguments::EmailSubmission => {
Permission::JmapEmailSubmissionQueryChanges
}
jmap_proto::method::query::RequestArguments::SieveScript => {
Permission::JmapSieveScriptQueryChanges
}
jmap_proto::method::query::RequestArguments::Principal => {
Permission::JmapPrincipalQueryChanges
}
jmap_proto::method::query::RequestArguments::Quota => {
Permission::JmapQuotaQueryChanges
}
},
RequestMethod::Query(m) => match m.arguments {
jmap_proto::method::query::RequestArguments::Email(_) => Permission::JmapEmailQuery,
jmap_proto::method::query::RequestArguments::Mailbox(_) => {
Permission::JmapMailboxQuery
}
jmap_proto::method::query::RequestArguments::EmailSubmission => {
Permission::JmapEmailSubmissionQuery
}
jmap_proto::method::query::RequestArguments::SieveScript => {
Permission::JmapSieveScriptQuery
}
jmap_proto::method::query::RequestArguments::Principal => {
Permission::JmapPrincipalQuery
}
jmap_proto::method::query::RequestArguments::Quota => Permission::JmapQuotaQuery,
},
RequestMethod::SearchSnippet(_) => Permission::JmapSearchSnippet,
RequestMethod::ValidateScript(_) => Permission::JmapSieveScriptValidate,
RequestMethod::LookupBlob(_) => Permission::JmapBlobLookup,
RequestMethod::UploadBlob(_) => Permission::JmapBlobUpload,
RequestMethod::Echo(_) => Permission::JmapEcho,
RequestMethod::Error(_) => return Ok(()),
};
if self.has_permission(permission) {
Ok(())
} else {
Err(trc::JmapEvent::Forbidden
.into_err()
.details("You are not authorized to perform this action"))
}
}
pub fn as_resource_token(&self) -> ResourceToken {
ResourceToken {
account_id: self.primary_id,

View File

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

View File

@@ -4,18 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use ahash::AHashSet;
use jmap_proto::{
request::capability::{
BlobCapabilities, Capabilities, Capability, CoreCapabilities, EmptyCapabilities,
MailCapabilities, SieveAccountCapabilities, SieveSessionCapabilities,
SubmissionCapabilities,
},
types::type_state::DataType,
};
use utils::{config::Config, map::vec_map::VecMap};
use super::settings::JmapConfig;
use ahash::AHashSet;
use jmap_proto::request::capability::{
BlobCapabilities, Capabilities, Capability, CoreCapabilities, EmptyCapabilities,
MailCapabilities, SieveAccountCapabilities, SieveSessionCapabilities, SubmissionCapabilities,
};
use types::type_state::DataType;
use utils::{config::Config, map::vec_map::VecMap};
impl JmapConfig {
pub fn add_capabilities(&mut self, config: &mut Config) {

View File

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

View File

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

View File

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

View File

@@ -4,24 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::{sync::Arc, time::Instant};
use ahash::RandomState;
use jmap_proto::types::{state::StateChange, type_state::DataType};
use mail_auth::{
dmarc::Dmarc,
mta_sts::TlsRpt,
report::{Record, tlsrpt::FailureDetails},
};
use store::{BlobStore, InMemoryStore, Store};
use tokio::sync::mpsc;
use utils::map::bitmap::Bitmap;
use crate::config::smtp::{
queue::QueueName,
report::AggregateFrequency,
resolver::{Policy, Tlsa},
};
use ahash::RandomState;
use mail_auth::{
dmarc::Dmarc,
mta_sts::TlsRpt,
report::{Record, tlsrpt::FailureDetails},
};
use std::{sync::Arc, time::Instant};
use store::{BlobStore, InMemoryStore, Store};
use tokio::sync::mpsc;
use types::type_state::{DataType, StateChange};
use utils::map::bitmap::Bitmap;
pub enum HousekeeperEvent {
AcmeReschedule {

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,116 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::{Server, auth::AccessToken};
use crate::Server;
use directory::{
QueryParams, Type,
backend::internal::{
PrincipalField,
manage::{ChangedPrincipals, ManageDirectory},
},
Type,
backend::internal::{PrincipalField, manage::ChangedPrincipals},
};
use jmap_proto::{
error::set::SetError,
types::{
acl::Acl,
property::Property,
value::{AclGrant, ArchivedAclGrant, MaybePatchValue, Value},
},
};
use utils::map::bitmap::Bitmap;
use types::acl::{AclGrant, ArchivedAclGrant};
impl Server {
pub async fn acl_set(
&self,
changes: &mut Vec<AclGrant>,
current: Option<&[AclGrant]>,
acl_changes: MaybePatchValue,
) -> Result<(), SetError> {
match acl_changes {
MaybePatchValue::Value(Value::List(values)) => {
*changes = self.map_acl_set(values).await?;
}
MaybePatchValue::Patch(patch) => {
let (mut patch, is_update) = self.map_acl_patch(patch).await?;
if let Some(changes_) = current {
*changes = changes_.to_vec();
}
if let Some(is_set) = is_update {
if !patch.grants.is_empty() {
if let Some(acl_item) = changes
.iter_mut()
.find(|item| item.account_id == patch.account_id)
{
let item = patch.grants.pop().unwrap();
if is_set {
acl_item.grants.insert(item);
} else {
acl_item.grants.remove(item);
if acl_item.grants.is_empty() {
changes.retain(|item| item.account_id != patch.account_id);
}
}
} else if is_set {
changes.push(patch);
}
}
} else if !patch.grants.is_empty() {
if let Some(acl_item) = changes
.iter_mut()
.find(|item| item.account_id == patch.account_id)
{
acl_item.grants = patch.grants;
} else {
changes.push(patch);
}
} else {
changes.retain(|item| item.account_id != patch.account_id);
}
}
_ => {
return Err(SetError::invalid_properties()
.with_property(Property::Acl)
.with_description("Invalid ACL property."));
}
}
Ok(())
}
pub async fn acl_get(
&self,
value: &[AclGrant],
access_token: &AccessToken,
account_id: u32,
) -> Value {
if access_token.is_member(account_id)
|| value.iter().any(|item| {
access_token.is_member(item.account_id) && item.grants.contains(Acl::Administer)
})
{
let mut acl_obj = jmap_proto::types::value::Object::with_capacity(value.len() / 2);
for item in value {
if let Some(name) = self
.store()
.get_principal_name(item.account_id)
.await
.unwrap_or_default()
{
acl_obj.append(
Property::_T(name),
item.grants
.map(|acl_item| Value::Text(acl_item.to_string()))
.collect::<Vec<_>>(),
);
}
}
Value::Object(acl_obj)
} else {
Value::Null
}
}
pub async fn refresh_acls(&self, acl_changes: &[AclGrant], current: Option<&[AclGrant]>) {
let mut changed_principals = ChangedPrincipals::new();
if let Some(acl_current) = current {
@@ -205,77 +103,4 @@ impl Server {
self.invalidate_principal_caches(changed_principals).await;
}
pub async fn map_acl_set(&self, acl_set: Vec<Value>) -> Result<Vec<AclGrant>, SetError> {
let mut acls = Vec::with_capacity(acl_set.len() / 2);
for item in acl_set.chunks_exact(2) {
if let (Value::Text(account_name), Value::UnsignedInt(grants)) = (&item[0], &item[1]) {
match self
.core
.storage
.directory
.query(QueryParams::name(account_name).with_return_member_of(false))
.await
{
Ok(Some(principal)) => {
acls.push(AclGrant {
account_id: principal.id(),
grants: Bitmap::from(*grants),
});
}
Ok(None) => {
return Err(SetError::invalid_properties()
.with_property(Property::Acl)
.with_description(format!("Account {account_name} does not exist.")));
}
_ => {
return Err(SetError::forbidden()
.with_property(Property::Acl)
.with_description("Temporary server failure during lookup"));
}
}
} else {
return Err(SetError::invalid_properties()
.with_property(Property::Acl)
.with_description("Invalid ACL value found."));
}
}
Ok(acls)
}
pub async fn map_acl_patch(
&self,
acl_patch: Vec<Value>,
) -> Result<(AclGrant, Option<bool>), SetError> {
if let (Value::Text(account_name), Value::UnsignedInt(grants)) =
(&acl_patch[0], &acl_patch[1])
{
match self
.core
.storage
.directory
.query(QueryParams::name(account_name).with_return_member_of(false))
.await
{
Ok(Some(principal)) => Ok((
AclGrant {
account_id: principal.id(),
grants: Bitmap::from(*grants),
},
acl_patch.get(2).map(|v| v.as_bool().unwrap_or(false)),
)),
Ok(None) => Err(SetError::invalid_properties()
.with_property(Property::Acl)
.with_description(format!("Account {account_name} does not exist."))),
_ => Err(SetError::forbidden()
.with_property(Property::Acl)
.with_description("Temporary server failure during lookup")),
}
} else {
Err(SetError::invalid_properties()
.with_property(Property::Acl)
.with_description("Invalid ACL value found."))
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::assert_is_unique_uid;
use crate::{
DavError, DavMethod,
common::{
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
file::DavFileResource,
};
use calcard::common::timezone::Tz;
use common::{DavName, Server, auth::AccessToken};
use dav_proto::{Depth, RequestHeaders};
@@ -14,23 +23,12 @@ use groupware::{
};
use http_proto::HttpResponse;
use hyper::StatusCode;
use jmap_proto::types::{
use store::write::{BatchBuilder, now};
use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection},
};
use store::write::{BatchBuilder, now};
use trc::AddContext;
use crate::{
DavError, DavMethod,
common::{
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
file::DavFileResource,
};
use super::assert_is_unique_uid;
pub(crate) trait CalendarCopyMoveRequestHandler: Sync + Send {
fn handle_calendar_copy_move_request(

View File

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

View File

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

View File

@@ -4,17 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use common::{Server, auth::AccessToken};
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
use http_proto::HttpResponse;
use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use trc::AddContext;
use crate::{
DavError, DavMethod,
common::{
@@ -23,6 +12,16 @@ use crate::{
uri::DavUriResource,
},
};
use common::{Server, auth::AccessToken};
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
use groupware::{cache::GroupwareCache, calendar::CalendarEvent};
use http_proto::HttpResponse;
use hyper::StatusCode;
use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CalendarGetRequestHandler: Sync + Send {
fn handle_calendar_get_request(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,6 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::assert_is_unique_uid;
use crate::{
DavError, DavMethod,
common::{
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
file::DavFileResource,
};
use common::{DavName, Server, auth::AccessToken};
use dav_proto::{Depth, RequestHeaders};
use groupware::{
@@ -13,23 +22,12 @@ use groupware::{
};
use http_proto::HttpResponse;
use hyper::StatusCode;
use jmap_proto::types::{
use store::write::BatchBuilder;
use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection, VanishedCollection},
};
use store::write::BatchBuilder;
use trc::AddContext;
use crate::{
DavError, DavMethod,
common::{
lock::{LockRequestHandler, ResourceState},
uri::DavUriResource,
},
file::DavFileResource,
};
use super::assert_is_unique_uid;
pub(crate) trait CardCopyMoveRequestHandler: Sync + Send {
fn handle_card_copy_move_request(

View File

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

View File

@@ -4,17 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use common::{Server, auth::AccessToken};
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
use groupware::{cache::GroupwareCache, contact::ContactCard};
use http_proto::HttpResponse;
use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use trc::AddContext;
use crate::{
DavError, DavMethod,
common::{
@@ -23,6 +12,16 @@ use crate::{
uri::DavUriResource,
},
};
use common::{Server, auth::AccessToken};
use dav_proto::{RequestHeaders, schema::property::Rfc1123DateTime};
use groupware::{cache::GroupwareCache, contact::ContactCard};
use http_proto::HttpResponse;
use hyper::StatusCode;
use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CardGetRequestHandler: Sync + Send {
fn handle_card_get_request(

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,22 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use calcard::{Entry, Parser};
use common::{DavName, Server, auth::AccessToken};
use dav_proto::{
RequestHeaders, Return,
schema::{property::Rfc1123DateTime, response::CardCondition},
};
use groupware::{cache::GroupwareCache, contact::ContactCard};
use http_proto::HttpResponse;
use hyper::StatusCode;
use jmap_proto::types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
use store::write::BatchBuilder;
use trc::AddContext;
use super::assert_is_unique_uid;
use crate::{
DavError, DavErrorCondition, DavMethod,
common::{
@@ -30,8 +15,21 @@ use crate::{
file::DavFileResource,
fix_percent_encoding,
};
use super::assert_is_unique_uid;
use calcard::{Entry, Parser};
use common::{DavName, Server, auth::AccessToken};
use dav_proto::{
RequestHeaders, Return,
schema::{property::Rfc1123DateTime, response::CardCondition},
};
use groupware::{cache::GroupwareCache, contact::ContactCard};
use http_proto::HttpResponse;
use hyper::StatusCode;
use store::write::BatchBuilder;
use trc::AddContext;
use types::{
acl::Acl,
collection::{Collection, SyncCollection},
};
pub(crate) trait CardUpdateRequestHandler: Sync + Send {
fn handle_card_update_request(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use jmap_proto::types::type_state::DataType;
use types::type_state::DataType;
use utils::map::bitmap::Bitmap;
#[derive(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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