Look for Stalwart's capability where Stalwart advertises it
Self-service credentials, the About page and Files all keyed off `urn:stalwart:jmap`, and all three looked for it in the session-level `capabilities`. Stalwart has never put it there. `Session::new` builds that list from a fixed set the capability is not part of, in any 0.16.x from 0.16.0 to 0.16.19; it is handed out per-account instead, so it arrives in `primaryAccounts` and in each account's `accountCapabilities`. So every real 0.16 server read as pre-0.16. Password changes, 2FA and app passwords fell back to `POST /api/account/auth`, which 0.16 removed, and reported that the server offers no self-service credential management. About named the wrong generation. Files ran the pre-0.16 path, omitting `nodeType` and listing the tree through get. Look in all three places, on both sides. Two nearby soft spots go with it: a transport error while probing the registry no longer downgrades a server to the legacy path -- which would have posted the current password to an endpoint that is not there -- and a locale request that is merely refused no longer discards a generation the capability had already settled. The mock advertised the capability in the session, which is why no test ever caught this; it now advertises it where the real server does, and validates `using` by the urn rather than by the session, as Stalwart does. Put the old lookup back and nine tests fail. Stalwart still publishes no version number to clients -- VERSION_PUBLIC is a fixed "1.0.0" -- so About continues to report the generation and edition, which are now the right ones.
This commit is contained in:
+17
-1
@@ -123,6 +123,20 @@ export class JmapClient {
|
||||
return Boolean(acc && cap in acc.accountCapabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the server carries a capability at all, wherever it chose to
|
||||
* advertise it.
|
||||
*
|
||||
* Stalwart hands `urn:stalwart:jmap` out per-account rather than putting it
|
||||
* in the session-level `capabilities`, so `hasCapability` alone reports every
|
||||
* real 0.16 server as though it were older. Look in all three places.
|
||||
*/
|
||||
hasCapabilityAnywhere(cap: string): boolean {
|
||||
if (this.hasCapability(cap)) return true;
|
||||
if (this.session?.primaryAccounts && cap in this.session.primaryAccounts) return true;
|
||||
return Object.values(this.session?.accounts ?? {}).some((a) => cap in (a.accountCapabilities ?? {}));
|
||||
}
|
||||
|
||||
primaryAccount(cap: string): Id | null {
|
||||
return this.session?.primaryAccounts[cap] ?? null;
|
||||
}
|
||||
@@ -210,7 +224,9 @@ export class JmapClient {
|
||||
*/
|
||||
private supportedUsing(using: string[]): string[] {
|
||||
if (!this.session?.capabilities) return using;
|
||||
return using.filter((u) => u === CAP.core || this.hasCapability(u));
|
||||
// Anywhere counts: a capability advertised per-account is one the server
|
||||
// has, and Stalwart advertises its own that way and no other.
|
||||
return using.filter((u) => u === CAP.core || this.hasCapabilityAnywhere(u));
|
||||
}
|
||||
|
||||
/** Low-level request: send invocations verbatim, return raw response. */
|
||||
|
||||
Reference in New Issue
Block a user