diff --git a/crates/jmap-proto/src/request/capability.rs b/crates/jmap-proto/src/request/capability.rs index 22124a6..1c31621 100644 --- a/crates/jmap-proto/src/request/capability.rs +++ b/crates/jmap-proto/src/request/capability.rs @@ -95,6 +95,9 @@ pub enum Capability { WebPushVapid = 1 << 18, #[serde(rename(serialize = "urn:ietf:params:jmap:emailpush"))] EmailPush = 1 << 19, + // inbuxa: the fork's own capability (contract C-1, multi-tenancy MT-22) + #[serde(rename(serialize = "urn:inbuxa:jmap"))] + Inbuxa = 1 << 20, } #[derive(Debug, Clone, Copy, Default)] @@ -124,9 +127,18 @@ pub enum Capabilities { Calendar(CalendarCapabilities), FileNode(FileNodeCapabilities), WebPush(WebPushCapabilities), + Inbuxa(InbuxaAccountCapabilities), Empty(EmptyCapabilities), } +/// inbuxa: `urn:inbuxa:jmap` on the signed-in principal's own account. +#[derive(Debug, Clone, serde::Serialize)] +pub struct InbuxaAccountCapabilities { + /// The logo that applies to the principal (MT-22): a URL or a data URL. + #[serde(rename(serialize = "logo"))] + pub logo: Option, +} + #[derive(Debug, Clone, serde::Serialize)] pub struct CoreCapabilities { #[serde(rename(serialize = "maxSizeUpload"))] @@ -334,6 +346,7 @@ impl Capability { Capability::Stalwart => "urn:stalwart:jmap", Capability::WebPushVapid => "urn:ietf:params:jmap:webpush-vapid", Capability::EmailPush => "urn:ietf:params:jmap:emailpush", + Capability::Inbuxa => "urn:inbuxa:jmap", } } @@ -480,6 +493,7 @@ impl Capability { "urn:stalwart:jmap" => Capability::Stalwart, "urn:ietf:params:jmap:webpush-vapid" => Capability::WebPushVapid, "urn:ietf:params:jmap:emailpush" => Capability::EmailPush, + "urn:inbuxa:jmap" => Capability::Inbuxa, ) } } diff --git a/crates/jmap/src/api/session.rs b/crates/jmap/src/api/session.rs index 2c0fe96..291b113 100644 --- a/crates/jmap/src/api/session.rs +++ b/crates/jmap/src/api/session.rs @@ -6,7 +6,7 @@ use common::{Server, auth::AccessToken}; use jmap_proto::request::capability::{ - Account, Capabilities, Capability, EmptyCapabilities, Session, + Account, Capabilities, Capability, EmptyCapabilities, InbuxaAccountCapabilities, Session, }; use registry::schema::enums::Permission; use std::future::Future; @@ -55,6 +55,19 @@ impl SessionHandler for Server { .unwrap_or_else(|| Capabilities::Empty(EmptyCapabilities::default())), ); } + // inbuxa: MT-22: the logo that applies to the signed-in principal + let logo = + inbuxa_features::tenancy::logo::for_account(self.registry(), access_token.account_id()) + .await + .caused_by(trc::location!())?; + session.capabilities.append( + Capability::Inbuxa, + Capabilities::Empty(EmptyCapabilities::default()), + ); + account.account_capabilities.append( + Capability::Inbuxa, + Capabilities::Inbuxa(InbuxaAccountCapabilities { logo }), + ); session.accounts.append(account_id, account); // Add secondary accounts @@ -123,7 +136,10 @@ impl AccountCapabilities for AccessToken { | Capability::Principals | Capability::PrincipalsAvailability | Capability::Stalwart => return true, - Capability::Core | Capability::PrincipalsOwner | Capability::WebPushVapid => { + Capability::Core + | Capability::PrincipalsOwner + | Capability::WebPushVapid + | Capability::Inbuxa => { return false; } };