IMAP mailbox synchronization
This commit is contained in:
@@ -192,12 +192,7 @@ impl JMAP {
|
||||
|
||||
session.add_account(
|
||||
(*id).into(),
|
||||
self.directory
|
||||
.principal_by_id(*id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.map(|p| p.name)
|
||||
.unwrap_or_else(|| Id::from(*id).to_string()),
|
||||
self.get_account_name(*id).await,
|
||||
is_personal,
|
||||
is_readonly,
|
||||
Some(&[Capability::Core, Capability::Mail, Capability::WebSocket]),
|
||||
@@ -206,6 +201,15 @@ impl JMAP {
|
||||
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
pub async fn get_account_name(&self, account_id: u32) -> String {
|
||||
self.directory
|
||||
.principal_by_id(account_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.map(|p| p.name)
|
||||
.unwrap_or_else(|| Id::from(account_id).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Config {
|
||||
|
||||
@@ -50,20 +50,7 @@ impl JMAP {
|
||||
.and_then(|h| h.split_once(' ').map(|(l, t)| (l, t.trim().to_string())))
|
||||
{
|
||||
let session = if let Some(account_id) = self.sessions.get(&token) {
|
||||
if let Some(access_token) = self.access_tokens.get(&account_id) {
|
||||
access_token.into()
|
||||
} else {
|
||||
// Refresh ACL token
|
||||
self.get_access_token(account_id).await.map(|access_token| {
|
||||
let access_token = Arc::new(access_token);
|
||||
self.access_tokens.insert(
|
||||
account_id,
|
||||
access_token.clone(),
|
||||
Instant::now() + self.config.session_cache_ttl,
|
||||
);
|
||||
access_token
|
||||
})
|
||||
}
|
||||
self.get_cached_access_token(account_id).await
|
||||
} else {
|
||||
let addr = self.build_remote_addr(req, remote_ip);
|
||||
if mechanism.eq_ignore_ascii_case("basic") {
|
||||
@@ -108,19 +95,11 @@ impl JMAP {
|
||||
self.is_anonymous_allowed(addr)?;
|
||||
None
|
||||
}
|
||||
.map(|session| {
|
||||
let session = Arc::new(session);
|
||||
self.sessions.insert(
|
||||
token,
|
||||
session.primary_id(),
|
||||
Instant::now() + self.config.session_cache_ttl,
|
||||
);
|
||||
self.access_tokens.insert(
|
||||
session.primary_id(),
|
||||
session.clone(),
|
||||
Instant::now() + self.config.session_cache_ttl,
|
||||
);
|
||||
session
|
||||
.map(|access_token| {
|
||||
let access_token = Arc::new(access_token);
|
||||
self.cache_session(token, &access_token);
|
||||
self.cache_access_token(access_token.clone());
|
||||
access_token
|
||||
})
|
||||
};
|
||||
|
||||
@@ -138,6 +117,35 @@ impl JMAP {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cache_session(&self, session_id: String, access_token: &AccessToken) {
|
||||
self.sessions.insert(
|
||||
session_id,
|
||||
access_token.primary_id(),
|
||||
Instant::now() + self.config.session_cache_ttl,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cache_access_token(&self, access_token: Arc<AccessToken>) {
|
||||
self.access_tokens.insert(
|
||||
access_token.primary_id(),
|
||||
access_token,
|
||||
Instant::now() + self.config.session_cache_ttl,
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn get_cached_access_token(&self, primary_id: u32) -> Option<Arc<AccessToken>> {
|
||||
if let Some(access_token) = self.access_tokens.get(&primary_id) {
|
||||
access_token.into()
|
||||
} else {
|
||||
// Refresh ACL token
|
||||
self.get_access_token(primary_id).await.map(|access_token| {
|
||||
let access_token = Arc::new(access_token);
|
||||
self.cache_access_token(access_token.clone());
|
||||
access_token
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_remote_addr(
|
||||
&self,
|
||||
req: &hyper::Request<hyper::body::Incoming>,
|
||||
|
||||
@@ -101,6 +101,10 @@ impl AccessToken {
|
||||
|| self.member_of.contains(&SUPERUSER_ID)
|
||||
}
|
||||
|
||||
pub fn is_primary_id(&self, account_id: u32) -> bool {
|
||||
self.primary_id == account_id
|
||||
}
|
||||
|
||||
pub fn is_super_user(&self) -> bool {
|
||||
self.primary_id == SUPERUSER_ID || self.member_of.contains(&SUPERUSER_ID)
|
||||
}
|
||||
@@ -109,6 +113,19 @@ 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();
|
||||
self.member_of
|
||||
.iter()
|
||||
.chain(self.access_to.iter().filter_map(move |(id, cols)| {
|
||||
if cols.contains(collection) {
|
||||
id.into()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn has_access(&self, to_account_id: u32, to_collection: impl Into<Collection>) -> bool {
|
||||
let to_collection = to_collection.into();
|
||||
self.is_member(to_account_id)
|
||||
|
||||
@@ -85,10 +85,7 @@ impl JMAP {
|
||||
|
||||
let (items_sent, mut changelog) = match &request.since_state {
|
||||
State::Initial => {
|
||||
let changelog = self
|
||||
.changes_(account_id, collection, Query::All)
|
||||
.await?
|
||||
.unwrap();
|
||||
let changelog = self.changes_(account_id, collection, Query::All).await?;
|
||||
if changelog.changes.is_empty() && changelog.from_change_id == 0 {
|
||||
return Ok(response);
|
||||
}
|
||||
@@ -98,12 +95,7 @@ impl JMAP {
|
||||
State::Exact(change_id) => (
|
||||
0,
|
||||
self.changes_(account_id, collection, Query::Since(*change_id))
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
MethodError::InvalidArguments(
|
||||
"The specified stateId does could not be found.".to_string(),
|
||||
)
|
||||
})?,
|
||||
.await?,
|
||||
),
|
||||
State::Intermediate(intermediate_state) => {
|
||||
let mut changelog = self
|
||||
@@ -112,12 +104,7 @@ impl JMAP {
|
||||
collection,
|
||||
Query::RangeInclusive(intermediate_state.from_id, intermediate_state.to_id),
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
MethodError::InvalidArguments(
|
||||
"The specified stateId does could not be found.".to_string(),
|
||||
)
|
||||
})?;
|
||||
.await?;
|
||||
if intermediate_state.items_sent >= changelog.changes.len() {
|
||||
(
|
||||
0,
|
||||
@@ -126,12 +113,7 @@ impl JMAP {
|
||||
collection,
|
||||
Query::Since(intermediate_state.to_id),
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
MethodError::InvalidArguments(
|
||||
"The specified stateId does could not be found.".to_string(),
|
||||
)
|
||||
})?,
|
||||
.await?,
|
||||
)
|
||||
} else {
|
||||
changelog.changes.drain(
|
||||
@@ -189,12 +171,12 @@ impl JMAP {
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn changes_(
|
||||
pub async fn changes_(
|
||||
&self,
|
||||
account_id: u32,
|
||||
collection: Collection,
|
||||
query: Query,
|
||||
) -> Result<Option<Changes>, MethodError> {
|
||||
) -> Result<Changes, MethodError> {
|
||||
self.store
|
||||
.changes(account_id, collection, query)
|
||||
.await
|
||||
|
||||
@@ -279,7 +279,7 @@ impl JMAP {
|
||||
}
|
||||
}
|
||||
|
||||
async fn mailbox_unread_tags(
|
||||
pub async fn mailbox_unread_tags(
|
||||
&self,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
|
||||
@@ -495,7 +495,14 @@ impl JMAP {
|
||||
#[inline(always)]
|
||||
pub fn is_valid_role(role: &str) -> bool {
|
||||
[
|
||||
"inbox", "trash", "spam", "junk", "drafts", "archive", "sent",
|
||||
"inbox",
|
||||
"trash",
|
||||
"spam",
|
||||
"junk",
|
||||
"drafts",
|
||||
"archive",
|
||||
"sent",
|
||||
"important",
|
||||
]
|
||||
.contains(&role)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user