Skip to content

Commit c7c996d

Browse files
committed
JMAP protocol layer refactoring (part 6)
1 parent 7c946a6 commit c7c996d

59 files changed

Lines changed: 1307 additions & 1028 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/common/src/config/groupware.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub struct GroupwareConfig {
4747

4848
// File storage settings
4949
pub max_file_size: usize,
50+
51+
// Sharing settings
52+
pub max_shares_per_item: usize,
5053
}
5154

5255
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
@@ -178,6 +181,7 @@ impl GroupwareConfig {
178181
} else {
179182
None
180183
},
184+
max_shares_per_item: config.property("sharing.max-shares-per-item").unwrap_or(10),
181185
itip_http_rsvp_expiration: config
182186
.property_or_default::<Duration>("calendar.scheduling.http-rsvp.expiration", "90d")
183187
.map(|d| d.as_secs())

crates/http/src/request.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use jmap::{
5050
websocket::upgrade::WebSocketUpgrade,
5151
};
5252
use jmap_proto::request::{Request, capability::Session};
53-
use std::{net::IpAddr, sync::Arc};
53+
use std::{net::IpAddr, str::FromStr, sync::Arc};
5454
use store::dispatch::lookup::KeyValue;
5555
use trc::SecurityEvent;
5656
use types::{blob::BlobId, id::Id};
@@ -93,7 +93,7 @@ impl ParseHttp for Server {
9393
let (_in_flight, access_token) =
9494
self.authenticate_headers(&req, &session, false).await?;
9595

96-
let request = fetch_body(
96+
let bytes = fetch_body(
9797
&mut req,
9898
if !access_token.has_permission(Permission::UnlimitedUploads) {
9999
self.core.jmap.upload_max_size
@@ -103,17 +103,18 @@ impl ParseHttp for Server {
103103
session.session_id,
104104
)
105105
.await
106-
.ok_or_else(|| trc::LimitEvent::SizeRequest.into_err())
107-
.and_then(|bytes| {
108-
Request::parse(
109-
&bytes,
110-
self.core.jmap.request_max_calls,
111-
self.core.jmap.request_max_size,
112-
)
113-
})?;
106+
.ok_or_else(|| trc::LimitEvent::SizeRequest.into_err())?;
114107

115108
return Ok(self
116-
.handle_jmap_request(request, access_token, &session)
109+
.handle_jmap_request(
110+
Request::parse(
111+
&bytes,
112+
self.core.jmap.request_max_calls,
113+
self.core.jmap.request_max_size,
114+
)?,
115+
access_token,
116+
&session,
117+
)
117118
.await
118119
.into_http_response());
119120
}
@@ -123,7 +124,7 @@ impl ParseHttp for Server {
123124
self.authenticate_headers(&req, &session, false).await?;
124125

125126
if let (Some(_), Some(blob_id), Some(name)) = (
126-
path.next().and_then(|p| Id::from_bytes(p.as_bytes())),
127+
path.next().and_then(|p| Id::from_str(p).ok()),
127128
path.next().and_then(BlobId::from_base32),
128129
path.next(),
129130
) {
@@ -151,9 +152,7 @@ impl ParseHttp for Server {
151152
let (_in_flight, access_token) =
152153
self.authenticate_headers(&req, &session, false).await?;
153154

154-
if let Some(account_id) =
155-
path.next().and_then(|p| Id::from_bytes(p.as_bytes()))
156-
{
155+
if let Some(account_id) = path.next().and_then(|p| Id::from_str(p).ok()) {
157156
return match fetch_body(
158157
&mut req,
159158
if !access_token.has_permission(Permission::UnlimitedUploads) {

crates/imap-proto/src/protocol/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ impl From<Flag> for Keyword {
316316
Flag::Deleted => Keyword::Deleted,
317317
Flag::Forwarded => Keyword::Forwarded,
318318
Flag::MDNSent => Keyword::MdnSent,
319-
Flag::Keyword(value) => Keyword::Other(value),
319+
Flag::Keyword(value) => Keyword::from_other(value),
320320
}
321321
}
322322
}

crates/imap/src/op/acl.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,13 @@ impl<T: SessionStream> Session<T> {
341341
}
342342
}
343343

344+
if mailbox.acls.len() > data.server.core.groupware.max_shares_per_item {
345+
return Err(trc::ImapEvent::Error
346+
.into_err()
347+
.details("Maximum shares per item exceeded")
348+
.caused_by(trc::location!()));
349+
}
350+
344351
let grants = mailbox
345352
.acls
346353
.iter()

crates/jmap-proto/src/error/set.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ impl<T: Property> From<(T, T)> for InvalidProperty<T> {
198198
}
199199
}
200200

201+
impl<T: Property> From<Key<'static, T>> for InvalidProperty<T> {
202+
fn from(property: Key<'static, T>) -> Self {
203+
InvalidProperty::Property(property)
204+
}
205+
}
206+
201207
impl<T: Property> serde::Serialize for InvalidProperty<T> {
202208
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
203209
where

crates/jmap-proto/src/method/copy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct CopyResponse<T: JmapObject> {
5050

5151
#[serde(rename = "notCreated")]
5252
#[serde(skip_serializing_if = "VecMap::is_empty")]
53-
pub not_created: VecMap<MaybeInvalid<Id>, SetError<T::Property>>,
53+
pub not_created: VecMap<Id, SetError<T::Property>>,
5454
}
5555

5656
#[derive(Debug, Clone, Default)]

crates/jmap-proto/src/method/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub struct ParseEmailResponse {
4343

4444
#[serde(rename = "notFound")]
4545
#[serde(skip_serializing_if = "Vec::is_empty")]
46-
pub not_found: Vec<MaybeInvalid<BlobId>>,
46+
pub not_found: Vec<BlobId>,
4747
}
4848

4949
impl<'de> DeserializeArguments<'de> for ParseEmailRequest {

crates/jmap-proto/src/method/search_snippet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub struct GetSearchSnippetResponse {
3434

3535
#[serde(rename = "notFound")]
3636
#[serde(skip_serializing_if = "Vec::is_empty")]
37-
pub not_found: Vec<MaybeInvalid<Id>>,
37+
pub not_found: Vec<Id>,
3838
}
3939

4040
#[derive(serde::Serialize, Clone, Debug)]

crates/jmap-proto/src/method/set.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct SetRequest<'x, T: JmapObject> {
3030
pub create: Option<VecMap<String, Value<'x, T::Property, T::Element>>>,
3131
pub update: Option<VecMap<MaybeInvalid<Id>, Value<'x, T::Property, T::Element>>>,
3232
pub destroy: Option<MaybeResultReference<Vec<MaybeInvalid<Id>>>>,
33-
pub arguments: T::SetArguments,
33+
pub arguments: T::SetArguments<'x>,
3434
}
3535

3636
#[derive(Debug, Clone, Default, serde::Serialize)]
@@ -66,11 +66,11 @@ pub struct SetResponse<T: JmapObject> {
6666

6767
#[serde(rename = "notUpdated")]
6868
#[serde(skip_serializing_if = "VecMap::is_empty")]
69-
pub not_updated: VecMap<MaybeInvalid<Id>, SetError<T::Property>>,
69+
pub not_updated: VecMap<Id, SetError<T::Property>>,
7070

7171
#[serde(rename = "notDestroyed")]
7272
#[serde(skip_serializing_if = "VecMap::is_empty")]
73-
pub not_destroyed: VecMap<MaybeInvalid<Id>, SetError<T::Property>>,
73+
pub not_destroyed: VecMap<Id, SetError<T::Property>>,
7474
}
7575

7676
impl<'de, T: JmapObject> DeserializeArguments<'de> for SetRequest<'de, T> {
@@ -236,7 +236,7 @@ impl<T: JmapObject> SetResponse<T> {
236236

237237
pub fn invalid_property_update(
238238
&mut self,
239-
id: MaybeInvalid<Id>,
239+
id: Id,
240240
property: impl Into<InvalidProperty<T::Property>>,
241241
) {
242242
self.not_updated.append(
@@ -258,6 +258,30 @@ impl<T: JmapObject> SetResponse<T> {
258258
}
259259
}
260260

261+
pub fn get_object_by_id(
262+
&mut self,
263+
id: Id,
264+
) -> Option<&mut Value<'static, T::Property, T::Element>> {
265+
if let Some(obj) = self.updated.get_mut(&id) {
266+
if let Some(obj) = obj {
267+
return Some(obj);
268+
} else {
269+
*obj = Some(Value::Object(Map::with_capacity(1)));
270+
return obj.as_mut().unwrap().into();
271+
}
272+
}
273+
274+
(&mut self.created)
275+
.into_iter()
276+
.map(|(_, obj)| obj)
277+
.find(|obj| {
278+
obj.as_object_and_get(&Key::Property(T::ID_PROPERTY))
279+
.and_then(|v| v.as_element())
280+
.and_then(|v| v.as_id())
281+
.is_some_and(|oid| oid == id)
282+
})
283+
}
284+
261285
pub fn has_changes(&self) -> bool {
262286
!self.created.is_empty() || !self.updated.is_empty() || !self.destroyed.is_empty()
263287
}

crates/jmap-proto/src/object/blob.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
object::{AnyId, JmapObject, JmapObjectId, MaybeReference, parse_ref},
99
request::deserialize::DeserializeArguments,
1010
};
11-
use jmap_tools::{Element, Key, Null, Property};
11+
use jmap_tools::{Element, Key, Property};
1212
use std::{borrow::Cow, str::FromStr};
1313
use types::{blob::BlobId, id::Id};
1414

@@ -171,15 +171,13 @@ impl JmapObject for Blob {
171171

172172
type Id = BlobId;
173173

174-
type Right = Null;
175-
176174
type Filter = ();
177175

178176
type Comparator = ();
179177

180178
type GetArguments = BlobGetArguments;
181179

182-
type SetArguments = ();
180+
type SetArguments<'de> = ();
183181

184182
type QueryArguments = ();
185183

@@ -194,12 +192,6 @@ impl From<BlobId> for BlobValue {
194192
}
195193
}
196194

197-
impl From<Null> for BlobProperty {
198-
fn from(_: Null) -> Self {
199-
unimplemented!()
200-
}
201-
}
202-
203195
impl JmapObjectId for BlobValue {
204196
fn as_id(&self) -> Option<Id> {
205197
None

0 commit comments

Comments
 (0)