2015-06-23 15:41:25 +00:00
|
|
|
/*
|
2016-01-07 04:06:39 +00:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2017-06-21 13:04:43 +00:00
|
|
|
Copyright 2017 Vector Creations Ltd.
|
2019-12-05 15:20:30 +00:00
|
|
|
Copyright 2017, 2018, 2019 New Vector Ltd
|
2023-02-20 14:46:07 +00:00
|
|
|
Copyright 2019 - 2023 The Matrix.org Foundation C.I.C.
|
2015-06-23 15:41:25 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2023-08-08 07:16:04 +00:00
|
|
|
import {
|
|
|
|
EventTimeline,
|
|
|
|
EventTimelineSet,
|
2024-01-31 15:52:23 +00:00
|
|
|
ICreateClientOpts,
|
2023-08-09 07:18:41 +00:00
|
|
|
IStartClientOpts,
|
|
|
|
MatrixClient,
|
2023-08-15 15:00:17 +00:00
|
|
|
MemoryStore,
|
2024-01-31 15:52:23 +00:00
|
|
|
PendingEventOrdering,
|
|
|
|
RoomNameState,
|
|
|
|
RoomNameType,
|
2023-10-12 00:49:07 +00:00
|
|
|
TokenRefreshFunction,
|
2023-08-08 07:16:04 +00:00
|
|
|
} from "matrix-js-sdk/src/matrix";
|
2024-03-25 17:44:45 +00:00
|
|
|
import { VerificationMethod } from "matrix-js-sdk/src/types";
|
2019-12-20 02:08:43 +00:00
|
|
|
import * as utils from "matrix-js-sdk/src/utils";
|
2021-10-22 22:23:32 +00:00
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
|
|
|
|
2017-06-13 11:46:49 +00:00
|
|
|
import createMatrixClient from "./utils/createMatrixClient";
|
2017-11-17 17:48:42 +00:00
|
|
|
import SettingsStore from "./settings/SettingsStore";
|
2017-12-07 17:10:45 +00:00
|
|
|
import MatrixActionCreators from "./actions/MatrixActionCreators";
|
2018-11-16 11:31:46 +00:00
|
|
|
import Modal from "./Modal";
|
2019-02-22 23:33:20 +00:00
|
|
|
import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler";
|
2019-04-04 10:59:53 +00:00
|
|
|
import * as StorageManager from "./utils/StorageManager";
|
2019-08-22 13:44:09 +00:00
|
|
|
import IdentityAuthClient from "./IdentityAuthClient";
|
2024-06-24 09:14:42 +00:00
|
|
|
import { crossSigningCallbacks } from "./SecurityManager";
|
2024-04-12 15:15:17 +00:00
|
|
|
import { ModuleRunner } from "./modules/ModuleRunner";
|
2022-09-07 15:42:39 +00:00
|
|
|
import { SlidingSyncManager } from "./SlidingSyncManager";
|
2023-06-05 17:12:23 +00:00
|
|
|
import { _t, UserFriendlyError } from "./languageHandler";
|
2022-12-16 17:10:26 +00:00
|
|
|
import { SettingLevel } from "./settings/SettingLevel";
|
2023-03-03 13:31:51 +00:00
|
|
|
import MatrixClientBackedController from "./settings/controllers/MatrixClientBackedController";
|
2023-03-31 09:08:45 +00:00
|
|
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
|
|
|
import PlatformPeg from "./PlatformPeg";
|
2023-09-25 11:18:15 +00:00
|
|
|
import { formatList } from "./utils/FormattingUtils";
|
2024-01-02 11:48:12 +00:00
|
|
|
import SdkConfig from "./SdkConfig";
|
2024-01-18 11:18:55 +00:00
|
|
|
import { Features } from "./settings/Settings";
|
2015-07-20 12:19:47 +00:00
|
|
|
|
2020-05-25 21:59:15 +00:00
|
|
|
export interface IMatrixClientCreds {
|
2020-06-18 13:32:43 +00:00
|
|
|
homeserverUrl: string;
|
2021-12-02 13:46:44 +00:00
|
|
|
identityServerUrl?: string;
|
2020-06-18 13:32:43 +00:00
|
|
|
userId: string;
|
2020-10-07 11:14:36 +00:00
|
|
|
deviceId?: string;
|
2023-07-04 13:49:27 +00:00
|
|
|
accessToken: string;
|
2023-09-19 00:06:19 +00:00
|
|
|
refreshToken?: string;
|
2020-10-06 11:42:28 +00:00
|
|
|
guest?: boolean;
|
2020-06-18 13:32:43 +00:00
|
|
|
pickleKey?: string;
|
2020-09-30 04:52:47 +00:00
|
|
|
freshLogin?: boolean;
|
2016-08-03 09:46:42 +00:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:52:28 +00:00
|
|
|
export interface MatrixClientPegAssignOpts {
|
|
|
|
/**
|
|
|
|
* If we are using Rust crypto, a key with which to encrypt the indexeddb.
|
|
|
|
*
|
|
|
|
* If provided, it must be exactly 32 bytes of data. If both this and
|
|
|
|
* {@link MatrixClientPegAssignOpts.rustCryptoStorePassword} are undefined,
|
|
|
|
* the store will be unencrypted.
|
|
|
|
*/
|
|
|
|
rustCryptoStoreKey?: Uint8Array;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If we are using Rust crypto, a password which will be used to derive a key to encrypt the store with.
|
|
|
|
*
|
|
|
|
* An alternative to {@link MatrixClientPegAssignOpts.rustCryptoStoreKey}. Ignored if `rustCryptoStoreKey` is set.
|
|
|
|
*
|
|
|
|
* Deriving a key from a password is (deliberately) a slow operation, so prefer to pass a `rustCryptoStoreKey`
|
|
|
|
* directly where possible.
|
|
|
|
*/
|
|
|
|
rustCryptoStorePassword?: string;
|
|
|
|
}
|
|
|
|
|
2022-05-10 02:34:27 +00:00
|
|
|
/**
|
|
|
|
* Holds the current instance of the `MatrixClient` to use across the codebase.
|
|
|
|
* Looking for an `MatrixClient`? Just look for the `MatrixClientPeg` on the peg
|
|
|
|
* board. "Peg" is the literal meaning of something you hang something on. So
|
|
|
|
* you'll find a `MatrixClient` hanging on the `MatrixClientPeg`.
|
|
|
|
*/
|
2020-05-25 22:06:05 +00:00
|
|
|
export interface IMatrixClientPeg {
|
2024-05-01 13:22:29 +00:00
|
|
|
/**
|
|
|
|
* The opts used to start the client
|
|
|
|
*/
|
2021-07-10 14:43:46 +00:00
|
|
|
opts: IStartClientOpts;
|
2017-04-06 10:13:39 +00:00
|
|
|
|
2024-05-01 13:22:29 +00:00
|
|
|
/**
|
|
|
|
* Get the current MatrixClient, if any
|
|
|
|
*/
|
2023-06-21 16:29:44 +00:00
|
|
|
get(): MatrixClient | null;
|
2024-05-01 13:22:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current MatrixClient, throwing an error if there isn't one
|
|
|
|
*/
|
2023-06-05 17:12:23 +00:00
|
|
|
safeGet(): MatrixClient;
|
2024-05-01 13:22:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Unset the current MatrixClient
|
|
|
|
*/
|
2020-05-25 22:06:05 +00:00
|
|
|
unset(): void;
|
2024-05-01 13:22:29 +00:00
|
|
|
|
|
|
|
/**
|
2024-06-05 08:52:28 +00:00
|
|
|
* Prepare the MatrixClient for use, including initialising the store and crypto, but do not start it.
|
2024-05-01 13:22:29 +00:00
|
|
|
*/
|
2024-06-05 08:52:28 +00:00
|
|
|
assign(opts?: MatrixClientPegAssignOpts): Promise<IStartClientOpts>;
|
2024-05-01 13:22:29 +00:00
|
|
|
|
|
|
|
/**
|
2024-06-05 08:52:28 +00:00
|
|
|
* Prepare the MatrixClient for use, including initialising the store and crypto, and start it.
|
2024-05-01 13:22:29 +00:00
|
|
|
*/
|
2024-06-05 08:52:28 +00:00
|
|
|
start(opts?: MatrixClientPegAssignOpts): Promise<void>;
|
2017-12-07 17:10:45 +00:00
|
|
|
|
2019-09-18 08:27:43 +00:00
|
|
|
/**
|
2019-06-14 14:31:19 +00:00
|
|
|
* If we've registered a user ID we set this to the ID of the
|
|
|
|
* user we've just registered. If they then go & log in, we
|
|
|
|
* can send them to the welcome user (obviously this doesn't
|
2022-04-28 10:46:02 +00:00
|
|
|
* guarantee they'll get a chat with the welcome user).
|
2019-06-14 14:31:19 +00:00
|
|
|
*
|
|
|
|
* @param {string} uid The user ID of the user we've just registered
|
|
|
|
*/
|
2022-04-28 10:46:02 +00:00
|
|
|
setJustRegisteredUserId(uid: string | null): void;
|
2019-06-14 14:31:19 +00:00
|
|
|
|
2019-09-18 08:27:43 +00:00
|
|
|
/**
|
2019-06-14 14:31:19 +00:00
|
|
|
* Returns true if the current user has just been registered by this
|
|
|
|
* client as determined by setJustRegisteredUserId()
|
|
|
|
*
|
|
|
|
* @returns {bool} True if user has just been registered
|
|
|
|
*/
|
2020-05-25 22:06:05 +00:00
|
|
|
currentUserIsJustRegistered(): boolean;
|
|
|
|
|
2020-11-02 17:25:48 +00:00
|
|
|
/**
|
|
|
|
* If the current user has been registered by this device then this
|
|
|
|
* returns a boolean of whether it was within the last N hours given.
|
|
|
|
*/
|
|
|
|
userRegisteredWithinLastHours(hours: number): boolean;
|
|
|
|
|
2022-07-29 11:43:29 +00:00
|
|
|
/**
|
|
|
|
* If the current user has been registered by this device then this
|
|
|
|
* returns a boolean of whether it was after a given timestamp.
|
|
|
|
*/
|
|
|
|
userRegisteredAfter(date: Date): boolean;
|
|
|
|
|
2020-05-25 22:06:05 +00:00
|
|
|
/**
|
|
|
|
* Replace this MatrixClientPeg's client with a client instance that has
|
|
|
|
* homeserver / identity server URLs and active credentials
|
|
|
|
*
|
|
|
|
* @param {IMatrixClientCreds} creds The new credentials to use.
|
2023-10-12 00:49:07 +00:00
|
|
|
* @param {TokenRefreshFunction} tokenRefreshFunction OPTIONAL function used by MatrixClient to attempt token refresh
|
|
|
|
* see {@link ICreateClientOpts.tokenRefreshFunction}
|
2020-05-25 22:06:05 +00:00
|
|
|
*/
|
2023-10-12 00:49:07 +00:00
|
|
|
replaceUsingCreds(creds: IMatrixClientCreds, tokenRefreshFunction?: TokenRefreshFunction): void;
|
2020-05-25 22:06:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-22 14:47:47 +00:00
|
|
|
/**
|
|
|
|
* Wrapper object for handling the js-sdk Matrix Client object in the react-sdk
|
|
|
|
* Handles the creation/initialisation of client objects.
|
|
|
|
* This module provides a singleton instance of this class so the 'current'
|
|
|
|
* Matrix Client object is available easily.
|
|
|
|
*/
|
2021-07-19 21:43:11 +00:00
|
|
|
class MatrixClientPegClass implements IMatrixClientPeg {
|
2020-05-25 21:52:05 +00:00
|
|
|
// These are the default options used when when the
|
|
|
|
// client is started in 'start'. These can be altered
|
|
|
|
// at any time up to after the 'will_start_client'
|
|
|
|
// event is finished processing.
|
2021-07-10 14:43:46 +00:00
|
|
|
public opts: IStartClientOpts = {
|
2020-05-25 21:52:05 +00:00
|
|
|
initialSyncLimit: 20,
|
|
|
|
};
|
|
|
|
|
2023-06-05 17:12:23 +00:00
|
|
|
private matrixClient: MatrixClient | null = null;
|
2022-04-13 18:05:08 +00:00
|
|
|
private justRegisteredUserId: string | null = null;
|
2020-05-25 21:59:15 +00:00
|
|
|
|
2023-06-21 16:29:44 +00:00
|
|
|
public get(): MatrixClient | null {
|
2016-07-21 16:57:55 +00:00
|
|
|
return this.matrixClient;
|
2015-09-28 16:46:49 +00:00
|
|
|
}
|
2015-06-09 16:40:42 +00:00
|
|
|
|
2023-06-05 17:12:23 +00:00
|
|
|
public safeGet(): MatrixClient {
|
|
|
|
if (!this.matrixClient) {
|
2023-09-22 15:39:40 +00:00
|
|
|
throw new UserFriendlyError("error_user_not_logged_in");
|
2023-06-05 17:12:23 +00:00
|
|
|
}
|
|
|
|
return this.matrixClient;
|
|
|
|
}
|
|
|
|
|
2020-05-25 21:52:05 +00:00
|
|
|
public unset(): void {
|
2016-07-21 16:57:55 +00:00
|
|
|
this.matrixClient = null;
|
2017-12-07 17:10:45 +00:00
|
|
|
|
|
|
|
MatrixActionCreators.stop();
|
2015-09-28 16:46:49 +00:00
|
|
|
}
|
2015-09-16 12:48:24 +00:00
|
|
|
|
2022-04-13 18:05:08 +00:00
|
|
|
public setJustRegisteredUserId(uid: string | null): void {
|
2020-05-25 21:52:05 +00:00
|
|
|
this.justRegisteredUserId = uid;
|
2020-11-02 17:25:48 +00:00
|
|
|
if (uid) {
|
2022-04-28 10:46:02 +00:00
|
|
|
const registrationTime = Date.now().toString();
|
|
|
|
window.localStorage.setItem("mx_registration_time", registrationTime);
|
2020-11-02 17:25:48 +00:00
|
|
|
}
|
2019-06-14 14:31:19 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 21:52:05 +00:00
|
|
|
public currentUserIsJustRegistered(): boolean {
|
2023-04-06 10:10:14 +00:00
|
|
|
return !!this.matrixClient && this.matrixClient.credentials.userId === this.justRegisteredUserId;
|
2019-06-14 14:31:19 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 17:25:48 +00:00
|
|
|
public userRegisteredWithinLastHours(hours: number): boolean {
|
2022-04-13 18:05:08 +00:00
|
|
|
if (hours <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-02 17:25:48 +00:00
|
|
|
try {
|
2023-02-24 15:28:40 +00:00
|
|
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
2022-04-13 18:05:08 +00:00
|
|
|
const diff = Date.now() - registrationTime;
|
|
|
|
return diff / 36e5 <= hours;
|
2020-11-02 17:25:48 +00:00
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 11:43:29 +00:00
|
|
|
public userRegisteredAfter(timestamp: Date): boolean {
|
|
|
|
try {
|
2023-02-24 15:28:40 +00:00
|
|
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
2022-07-29 11:43:29 +00:00
|
|
|
return timestamp.getTime() <= registrationTime;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 00:49:07 +00:00
|
|
|
public replaceUsingCreds(creds: IMatrixClientCreds, tokenRefreshFunction?: TokenRefreshFunction): void {
|
|
|
|
this.createClient(creds, tokenRefreshFunction);
|
2016-07-25 15:28:28 +00:00
|
|
|
}
|
|
|
|
|
2023-03-31 09:08:45 +00:00
|
|
|
private onUnexpectedStoreClose = async (): Promise<void> => {
|
|
|
|
if (!this.matrixClient) return;
|
|
|
|
this.matrixClient.stopClient(); // stop the client as the database has failed
|
2023-05-16 15:08:01 +00:00
|
|
|
this.matrixClient.store.destroy();
|
2023-03-31 09:08:45 +00:00
|
|
|
|
|
|
|
if (!this.matrixClient.isGuest()) {
|
|
|
|
// If the user is not a guest then prompt them to reload rather than doing it for them
|
|
|
|
// For guests this is likely to happen during e-mail verification as part of registration
|
|
|
|
|
2024-01-02 11:48:12 +00:00
|
|
|
const brand = SdkConfig.get().brand;
|
|
|
|
const platform = PlatformPeg.get()?.getHumanReadableName();
|
|
|
|
|
|
|
|
// Determine the description based on the platform
|
|
|
|
const description =
|
|
|
|
platform === "Web Platform"
|
|
|
|
? _t("error_database_closed_description|for_web", { brand })
|
|
|
|
: _t("error_database_closed_description|for_desktop");
|
|
|
|
|
|
|
|
const [reload] = await Modal.createDialog(ErrorDialog, {
|
|
|
|
title: _t("error_database_closed_title", { brand }),
|
|
|
|
description,
|
2023-08-23 10:57:22 +00:00
|
|
|
button: _t("action|reload"),
|
2024-01-02 11:48:12 +00:00
|
|
|
}).finished;
|
|
|
|
|
2023-03-31 09:08:45 +00:00
|
|
|
if (!reload) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlatformPeg.get()?.reload();
|
|
|
|
};
|
|
|
|
|
2024-06-05 08:52:28 +00:00
|
|
|
/**
|
|
|
|
* Implementation of {@link IMatrixClientPeg.assign}.
|
|
|
|
*/
|
|
|
|
public async assign(assignOpts: MatrixClientPegAssignOpts = {}): Promise<IStartClientOpts> {
|
2023-06-05 17:12:23 +00:00
|
|
|
if (!this.matrixClient) {
|
|
|
|
throw new Error("createClient must be called first");
|
|
|
|
}
|
|
|
|
|
2018-10-04 12:40:56 +00:00
|
|
|
for (const dbType of ["indexeddb", "memory"]) {
|
|
|
|
try {
|
|
|
|
const promise = this.matrixClient.store.startup();
|
2021-09-21 15:48:09 +00:00
|
|
|
logger.log("MatrixClientPeg: waiting for MatrixClient store to initialise");
|
2018-10-04 12:40:56 +00:00
|
|
|
await promise;
|
|
|
|
break;
|
|
|
|
} catch (err) {
|
|
|
|
if (dbType === "indexeddb") {
|
2021-10-15 14:30:53 +00:00
|
|
|
logger.error("Error starting matrixclient store - falling back to memory store", err);
|
2019-09-18 08:27:43 +00:00
|
|
|
this.matrixClient.store = new MemoryStore({
|
2020-05-25 21:52:05 +00:00
|
|
|
localStorage: localStorage,
|
2018-10-04 12:40:56 +00:00
|
|
|
});
|
|
|
|
} else {
|
2021-10-15 14:30:53 +00:00
|
|
|
logger.error("Failed to start memory store!", err);
|
2018-10-04 12:40:56 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-31 09:08:45 +00:00
|
|
|
this.matrixClient.store.on?.("closed", this.onUnexpectedStoreClose);
|
2018-10-04 12:40:56 +00:00
|
|
|
|
2017-07-18 22:46:03 +00:00
|
|
|
// try to initialise e2e on the new client
|
2022-12-16 17:10:26 +00:00
|
|
|
if (!SettingsStore.getValue("lowBandwidth")) {
|
2024-06-05 08:52:28 +00:00
|
|
|
await this.initClientCrypto(assignOpts.rustCryptoStoreKey, assignOpts.rustCryptoStorePassword);
|
2017-07-18 22:46:03 +00:00
|
|
|
}
|
|
|
|
|
2016-08-03 15:45:23 +00:00
|
|
|
const opts = utils.deepCopy(this.opts);
|
2016-08-03 16:23:09 +00:00
|
|
|
// the react sdk doesn't work without this, so don't allow
|
2021-07-10 14:43:46 +00:00
|
|
|
opts.pendingEventOrdering = PendingEventOrdering.Detached;
|
2019-02-07 18:24:07 +00:00
|
|
|
opts.lazyLoadMembers = true;
|
2020-06-03 11:25:57 +00:00
|
|
|
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
|
2023-02-20 14:46:07 +00:00
|
|
|
opts.threadSupport = true;
|
2017-02-02 14:27:27 +00:00
|
|
|
|
2022-09-07 15:42:39 +00:00
|
|
|
if (SettingsStore.getValue("feature_sliding_sync")) {
|
MSC3575 (Sliding Sync) add well-known proxy support (#12307)
* Initial commit
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove commented code
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Change function to reflect it's proxy not native support
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Re-add check for servers with native support
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add native support check back in
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Re-add endpoint health check function
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Use inbuilt `getWellKnown` function
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Change the error message to the correct function
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Stop storing the proxyurl in the settings for now
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Make the logger messages more useful
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Start moving the checking logic directly into the controller
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add missing import
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Get the client rather than passing it in to the functions
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* remove invalid `function` keyword
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Fix imports
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Our new functions are private
We shouldn't(?) have to use these check in future elsewhere
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Change our proxy check function to return a boolean
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Make `nativeSlidingSyncSupport` also return boolean, add in health check
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Disable the sliding sync option if the server doesn't support
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Only enable the setting if it passes (again)
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Update our comments to better match what's going on
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove unused dialog
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add a well-known check on start-up, if sliding sync has been enabled
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Check against the correct endpoint...
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Extract baseUrl as we'll reuse it
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Make the logs differentiate between the types of proxy
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Grab the client well-known directly for use
Can't use the client object at this point, it hasn't read in the well-known
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add myself to the copyright assignation
I wrote the majority of this file...
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Only return `true` if it's actually there
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Correct the `proxySlidingSyncSupport` function comment to match the code
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Correct the `nativeSlidingSyncSupport`function comment to match the code
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Another comment/functionality paring
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove duplicated types from the doc
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move await to the previous line
Removes brackets, and corrects `wellKnown` from being a `Promise`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* use `waitForClientWellKnown` to avoid a race condition with the request
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move getting the client out of the `if`, use `waitForClientWellKnown`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove `beforeChange` override
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move proxy setup logic into `SlidingSyncManager`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Swap `configure` to private, we call it from `setup` which handles proxy
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Promises are always `true`
TIL.
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* use `timeoutSignal`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Change message when there's no server support
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Refactor `slidingSyncHealthCheck`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Refactor `nativeSlidingSyncSupport` with try/catch
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Change comment to hotlink
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Try and make the toggle disabled when there's no endpoint
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move the if statement outside the refactored fn to avoid an await
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Revert "Swap `configure` to private, we call it from `setup` which handles proxy"
This reverts commit c80a00b50c261becc9ad58e08d2a893d572d8426.
* Remove unused import
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Further refactor `slidingSyncHealthCheck`
`proxySlidingSyncSupport` already checks the client well-known is there
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Make `proxySlidingSyncSupport` log on success
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Clarify log message for proxy being up
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move the logic into SlidingSyncManager
All so we can set a static variable because the disabled check isn't asynchronous :)
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Obviously this isn't a return so don't overwrite with false!
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove outdated comment
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* No need to pass in the client
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Activating SS should probably be info level logs
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* If we've not enabled sliding sync, push the logs down a bit
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Update i18n error message
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove unused i18n strings
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Correct log message
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Prettier
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove many of the log messages
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Short out of `checkSupport` if it's `true`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add the endpoint back into the log when we're enabling it
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Note in the comment that `feature_sliding_sync_proxy_url` is legacy
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Expand the well-known liveness check log
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* No need to stall the client waiting for sliding sync support
* `AutoDiscovery.findClientConfig` throws if the baseUrl is blank
* Fix `getProxyFromWellKnown` (?)
* Add missing semicolon
Sorry, linter!
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Pass our `MatrixClient` through instead of trying to grab it
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Add missing return in function comment
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Actually pass through our Client, not the Peg object
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Remove SonarCube smell complaint
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Neew to make our other two methods public to test
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* First passing test
Hurrah!
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Two more tests, this time on `checkSupport`
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Reset our `serverSupportsSlidingSync` between tests
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Check the static member is being set
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move the static assignation down to the relevant tests
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Pull getProxyFromWellKnown mocking up
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Check we /haven't/ shorted out
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Move our spy up so we can reuse it
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Check spidering is being called
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Test the proxy is declared
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Test entered manually
Signed-off-by: Ed Geraghty <ed@geraghty.family>
* Sorry, linter
* I guess these strings are wrong?
* Replace any with string
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
---------
Signed-off-by: Ed Geraghty <ed@geraghty.family>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-04-30 18:11:11 +00:00
|
|
|
opts.slidingSync = await SlidingSyncManager.instance.setup(this.matrixClient);
|
|
|
|
} else {
|
|
|
|
SlidingSyncManager.instance.checkSupport(this.matrixClient);
|
2022-09-07 15:42:39 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 23:33:20 +00:00
|
|
|
// Connect the matrix client to the dispatcher and setting handlers
|
2017-12-07 17:10:45 +00:00
|
|
|
MatrixActionCreators.start(this.matrixClient);
|
2019-02-22 23:33:20 +00:00
|
|
|
MatrixClientBackedSettingsHandler.matrixClient = this.matrixClient;
|
2023-03-03 13:31:51 +00:00
|
|
|
MatrixClientBackedController.matrixClient = this.matrixClient;
|
2017-12-07 17:10:45 +00:00
|
|
|
|
2019-07-04 22:45:40 +00:00
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
2022-12-16 17:10:26 +00:00
|
|
|
/**
|
|
|
|
* Attempt to initialize the crypto layer on a newly-created MatrixClient
|
2024-06-05 08:52:28 +00:00
|
|
|
*
|
2024-06-24 09:14:42 +00:00
|
|
|
* @param rustCryptoStoreKey - A key with which to encrypt the rust crypto indexeddb.
|
2024-06-05 08:52:28 +00:00
|
|
|
* If provided, it must be exactly 32 bytes of data. If both this and `rustCryptoStorePassword` are
|
|
|
|
* undefined, the store will be unencrypted.
|
|
|
|
*
|
|
|
|
* @param rustCryptoStorePassword - An alternative to `rustCryptoStoreKey`. Ignored if `rustCryptoStoreKey` is set.
|
|
|
|
* A password which will be used to derive a key to encrypt the store with. Deriving a key from a password is
|
|
|
|
* (deliberately) a slow operation, so prefer to pass a `rustCryptoStoreKey` directly where possible.
|
2022-12-16 17:10:26 +00:00
|
|
|
*/
|
2024-06-05 08:52:28 +00:00
|
|
|
private async initClientCrypto(rustCryptoStoreKey?: Uint8Array, rustCryptoStorePassword?: string): Promise<void> {
|
2023-06-05 17:12:23 +00:00
|
|
|
if (!this.matrixClient) {
|
|
|
|
throw new Error("createClient must be called first");
|
|
|
|
}
|
|
|
|
|
2024-06-24 09:14:42 +00:00
|
|
|
if (!rustCryptoStoreKey && !rustCryptoStorePassword) {
|
|
|
|
logger.error("Warning! Not using an encryption key for rust crypto store.");
|
2024-01-31 15:52:23 +00:00
|
|
|
}
|
2022-12-16 17:10:26 +00:00
|
|
|
|
2024-06-24 09:14:42 +00:00
|
|
|
// Record the fact that we used the Rust crypto stack with this client. This just guards against people
|
|
|
|
// rolling back to versions of EW that did not default to Rust crypto (which would lead to an error, since
|
|
|
|
// we cannot migrate from Rust to Legacy crypto).
|
|
|
|
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, true);
|
2022-12-16 17:10:26 +00:00
|
|
|
|
2024-06-24 09:14:42 +00:00
|
|
|
await this.matrixClient.initRustCrypto({
|
|
|
|
storageKey: rustCryptoStoreKey,
|
|
|
|
storagePassword: rustCryptoStorePassword,
|
|
|
|
});
|
2022-12-16 17:10:26 +00:00
|
|
|
|
2024-06-24 09:14:42 +00:00
|
|
|
StorageManager.setCryptoInitialised(true);
|
|
|
|
// TODO: device dehydration and whathaveyou
|
|
|
|
return;
|
2022-12-16 17:10:26 +00:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:52:28 +00:00
|
|
|
/**
|
|
|
|
* Implementation of {@link IMatrixClientPeg.start}.
|
|
|
|
*/
|
|
|
|
public async start(assignOpts?: MatrixClientPegAssignOpts): Promise<void> {
|
|
|
|
const opts = await this.assign(assignOpts);
|
2019-07-04 22:45:40 +00:00
|
|
|
|
2021-09-21 15:48:09 +00:00
|
|
|
logger.log(`MatrixClientPeg: really starting MatrixClient`);
|
2023-06-05 17:12:23 +00:00
|
|
|
await this.matrixClient!.startClient(opts);
|
2021-09-21 15:48:09 +00:00
|
|
|
logger.log(`MatrixClientPeg: MatrixClient started`);
|
2016-08-03 15:41:22 +00:00
|
|
|
}
|
2016-08-03 15:39:47 +00:00
|
|
|
|
2022-08-26 10:23:56 +00:00
|
|
|
private namesToRoomName(names: string[], count: number): string | undefined {
|
|
|
|
const countWithoutMe = count - 1;
|
|
|
|
if (!names.length) {
|
2023-09-22 15:39:40 +00:00
|
|
|
return _t("empty_room");
|
2022-08-26 10:23:56 +00:00
|
|
|
}
|
|
|
|
if (names.length === 1 && countWithoutMe <= 1) {
|
|
|
|
return names[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private memberNamesToRoomName(names: string[], count: number): string {
|
|
|
|
const name = this.namesToRoomName(names, count);
|
|
|
|
if (name) return name;
|
|
|
|
|
|
|
|
if (names.length === 2 && count === 2) {
|
2023-09-25 11:18:15 +00:00
|
|
|
return formatList(names);
|
2022-08-26 10:23:56 +00:00
|
|
|
}
|
2023-09-25 11:18:15 +00:00
|
|
|
return formatList(names, 1);
|
2022-08-26 10:23:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private inviteeNamesToRoomName(names: string[], count: number): string {
|
|
|
|
const name = this.namesToRoomName(names, count);
|
|
|
|
if (name) return name;
|
|
|
|
|
|
|
|
if (names.length === 2 && count === 2) {
|
2023-09-22 15:39:40 +00:00
|
|
|
return _t("inviting_user1_and_user2", {
|
2022-08-26 10:23:56 +00:00
|
|
|
user1: names[0],
|
|
|
|
user2: names[1],
|
|
|
|
});
|
|
|
|
}
|
2023-09-22 15:39:40 +00:00
|
|
|
return _t("inviting_user_and_n_others", {
|
2022-08-26 10:23:56 +00:00
|
|
|
user: names[0],
|
|
|
|
count: count - 1,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-10-12 00:49:07 +00:00
|
|
|
private createClient(creds: IMatrixClientCreds, tokenRefreshFunction?: TokenRefreshFunction): void {
|
2020-10-09 15:59:56 +00:00
|
|
|
const opts: ICreateClientOpts = {
|
2016-08-11 12:50:38 +00:00
|
|
|
baseUrl: creds.homeserverUrl,
|
|
|
|
idBaseUrl: creds.identityServerUrl,
|
|
|
|
accessToken: creds.accessToken,
|
2023-10-12 00:49:07 +00:00
|
|
|
refreshToken: creds.refreshToken,
|
|
|
|
tokenRefreshFunction,
|
2020-09-30 04:52:47 +00:00
|
|
|
userId: creds.userId,
|
|
|
|
deviceId: creds.deviceId,
|
2020-05-28 04:05:45 +00:00
|
|
|
pickleKey: creds.pickleKey,
|
2016-07-21 16:57:55 +00:00
|
|
|
timelineSupport: true,
|
2020-07-29 17:03:43 +00:00
|
|
|
forceTURN: !SettingsStore.getValue("webRtcAllowPeerToPeer"),
|
2019-08-14 13:02:25 +00:00
|
|
|
fallbackICEServerAllowed: !!SettingsStore.getValue("fallbackICEServerAllowed"),
|
2021-02-16 15:48:58 +00:00
|
|
|
// Gather up to 20 ICE candidates when a call arrives: this should be more than we'd
|
|
|
|
// ever normally need, so effectively this should make all the gathering happen when
|
|
|
|
// the call arrives.
|
|
|
|
iceCandidatePoolSize: 20,
|
2020-01-29 15:02:52 +00:00
|
|
|
verificationMethods: [
|
2024-03-25 17:44:45 +00:00
|
|
|
VerificationMethod.Sas,
|
|
|
|
VerificationMethod.ShowQrCode,
|
|
|
|
VerificationMethod.Reciprocate,
|
2020-01-29 15:02:52 +00:00
|
|
|
],
|
2019-08-23 10:17:51 +00:00
|
|
|
identityServer: new IdentityAuthClient(),
|
2022-08-26 10:23:56 +00:00
|
|
|
// These are always installed regardless of the labs flag so that cross-signing features
|
|
|
|
// can toggle on without reloading and also be accessed immediately after login.
|
|
|
|
cryptoCallbacks: { ...crossSigningCallbacks },
|
|
|
|
roomNameGenerator: (_: string, state: RoomNameState) => {
|
|
|
|
switch (state.type) {
|
|
|
|
case RoomNameType.Generated:
|
|
|
|
switch (state.subtype) {
|
|
|
|
case "Inviting":
|
|
|
|
return this.inviteeNamesToRoomName(state.names, state.count);
|
|
|
|
default:
|
|
|
|
return this.memberNamesToRoomName(state.names, state.count);
|
|
|
|
}
|
|
|
|
case RoomNameType.EmptyRoom:
|
|
|
|
if (state.oldName) {
|
2023-09-22 15:39:40 +00:00
|
|
|
return _t("empty_room_was_name", {
|
2022-08-26 10:23:56 +00:00
|
|
|
oldName: state.oldName,
|
|
|
|
});
|
|
|
|
} else {
|
2023-09-22 15:39:40 +00:00
|
|
|
return _t("empty_room");
|
2022-08-26 10:23:56 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
},
|
2016-07-21 16:57:55 +00:00
|
|
|
};
|
|
|
|
|
2024-04-12 15:15:17 +00:00
|
|
|
const dehydrationKeyCallback = ModuleRunner.instance.extensions.cryptoSetup.getDehydrationKeyCallback();
|
|
|
|
if (dehydrationKeyCallback) {
|
|
|
|
opts.cryptoCallbacks!.getDehydrationKey = dehydrationKeyCallback;
|
2021-03-22 23:52:09 +00:00
|
|
|
}
|
2019-11-15 10:57:20 +00:00
|
|
|
|
2020-09-03 20:28:42 +00:00
|
|
|
this.matrixClient = createMatrixClient(opts);
|
2016-08-11 12:50:38 +00:00
|
|
|
this.matrixClient.setGuest(Boolean(creds.guest));
|
2016-09-08 02:02:26 +00:00
|
|
|
|
2022-08-26 10:23:56 +00:00
|
|
|
const notifTimelineSet = new EventTimelineSet(undefined, {
|
2017-10-11 16:56:17 +00:00
|
|
|
timelineSupport: true,
|
2021-10-22 08:30:36 +00:00
|
|
|
pendingEvents: false,
|
2016-09-08 02:02:26 +00:00
|
|
|
});
|
|
|
|
// XXX: what is our initial pagination token?! it somehow needs to be synchronised with /sync.
|
|
|
|
notifTimelineSet.getLiveTimeline().setPaginationToken("", EventTimeline.BACKWARDS);
|
|
|
|
this.matrixClient.setNotifTimelineSet(notifTimelineSet);
|
2016-07-21 16:57:55 +00:00
|
|
|
}
|
2015-09-28 16:46:49 +00:00
|
|
|
}
|
2015-06-09 16:40:42 +00:00
|
|
|
|
2022-05-10 02:34:27 +00:00
|
|
|
/**
|
|
|
|
* Note: You should be using a React context with access to a client rather than
|
|
|
|
* using this, as in a multi-account world this will not exist!
|
|
|
|
*/
|
2022-02-16 11:19:28 +00:00
|
|
|
export const MatrixClientPeg: IMatrixClientPeg = new MatrixClientPegClass();
|
|
|
|
|
2020-05-25 22:06:05 +00:00
|
|
|
if (!window.mxMatrixClientPeg) {
|
2022-02-16 11:19:28 +00:00
|
|
|
window.mxMatrixClientPeg = MatrixClientPeg;
|
2015-09-28 16:46:49 +00:00
|
|
|
}
|