diff --git a/lib/src/client.dart b/lib/src/client.dart index a752e7ec..d44edc1b 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -33,6 +33,7 @@ import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart'; +import 'package:matrix/src/utils/client_init_exception.dart'; import 'package:matrix/src/utils/compute_callback.dart'; import 'package:matrix/src/utils/multilock.dart'; import 'package:matrix/src/utils/run_benchmarked.dart'; @@ -1464,16 +1465,26 @@ class Client extends MatrixApi { newUserID == null || newDeviceID == null || newDeviceName == null)) { - throw Exception( - 'If one of [newToken, newUserID, newDeviceID, newDeviceName] is set then all of them must be set!'); + throw ClientInitPreconditionError( + 'If one of [newToken, newUserID, newDeviceID, newDeviceName] is set then all of them must be set!', + ); } - if (_initLock) throw Exception('[init()] has been called multiple times!'); + if (_initLock) { + throw ClientInitPreconditionError( + '[init()] has been called multiple times!', + ); + } _initLock = true; + String? olmAccount; + String? accessToken; + String? userID; try { Logs().i('Initialize client $clientName'); if (isLogged()) { - throw Exception('User is already logged in! Call [logout()] first!'); + throw ClientInitPreconditionError( + 'User is already logged in! Call [logout()] first!', + ); } final databaseBuilder = this.databaseBuilder; @@ -1487,9 +1498,6 @@ class Client extends MatrixApi { _groupCallSessionId = randomAlpha(12); _serverConfigCache.invalidate(); - String? olmAccount; - String? accessToken; - String? userID; final account = await this.database?.getClient(clientName); if (account != null) { _id = account['client_id']; @@ -1600,12 +1608,24 @@ class Client extends MatrixApi { await firstSyncReceived; } return; - } catch (e, s) { - Logs().e('Initialization failed', e, s); - await logout().catchError((_) => null); - onLoginStateChanged.addError(e, s); - _initLock = false; + } on ClientInitPreconditionError { rethrow; + } catch (e, s) { + Logs().wtf('Client initialization failed', e, s); + onLoginStateChanged.addError(e, s); + final clientInitException = ClientInitException( + e, + homeserver: homeserver, + accessToken: accessToken, + userId: userID, + deviceId: deviceID, + deviceName: deviceName, + olmAccount: olmAccount, + ); + await clear(); + throw clientInitException; + } finally { + _initLock = false; } } diff --git a/lib/src/utils/client_init_exception.dart b/lib/src/utils/client_init_exception.dart new file mode 100644 index 00000000..da2e90e6 --- /dev/null +++ b/lib/src/utils/client_init_exception.dart @@ -0,0 +1,34 @@ +/// Exception thrown on Client initialization. This object might contain +/// enough information to restore the session or to decide if you need to +/// logout the session or clear the database. +class ClientInitException implements Exception { + final Object originalException; + final Uri? homeserver; + final String? accessToken; + final String? userId; + final String? deviceId; + final String? deviceName; + final String? olmAccount; + + ClientInitException( + this.originalException, { + this.homeserver, + this.accessToken, + this.userId, + this.deviceId, + this.deviceName, + this.olmAccount, + }); + + @override + String toString() => originalException.toString(); +} + +class ClientInitPreconditionError implements Exception { + final String cause; + + ClientInitPreconditionError(this.cause); + + @override + String toString() => 'Client Init Precondition Error: $cause'; +} diff --git a/test/client_test.dart b/test/client_test.dart index ef025f07..db86214e 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -26,6 +26,7 @@ import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; import 'package:matrix/matrix.dart'; +import 'package:matrix/src/utils/client_init_exception.dart'; import 'fake_client.dart'; import 'fake_database.dart'; import 'fake_matrix_api.dart'; @@ -1139,6 +1140,41 @@ void main() { reason: '!5345234235:example.com not found as archived room'); }); + test('Client Init Exception', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().w('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + final customClient = Client( + 'failclient', + databaseBuilder: getMatrixSdkDatabase, + ); + try { + await customClient.init( + newToken: 'testtoken', + newDeviceID: 'testdeviceid', + newDeviceName: 'testdevicename', + newHomeserver: Uri.parse('https://test.server'), + newOlmAccount: 'abcd', + newUserID: '@user:server', + ); + throw Exception('No exception?'); + } on ClientInitException catch (error) { + expect(error.accessToken, 'testtoken'); + expect(error.deviceId, 'testdeviceid'); + expect(error.deviceName, 'testdevicename'); + expect(error.homeserver, Uri.parse('https://test.server')); + expect(error.olmAccount, 'abcd'); + expect(error.userId, '@user:server'); + expect(error.toString(), 'Exception: BAD_ACCOUNT_KEY'); + } + }); + tearDown(() async { await matrix.dispose(closeDatabase: true); });