diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 3f3e1c15cc..f4817a070a 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -351,6 +351,9 @@ final class BuildSettings: NSObject { static let authScreenShowCustomServerOptions = true static let authScreenShowSocialLoginSection = true + // MARK: - Authentication Options + static let authEnableRefreshTokens = false + // MARK: - Unified Search static let unifiedSearchScreenShowPublicDirectory = true diff --git a/Config/CommonConfiguration.swift b/Config/CommonConfiguration.swift index 5edcacc37c..2d628d3bce 100644 --- a/Config/CommonConfiguration.swift +++ b/Config/CommonConfiguration.swift @@ -76,6 +76,8 @@ class CommonConfiguration: NSObject, Configurable { sdkOptions.enableKeyBackupWhenStartingMXCrypto = false sdkOptions.clientPermalinkBaseUrl = BuildSettings.clientPermalinkBaseUrl + + sdkOptions.authEnableRefreshTokens = BuildSettings.authEnableRefreshTokens // Configure key provider delegate MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared } diff --git a/Podfile b/Podfile index 1898c52bbd..2d8c4ed5e3 100644 --- a/Podfile +++ b/Podfile @@ -150,4 +150,4 @@ post_install do |installer| config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' end end -end +end \ No newline at end of file diff --git a/Riot/Managers/UserSessions/UserSessionsService.swift b/Riot/Managers/UserSessions/UserSessionsService.swift index b8618abb0d..7a68d9752a 100644 --- a/Riot/Managers/UserSessions/UserSessionsService.swift +++ b/Riot/Managers/UserSessions/UserSessionsService.swift @@ -155,7 +155,7 @@ class UserSessionsService: NSObject { let isSessionStateValid: Bool switch mxSession.state { - case .closed, .unknownToken: + case .closed: isSessionStateValid = false default: isSessionStateValid = true diff --git a/Riot/Modules/Analytics/Analytics.swift b/Riot/Modules/Analytics/Analytics.swift index 6a48f5c6ce..4bef46af29 100644 --- a/Riot/Modules/Analytics/Analytics.swift +++ b/Riot/Modules/Analytics/Analytics.swift @@ -93,7 +93,7 @@ import AnalyticsEvents // Catch and log crashes MXLogger.logCrashes(true) - MXLogger.setBuildVersion(AppDelegate.theDelegate().build) + MXLogger.setBuildVersion(AppInfo.current.buildInfo.readableBuildVersion) } /// Use the analytics settings from the supplied session to configure analytics. @@ -210,6 +210,18 @@ extension Analytics { } } + /// Track when a user becomes unauthenticated without pressing the `sign out` button. + /// - Parameters: + /// - softLogout: Wether it was a soft/hard logout that was triggered. + /// - refreshTokenAuth: Wether it was either an access-token-based or refresh-token-based auth mechanism enabled. + /// - errorCode: The error code as returned by the homeserver that triggered the logout. + /// - errorReason: The reason for the error as returned by the homeserver that triggered the logout. + func trackAuthUnauthenticatedError(softLogout: Bool, refreshTokenAuth: Bool, errorCode: String, errorReason: String) { + let errorCode = AnalyticsEvent.UnauthenticatedError.ErrorCode(rawValue: errorCode) ?? .M_UNKNOWN + let event = AnalyticsEvent.UnauthenticatedError(errorCode: errorCode, errorReason: errorReason, refreshTokenAuth: refreshTokenAuth, softLogout: softLogout) + client.capture(event) + } + /// Track whether the user accepted or declined the terms to an identity server. /// **Note** This method isn't currently implemented. /// - Parameter accepted: Whether the terms were accepted. diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 3a859b7d5d..3f484f317c 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -337,7 +337,7 @@ - (void)viewDidAppear:(BOOL)animated didCheckFalseAuthScreenDisplay = YES; MXLogDebug(@"[AuthenticationVC] viewDidAppear: Checking false logout"); - [[MXKAccountManager sharedManager] forceReloadAccounts]; + [MXKAccountManager sharedManagerWithReload: YES]; if ([MXKAccountManager sharedManager].activeAccounts.count) { // For now, we do not have better solution than forcing the user to restart the app diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m index 91a1eda043..18d65c9272 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m @@ -1451,7 +1451,9 @@ - (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) { return NO; - }]; + } andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; + } andUnauthenticatedHandler: nil]; MXWeakify(self); [[MXKAccountManager sharedManager].dehydrationService rehydrateDeviceWithMatrixRestClient:mxRestClient dehydrationKey:keyData success:^(NSString * deviceId) { diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h index 8d21f6e72b..e2c56d63d0 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h @@ -17,6 +17,7 @@ */ #import +#import "MXKAccountData.h" @class MXKAccount; @@ -56,29 +57,7 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer `MXKAccount` object contains the credentials of a logged matrix user. It is used to handle matrix session and presence for this user. */ -@interface MXKAccount : NSObject - -/** - The account's credentials: homeserver, access token, user id. - */ -@property (nonatomic, readonly) MXCredentials *mxCredentials; - -/** - The identity server URL. - */ -@property (nonatomic) NSString *identityServerURL; - -/** - The antivirus server URL, if any (nil by default). - Set a non-null url to configure the antivirus scanner use. - */ -@property (nonatomic) NSString *antivirusServerURL; - -/** - The Push Gateway URL used to send event notifications to (nil by default). - This URL should be over HTTPS and never over HTTP. - */ -@property (nonatomic) NSString *pushGatewayURL; +@interface MXKAccount : MXKAccountData /** The matrix REST client used to make matrix API requests. @@ -107,12 +86,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer */ @property (nonatomic, readonly) NSString *fullDisplayName; -/** - The 3PIDs linked to this account. - [self load3PIDs] must be called to update the property. - */ -@property (nonatomic, readonly) NSArray *threePIDs; - /** The email addresses linked to this account. This is a subset of self.threePIDs. @@ -125,12 +98,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer */ @property (nonatomic, readonly) NSArray *linkedPhoneNumbers; -/** - The account user's device. - [self loadDeviceInformation] must be called to update the property. - */ -@property (nonatomic, readonly) MXDevice *device; - /** The account user's presence (`MXPresenceUnknown` by default, available if matrix session `mxSession` is opened). The notification `kMXKAccountUserInfoDidChangeNotification` is posted in case of change of this property. @@ -148,11 +115,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer */ @property (nonatomic, readonly) BOOL pushNotificationServiceIsActive; -/** - Transient information storage. - */ -@property (nonatomic, strong, readonly) NSMutableDictionary> *others; - /** Enable Push notification based on Apple Push Notification Service (APNS). @@ -166,11 +128,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer success:(void (^)(void))success failure:(void (^)(NSError *))failure; -/** - Flag to indicate that an APNS pusher has been set on the homeserver for this device. - */ -@property (nonatomic, readonly) BOOL hasPusherForPushNotifications; - /** The Push notification activity (based on PushKit) for this account. YES when Push is turned on (locally available and enabled homeserver side). @@ -190,26 +147,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer success:(void (^)(void))success failure:(void (^)(NSError *))failure; -/** - Flag to indicate that a PushKit pusher has been set on the homeserver for this device. - */ -@property (nonatomic, readonly) BOOL hasPusherForPushKitNotifications; - - -/** - Enable In-App notifications based on Remote notifications rules. - NO by default. - */ -@property (nonatomic) BOOL enableInAppNotifications; - -/** - Disable the account without logging out (NO by default). - - A matrix session is automatically opened for the account when this property is toggled from YES to NO. - The session is closed when this property is set to YES. - */ -@property (nonatomic,getter=isDisabled) BOOL disabled; - /** Manage the online presence event. @@ -217,11 +154,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer */ @property (nonatomic) BOOL hideUserPresence; -/** - Flag indicating if the end user has been warned about encryption and its limitations. - */ -@property (nonatomic,getter=isWarnedAboutEncryption) BOOL warnedAboutEncryption; - /** Register the MXKAccountOnCertificateChange block that will be used to handle certificate change during account use. This block is nil by default, any new certificate is ignored/untrusted (this will abort the connection to the server). @@ -284,11 +216,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer #pragma mark - Soft logout -/** - Flag to indicate if the account has been logged out by the homeserver admin. - */ -@property (nonatomic, readonly) BOOL isSoftLogout; - /** Soft logout the account. @@ -432,4 +359,8 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer success:(void (^)(void))success failure:(void (^)(NSError *error))failure; +/** + Handle unauthenticated errors from the server triggering hard/soft logouts as appropriate. + */ +- (void)handleUnauthenticatedWithError:(MXError *)error isSoftLogout:(BOOL)isSoftLogout isRefreshTokenAuth:(BOOL)isRefreshTokenAuth andCompletion:(void (^)(void))completion; @end diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m index 67697acad8..cd5f5d784b 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m @@ -36,6 +36,8 @@ #import "MXKSwiftHeader.h" +#import "GeneratedInterface-Swift.h" + NSString *const kMXKAccountUserInfoDidChangeNotification = @"kMXKAccountUserInfoDidChangeNotification"; NSString *const kMXKAccountAPNSActivityDidChangeNotification = @"kMXKAccountAPNSActivityDidChangeNotification"; NSString *const kMXKAccountPushKitActivityDidChangeNotification = @"kMXKAccountPushKitActivityDidChangeNotification"; @@ -92,13 +94,10 @@ @interface MXKAccount () @property (nonatomic, strong) id backgroundTask; @property (nonatomic, strong) id backgroundSyncBgTask; -@property (nonatomic, strong) NSMutableDictionary> *others; - @end @implementation MXKAccount -@synthesize mxCredentials, mxSession, mxRestClient; -@synthesize threePIDs; +@synthesize mxSession, mxRestClient; @synthesize userPresence; @synthesize userTintColor; @synthesize hideUserPresence; @@ -144,7 +143,7 @@ - (instancetype)initWithCredentials:(MXCredentials*)credentials notifyOpenSessionFailure = YES; // Report credentials and alloc REST client. - mxCredentials = credentials; + _mxCredentials = credentials; [self prepareRESTClient]; userPresence = MXPresenceUnknown; @@ -171,65 +170,19 @@ - (void)dealloc - (id)initWithCoder:(NSCoder *)coder { - self = [super init]; + self = [super initWithCoder:coder]; if (self) { notifyOpenSessionFailure = YES; - NSString *homeServerURL = [coder decodeObjectForKey:@"homeserverurl"]; - NSString *userId = [coder decodeObjectForKey:@"userid"]; - NSString *accessToken = [coder decodeObjectForKey:@"accesstoken"]; - _identityServerURL = [coder decodeObjectForKey:@"identityserverurl"]; - NSString *identityServerAccessToken = [coder decodeObjectForKey:@"identityserveraccesstoken"]; - - mxCredentials = [[MXCredentials alloc] initWithHomeServer:homeServerURL - userId:userId - accessToken:accessToken]; - - mxCredentials.identityServer = _identityServerURL; - mxCredentials.identityServerAccessToken = identityServerAccessToken; - mxCredentials.deviceId = [coder decodeObjectForKey:@"deviceId"]; - mxCredentials.allowedCertificate = [coder decodeObjectForKey:@"allowedCertificate"]; - [self prepareRESTClient]; [self registerAccountDataDidChangeIdentityServerNotification]; [self registerIdentityServiceDidChangeAccessTokenNotification]; - if ([coder decodeObjectForKey:@"threePIDs"]) - { - threePIDs = [coder decodeObjectForKey:@"threePIDs"]; - } - - if ([coder decodeObjectForKey:@"device"]) - { - _device = [coder decodeObjectForKey:@"device"]; - } - userPresence = MXPresenceUnknown; - if ([coder decodeObjectForKey:@"antivirusserverurl"]) - { - _antivirusServerURL = [coder decodeObjectForKey:@"antivirusserverurl"]; - } - - if ([coder decodeObjectForKey:@"pushgatewayurl"]) - { - _pushGatewayURL = [coder decodeObjectForKey:@"pushgatewayurl"]; - } - - _hasPusherForPushNotifications = [coder decodeBoolForKey:@"_enablePushNotifications"]; - _hasPusherForPushKitNotifications = [coder decodeBoolForKey:@"enablePushKitNotifications"]; - _enableInAppNotifications = [coder decodeBoolForKey:@"enableInAppNotifications"]; - - _disabled = [coder decodeBoolForKey:@"disabled"]; - _isSoftLogout = [coder decodeBoolForKey:@"isSoftLogout"]; - - _warnedAboutEncryption = [coder decodeBoolForKey:@"warnedAboutEncryption"]; - - _others = [coder decodeObjectForKey:@"others"]; - // Refresh device information [self loadDeviceInformation:nil failure:nil]; } @@ -237,60 +190,6 @@ - (id)initWithCoder:(NSCoder *)coder return self; } -- (void)encodeWithCoder:(NSCoder *)coder -{ - [coder encodeObject:mxCredentials.homeServer forKey:@"homeserverurl"]; - [coder encodeObject:mxCredentials.userId forKey:@"userid"]; - [coder encodeObject:mxCredentials.accessToken forKey:@"accesstoken"]; - [coder encodeObject:mxCredentials.identityServerAccessToken forKey:@"identityserveraccesstoken"]; - - if (mxCredentials.deviceId) - { - [coder encodeObject:mxCredentials.deviceId forKey:@"deviceId"]; - } - - if (mxCredentials.allowedCertificate) - { - [coder encodeObject:mxCredentials.allowedCertificate forKey:@"allowedCertificate"]; - } - - if (self.threePIDs) - { - [coder encodeObject:threePIDs forKey:@"threePIDs"]; - } - - if (self.device) - { - [coder encodeObject:_device forKey:@"device"]; - } - - if (self.identityServerURL) - { - [coder encodeObject:_identityServerURL forKey:@"identityserverurl"]; - } - - if (self.antivirusServerURL) - { - [coder encodeObject:_antivirusServerURL forKey:@"antivirusserverurl"]; - } - - if (self.pushGatewayURL) - { - [coder encodeObject:_pushGatewayURL forKey:@"pushgatewayurl"]; - } - - [coder encodeBool:_hasPusherForPushNotifications forKey:@"_enablePushNotifications"]; - [coder encodeBool:_hasPusherForPushKitNotifications forKey:@"enablePushKitNotifications"]; - [coder encodeBool:_enableInAppNotifications forKey:@"enableInAppNotifications"]; - - [coder encodeBool:_disabled forKey:@"disabled"]; - [coder encodeBool:_isSoftLogout forKey:@"isSoftLogout"]; - - [coder encodeBool:_warnedAboutEncryption forKey:@"warnedAboutEncryption"]; - - [coder encodeObject:_others forKey:@"others"]; -} - #pragma mark - Properties - (void)setIdentityServerURL:(NSString *)identityServerURL @@ -298,10 +197,10 @@ - (void)setIdentityServerURL:(NSString *)identityServerURL if (identityServerURL.length) { _identityServerURL = identityServerURL; - mxCredentials.identityServer = identityServerURL; + self.mxCredentials.identityServer = identityServerURL; // Update services used in MXSession - [mxSession setIdentityServer:mxCredentials.identityServer andAccessToken:mxCredentials.identityServerAccessToken]; + [mxSession setIdentityServer:self.mxCredentials.identityServer andAccessToken:self.mxCredentials.identityServerAccessToken]; } else { @@ -355,24 +254,19 @@ - (NSString*)fullDisplayName { if (self.userDisplayName.length) { - return [NSString stringWithFormat:@"%@ (%@)", self.userDisplayName, mxCredentials.userId]; + return [NSString stringWithFormat:@"%@ (%@)", self.userDisplayName, self.mxCredentials.userId]; } else { - return mxCredentials.userId; + return self.mxCredentials.userId; } } -- (NSArray *)threePIDs -{ - return threePIDs; -} - - (NSArray *)linkedEmails { NSMutableArray *linkedEmails = [NSMutableArray array]; - for (MXThirdPartyIdentifier *threePID in threePIDs) + for (MXThirdPartyIdentifier *threePID in self.threePIDs) { if ([threePID.medium isEqualToString:kMX3PIDMediumEmail]) { @@ -387,7 +281,7 @@ - (NSString*)fullDisplayName { NSMutableArray *linkedPhoneNumbers = [NSMutableArray array]; - for (MXThirdPartyIdentifier *threePID in threePIDs) + for (MXThirdPartyIdentifier *threePID in self.threePIDs) { if ([threePID.medium isEqualToString:kMX3PIDMediumMSISDN]) { @@ -402,7 +296,7 @@ - (UIColor*)userTintColor { if (!userTintColor) { - userTintColor = [MXKTools colorWithRGBValue:[mxCredentials.userId hash]]; + userTintColor = [MXKTools colorWithRGBValue:[self.mxCredentials.userId hash]]; } return userTintColor; @@ -410,7 +304,7 @@ - (UIColor*)userTintColor - (BOOL)pushNotificationServiceIsActive { - BOOL pushNotificationServiceIsActive = ([[MXKAccountManager sharedManager] isAPNSAvailable] && _hasPusherForPushNotifications && mxSession); + BOOL pushNotificationServiceIsActive = ([[MXKAccountManager sharedManager] isAPNSAvailable] && self.hasPusherForPushNotifications && mxSession); MXLogDebug(@"[MXKAccount][Push] pushNotificationServiceIsActive: %@", @(pushNotificationServiceIsActive)); return pushNotificationServiceIsActive; @@ -461,7 +355,7 @@ - (void)enablePushNotifications:(BOOL)enable } } } - else if (_hasPusherForPushNotifications) + else if (self.hasPusherForPushNotifications) { MXLogDebug(@"[MXKAccount][Push] enablePushNotifications: Disable APNS for %@ account", self.mxCredentials.userId); @@ -487,7 +381,7 @@ - (void)enablePushNotifications:(BOOL)enable - (BOOL)isPushKitNotificationActive { - BOOL isPushKitNotificationActive = ([[MXKAccountManager sharedManager] isPushAvailable] && _hasPusherForPushKitNotifications && mxSession); + BOOL isPushKitNotificationActive = ([[MXKAccountManager sharedManager] isPushAvailable] && self.hasPusherForPushKitNotifications && mxSession); MXLogDebug(@"[MXKAccount][Push] isPushKitNotificationActive: %@", @(isPushKitNotificationActive)); return isPushKitNotificationActive; @@ -535,7 +429,7 @@ - (void)enablePushKitNotifications:(BOOL)enable failure (error); } } - else if (_hasPusherForPushKitNotifications) + else if (self.hasPusherForPushKitNotifications) { MXLogDebug(@"[MXKAccount][Push] enablePushKitNotifications: Disable Push for %@ account", self.mxCredentials.userId); @@ -633,7 +527,7 @@ - (void)setUserDisplayName:(NSString*)displayname success:(void (^)(void))succes success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; } failure:failure]; } @@ -653,7 +547,7 @@ - (void)setUserAvatarUrl:(NSString*)avatarUrl success:(void (^)(void))success fa success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; } failure:failure]; } @@ -686,9 +580,9 @@ - (void)changePassword:(NSString*)oldPassword with:(NSString*)newPassword succes - (void)load3PIDs:(void (^)(void))success failure:(void (^)(NSError *))failure { + [mxRestClient threePIDs:^(NSArray *threePIDs2) { - - self->threePIDs = threePIDs2; + self->_threePIDs = threePIDs2; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; @@ -708,9 +602,9 @@ - (void)load3PIDs:(void (^)(void))success failure:(void (^)(NSError *))failure - (void)loadDeviceInformation:(void (^)(void))success failure:(void (^)(NSError *error))failure { - if (mxCredentials.deviceId) + if (self.mxCredentials.deviceId) { - [mxRestClient deviceByDeviceId:mxCredentials.deviceId success:^(MXDevice *device) { + [mxRestClient deviceByDeviceId:self.mxCredentials.deviceId success:^(MXDevice *device) { self->_device = device; @@ -751,21 +645,21 @@ - (void)setUserPresence:(MXPresence)presence andStatusMessage:(NSString *)status [mxSession.myUser setPresence:userPresence andStatusMessage:statusMessage success:^{ - MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) succeeded", self->mxCredentials.userId, (unsigned long)self->userPresence); + MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) succeeded", self.mxCredentials.userId, (unsigned long)self->userPresence); if (completion) { completion(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; } failure:^(NSError *error) { - MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) failed", self->mxCredentials.userId, (unsigned long)self->userPresence); + MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) failed", self.mxCredentials.userId, (unsigned long)self->userPresence); }]; } else if (hideUserPresence) { - MXLogDebug(@"[MXKAccount] %@: set user presence is disabled.", mxCredentials.userId); + MXLogDebug(@"[MXKAccount] %@: set user presence is disabled.", self.mxCredentials.userId); } } @@ -783,7 +677,7 @@ - (void)setUserPresence:(MXPresence)presence andStatusMessage:(NSString *)status -(void)openSessionWithStore:(id)store { // Sanity check - if (!mxCredentials || !mxRestClient) + if (!self.mxCredentials || !mxRestClient) { MXLogDebug(@"[MXKAccount] Matrix session cannot be created without credentials"); return; @@ -1048,9 +942,9 @@ - (void)softLogout - (void)hydrateWithCredentials:(MXCredentials*)credentials { // Sanity check - if ([mxCredentials.userId isEqualToString:credentials.userId]) + if ([self.mxCredentials.userId isEqualToString:credentials.userId]) { - mxCredentials = credentials; + _mxCredentials = credentials; _isSoftLogout = NO; [[MXKAccountManager sharedManager] saveAccounts]; @@ -1058,11 +952,10 @@ - (void)hydrateWithCredentials:(MXCredentials*)credentials } else { - MXLogDebug(@"[MXKAccount] hydrateWithCredentials: Error: users ids mismatch: %@ vs %@", credentials.userId, mxCredentials.userId); + MXLogDebug(@"[MXKAccount] hydrateWithCredentials: Error: users ids mismatch: %@ vs %@", credentials.userId, self.mxCredentials.userId); } } - - (void)deletePusher { if (self.pushNotificationServiceIsActive) @@ -1259,7 +1152,7 @@ - (void)enableAPNSPusher:(BOOL)enabled success:(void (^)(void))success failure:( success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId]; } failure:^(NSError *error) { @@ -1278,7 +1171,7 @@ - (void)enableAPNSPusher:(BOOL)enabled success:(void (^)(void))success failure:( success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId]; return; } @@ -1295,7 +1188,7 @@ - (void)enableAPNSPusher:(BOOL)enabled success:(void (^)(void))success failure:( failure(error); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountAPNSActivityDidChangeNotification object:self.mxCredentials.userId]; }]; } @@ -1322,7 +1215,7 @@ - (void)refreshPushKitPusher MXLogDebug(@"[MXKAccount][Push] refreshPushKitPusher: Error: %@", error); }]; } - else if (_hasPusherForPushKitNotifications) + else if (self.hasPusherForPushKitNotifications) { if ([MXKAccountManager sharedManager].pushDeviceToken) { @@ -1336,7 +1229,7 @@ - (void)refreshPushKitPusher else { MXLogDebug(@"[MXKAccount][Push] refreshPushKitPusher: PushKit pusher for %@ account is already disabled. Reset _hasPusherForPushKitNotifications", self.mxCredentials.userId); - _hasPusherForPushKitNotifications = NO; + self->_hasPusherForPushKitNotifications = NO; [[MXKAccountManager sharedManager] saveAccounts]; } } @@ -1398,7 +1291,7 @@ - (void)enablePushKitPusher:(BOOL)enabled success:(void (^)(void))success failur success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId]; } failure:^(NSError *error) { @@ -1417,7 +1310,7 @@ - (void)enablePushKitPusher:(BOOL)enabled success:(void (^)(void))success failur success(); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId]; return; } @@ -1434,7 +1327,7 @@ - (void)enablePushKitPusher:(BOOL)enabled success:(void (^)(void))success failur failure(error); } - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountPushKitActivityDidChangeNotification object:self.mxCredentials.userId]; }]; } @@ -1443,7 +1336,7 @@ - (void)enablePusher:(BOOL)enabled appId:(NSString*)appId token:(NSData*)token p MXLogDebug(@"[MXKAccount][Push] enablePusher: %@", @(enabled)); // Refuse to try & turn push on if we're not logged in, it's nonsensical. - if (!mxCredentials) + if (!self.mxCredentials) { MXLogDebug(@"[MXKAccount][Push] enablePusher: Not setting push token because we're not logged in"); return; @@ -1610,7 +1503,7 @@ - (void)launchInitialServerSync [self.mxSession startWithSyncFilter:syncFilter onServerSyncDone:^{ MXStrongifyAndReturnIfNil(self); - MXLogDebug(@"[MXKAccount] %@: The session is ready. Matrix SDK session has been started in %0.fms.", self->mxCredentials.userId, [[NSDate date] timeIntervalSinceDate:self->openSessionStartDate] * 1000); + MXLogDebug(@"[MXKAccount] %@: The session is ready. Matrix SDK session has been started in %0.fms.", self.mxCredentials.userId, [[NSDate date] timeIntervalSinceDate:self->openSessionStartDate] * 1000); [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; @@ -1745,11 +1638,11 @@ - (void)onMatrixSessionStateChange } // Here displayname or other information have been updated, post update notification. - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self->mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; }]; // User information are just up-to-date (`mxSession` is running), post update notification. - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; } } else if (mxSession.state == MXSessionStateStoreDataReady || mxSession.state == MXSessionStateSyncInProgress) @@ -1764,40 +1657,30 @@ - (void)onMatrixSessionStateChange { // Here the initial server sync is in progress. The session is not running yet, but some user's information are available (from local storage). // We post update notification to let observer take into account this user's information even if they may not be up-to-date. - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:mxCredentials.userId]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId]; } } else if (mxSession.state == MXSessionStatePaused) { isPauseRequested = NO; } - else if (mxSession.state == MXSessionStateUnknownToken) - { - // Logout this account - [[MXKAccountManager sharedManager] removeAccount:self completion:nil]; - } - else if (mxSession.state == MXSessionStateSoftLogout) - { - // Soft logout this account - [[MXKAccountManager sharedManager] softLogout:self]; - } } - (void)prepareRESTClient { - if (!mxCredentials) + if (!self.mxCredentials) { return; } - - mxRestClient = [[MXRestClient alloc] initWithCredentials:mxCredentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) { - + MXWeakify(self); + mxRestClient = [[MXRestClient alloc] initWithCredentials:self.mxCredentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) { + MXStrongifyAndReturnValueIfNil(self, NO); if (_onCertificateChangeBlock) { if (_onCertificateChangeBlock (self, certificate)) { // Update the certificate in credentials - self->mxCredentials.allowedCertificate = certificate; + self.mxCredentials.allowedCertificate = certificate; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; @@ -1805,16 +1688,39 @@ - (void)prepareRESTClient return YES; } - self->mxCredentials.ignoredCertificate = certificate; + self.mxCredentials.ignoredCertificate = certificate; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; } return NO; + } andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [MXKAccountManager.sharedManager readAndWriteCredentials:handler]; + } andUnauthenticatedHandler:^(MXError *error, BOOL isSoftLogout, BOOL isRefreshTokenAuth, void (^completion)(void)) { + MXStrongifyAndReturnIfNil(self); + [self handleUnauthenticatedWithError:error isSoftLogout:isSoftLogout isRefreshTokenAuth:isRefreshTokenAuth andCompletion:completion]; }]; } +- (void)handleUnauthenticatedWithError:(MXError *)error isSoftLogout:(BOOL)isSoftLogout isRefreshTokenAuth:(BOOL)isRefreshTokenAuth andCompletion:(void (^)(void))completion +{ + + [Analytics.shared trackAuthUnauthenticatedErrorWithSoftLogout:isSoftLogout refreshTokenAuth:isRefreshTokenAuth errorCode:error.errcode errorReason:error.error]; + MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: trackAuthUnauthenticatedErrorWithSoftLogout sent"); + if (isSoftLogout) + { + MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: soft logout."); + [[MXKAccountManager sharedManager] softLogout:self]; + completion(); + } + else + { + MXLogDebug(@"[MXKAccountManager] handleUnauthenticated: hard logout."); + [[MXKAccountManager sharedManager] removeAccount:self sendLogoutRequest:NO completion:completion]; + } +} + - (void)onDateTimeFormatUpdate { if ([mxSession.roomSummaryUpdateDelegate isKindOfClass:MXKEventFormatter.class]) @@ -1871,7 +1777,7 @@ - (void)onDateTimeFormatUpdate #pragma mark - Crypto - (void)resetDeviceId { - mxCredentials.deviceId = nil; + self.mxCredentials.deviceId = nil; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; @@ -2187,11 +2093,11 @@ - (void)handleAccountDataDidChangeIdentityServerNotification:(NSNotification*)no MXSession *mxSession = notification.object; if (mxSession == self.mxSession) { - if (![mxCredentials.identityServer isEqualToString:self.mxSession.accountDataIdentityServer]) + if (![self.mxCredentials.identityServer isEqualToString:self.mxSession.accountDataIdentityServer]) { _identityServerURL = self.mxSession.accountDataIdentityServer; - mxCredentials.identityServer = _identityServerURL; - mxCredentials.identityServerAccessToken = nil; + self.mxCredentials.identityServer = _identityServerURL; + self.mxCredentials.identityServerAccessToken = nil; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; @@ -2204,7 +2110,7 @@ - (void)handleAccountDataDidChangeIdentityServerNotification:(NSNotification*)no - (void)identityService:(MXIdentityService *)identityService didUpdateAccessToken:(NSString *)accessToken { - mxCredentials.identityServerAccessToken = accessToken; + self.mxCredentials.identityServerAccessToken = accessToken; } - (void)registerIdentityServiceDidChangeAccessTokenNotification @@ -2220,9 +2126,9 @@ - (void)handleIdentityServiceDidChangeAccessTokenNotification:(NSNotification*)n NSString *identityServer = userInfo[MXIdentityServiceNotificationIdentityServerKey]; NSString *accessToken = userInfo[MXIdentityServiceNotificationAccessTokenKey]; - if (userId && identityServer && accessToken && [mxCredentials.identityServer isEqualToString:identityServer]) + if (userId && identityServer && accessToken && [self.mxCredentials.identityServer isEqualToString:identityServer]) { - mxCredentials.identityServerAccessToken = accessToken; + self.mxCredentials.identityServerAccessToken = accessToken; // Archive updated field [[MXKAccountManager sharedManager] saveAccounts]; diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h new file mode 100644 index 0000000000..3098f66d11 --- /dev/null +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h @@ -0,0 +1,118 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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. +// +#import + +@class MXKAccountData; + +@interface MXKAccountData : NSObject { + +@protected MXCredentials *_mxCredentials; +@protected NSString *_identityServerURL; +@protected NSString *_antivirusServerURL; +@protected NSString *_pushGatewayURL; +@protected MXDevice *_device; +@protected BOOL _disabled; +@protected BOOL _enableInAppNotifications; +@protected BOOL _warnedAboutEncryption; +@protected NSMutableDictionary> *_others; +@protected NSArray *_threePIDs; +@protected BOOL _isSoftLogout; +@protected BOOL _hasPusherForPushNotifications; +@protected BOOL _hasPusherForPushKitNotifications; +} + +/** + The account's credentials: homeserver, access token, user id. + */ +@property (nonatomic, readonly, nonnull) MXCredentials *mxCredentials; + +/** + The identity server URL. + */ +@property (nonatomic, nonnull) NSString *identityServerURL; + +/** + The antivirus server URL, if any (nil by default). + Set a non-null url to configure the antivirus scanner use. + */ +@property (nonatomic, nullable) NSString *antivirusServerURL; + +/** + The Push Gateway URL used to send event notifications to (nil by default). + This URL should be over HTTPS and never over HTTP. + */ +@property (nonatomic, nullable) NSString *pushGatewayURL; + +/** + The 3PIDs linked to this account. + [self load3PIDs] must be called to update the property. + */ +@property (nonatomic, readonly, nullable) NSArray *threePIDs; + +/** + The account user's device. + [self loadDeviceInformation] must be called to update the property. + */ +@property (nonatomic, readonly, nullable) MXDevice *device; + +/** + Transient information storage. + */ +@property (nonatomic, strong, readonly, nonnull) NSMutableDictionary> *others; + +/** + Flag to indicate that an APNS pusher has been set on the homeserver for this device. + */ +@property (nonatomic, readonly) BOOL hasPusherForPushNotifications; + +/** + The Push notification activity (based on PushKit) for this account. + YES when Push is turned on (locally available and enabled homeserver side). + */ +@property (nonatomic, readonly) BOOL isPushKitNotificationActive; + +/** + Flag to indicate that a PushKit pusher has been set on the homeserver for this device. + */ +@property (nonatomic, readonly) BOOL hasPusherForPushKitNotifications; + + +/** + Enable In-App notifications based on Remote notifications rules. + NO by default. + */ +@property (nonatomic) BOOL enableInAppNotifications; + +/** + Disable the account without logging out (NO by default). + + A matrix session is automatically opened for the account when this property is toggled from YES to NO. + The session is closed when this property is set to YES. + */ +@property (nonatomic,getter=isDisabled) BOOL disabled; + +/** + Flag indicating if the end user has been warned about encryption and its limitations. + */ +@property (nonatomic,getter=isWarnedAboutEncryption) BOOL warnedAboutEncryption; + +#pragma mark - Soft logout + +/** + Flag to indicate if the account has been logged out by the homeserver admin. + */ +@property (nonatomic, readonly) BOOL isSoftLogout; +@end diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m new file mode 100644 index 0000000000..4d59e2a4b7 --- /dev/null +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m @@ -0,0 +1,150 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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. +// + +#import +#import "MXKAccountData.h" + +@interface MXKAccountData () + +@end + +@implementation MXKAccountData + +@synthesize mxCredentials = _mxCredentials; + +#pragma mark - NSCoding + +- (id)initWithCoder:(NSCoder *)coder +{ + self = [super init]; + + if (self) + { + + NSString *homeServerURL = [coder decodeObjectForKey:@"homeserverurl"]; + NSString *userId = [coder decodeObjectForKey:@"userid"]; + NSString *accessToken = [coder decodeObjectForKey:@"accesstoken"]; + _identityServerURL = [coder decodeObjectForKey:@"identityserverurl"]; + NSString *identityServerAccessToken = [coder decodeObjectForKey:@"identityserveraccesstoken"]; + + _mxCredentials = [[MXCredentials alloc] initWithHomeServer:homeServerURL + userId:userId + accessToken:accessToken]; + + _mxCredentials.accessTokenExpiresAt = [coder decodeInt64ForKey:@"accessTokenExpiresAt"]; + _mxCredentials.refreshToken = [coder decodeObjectForKey:@"refreshToken"]; + _mxCredentials.identityServer = _identityServerURL; + _mxCredentials.identityServerAccessToken = identityServerAccessToken; + _mxCredentials.deviceId = [coder decodeObjectForKey:@"deviceId"]; + _mxCredentials.allowedCertificate = [coder decodeObjectForKey:@"allowedCertificate"]; + + if ([coder decodeObjectForKey:@"threePIDs"]) + { + _threePIDs = [coder decodeObjectForKey:@"threePIDs"]; + } + + if ([coder decodeObjectForKey:@"device"]) + { + _device = [coder decodeObjectForKey:@"device"]; + } + + if ([coder decodeObjectForKey:@"antivirusserverurl"]) + { + _antivirusServerURL = [coder decodeObjectForKey:@"antivirusserverurl"]; + } + + if ([coder decodeObjectForKey:@"pushgatewayurl"]) + { + _pushGatewayURL = [coder decodeObjectForKey:@"pushgatewayurl"]; + } + + _hasPusherForPushNotifications = [coder decodeBoolForKey:@"_enablePushNotifications"]; + _hasPusherForPushKitNotifications = [coder decodeBoolForKey:@"enablePushKitNotifications"]; + _enableInAppNotifications = [coder decodeBoolForKey:@"enableInAppNotifications"]; + + _disabled = [coder decodeBoolForKey:@"disabled"]; + _isSoftLogout = [coder decodeBoolForKey:@"isSoftLogout"]; + + _warnedAboutEncryption = [coder decodeBoolForKey:@"warnedAboutEncryption"]; + + _others = [coder decodeObjectForKey:@"others"]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:_mxCredentials.homeServer forKey:@"homeserverurl"]; + [coder encodeObject:_mxCredentials.userId forKey:@"userid"]; + [coder encodeObject:_mxCredentials.accessToken forKey:@"accesstoken"]; + if (self.mxCredentials.accessTokenExpiresAt) { + [coder encodeInt64:_mxCredentials.accessTokenExpiresAt forKey:@"accessTokenExpiresAt"]; + } + if (self.mxCredentials.refreshToken) { + [coder encodeObject:_mxCredentials.refreshToken forKey:@"refreshToken"]; + } + [coder encodeObject:_mxCredentials.identityServerAccessToken forKey:@"identityserveraccesstoken"]; + + if (self.mxCredentials.deviceId) + { + [coder encodeObject:_mxCredentials.deviceId forKey:@"deviceId"]; + } + + if (self.mxCredentials.allowedCertificate) + { + [coder encodeObject:_mxCredentials.allowedCertificate forKey:@"allowedCertificate"]; + } + + if (self.threePIDs) + { + [coder encodeObject:_threePIDs forKey:@"threePIDs"]; + } + + if (self.device) + { + [coder encodeObject:_device forKey:@"device"]; + } + + if (self.identityServerURL) + { + [coder encodeObject:_identityServerURL forKey:@"identityserverurl"]; + } + + if (self.antivirusServerURL) + { + [coder encodeObject:_antivirusServerURL forKey:@"antivirusserverurl"]; + } + + if (self.pushGatewayURL) + { + [coder encodeObject:_pushGatewayURL forKey:@"pushgatewayurl"]; + } + + [coder encodeBool:_hasPusherForPushNotifications forKey:@"_enablePushNotifications"]; + [coder encodeBool:_hasPusherForPushKitNotifications forKey:@"enablePushKitNotifications"]; + [coder encodeBool:_enableInAppNotifications forKey:@"enableInAppNotifications"]; + + [coder encodeBool:_disabled forKey:@"disabled"]; + [coder encodeBool:_isSoftLogout forKey:@"isSoftLogout"]; + + [coder encodeBool:_warnedAboutEncryption forKey:@"warnedAboutEncryption"]; + + [coder encodeObject:_others forKey:@"others"]; +} + + +@end diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h index 70c4857c9f..cfbec2a9ab 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h @@ -110,6 +110,8 @@ extern NSString *const MXKAccountManagerDataType; */ + (MXKAccountManager *)sharedManager; ++ (MXKAccountManager *)sharedManagerWithReload:(BOOL)reload; + /** Check for each enabled account if a matrix session is already opened. Open a matrix session for each enabled account which doesn't have a session. @@ -208,11 +210,6 @@ extern NSString *const MXKAccountManagerDataType; */ - (MXKAccount *)accountKnowingUserWithUserId:(NSString *)userId; -/** - Force the account manager to reload existing accounts from the local storage. - The account manager is supposed to handle itself the list of the accounts. - Call this method only when an account has been changed from an other application from the same group. - */ -- (void)forceReloadAccounts; +- (void)readAndWriteCredentials:(void (^)(NSArray * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler; @end diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m index 0763dab531..9c37c1696c 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m @@ -21,6 +21,8 @@ #import "MXKAppSettings.h" #import "MXKTools.h" +#import "MXKAccountData.h" +#import "MXRefreshTokenData.h" static NSString *const kMXKAccountsKeyOld = @"accounts"; static NSString *const kMXKAccountsKey = @"accountsV2"; @@ -43,13 +45,23 @@ The list of all accounts (enabled and disabled). Each value is a `MXKAccount` in @implementation MXKAccountManager + (MXKAccountManager *)sharedManager +{ + return [MXKAccountManager sharedManagerWithReload:NO]; +} + ++ (MXKAccountManager *)sharedManagerWithReload:(BOOL)reload { static MXKAccountManager *sharedAccountManager = nil; static dispatch_once_t onceToken; + __block BOOL didLoad = false; dispatch_once(&onceToken, ^{ + didLoad = true; sharedAccountManager = [[super allocWithZone:NULL] init]; }); + if (reload && !didLoad) { + [sharedAccountManager loadAccounts]; + } return sharedAccountManager; } @@ -599,7 +611,6 @@ - (NSString*)accountFile - (void)loadAccounts { MXLogDebug(@"[MXKAccountManager] loadAccounts"); - NSString *accountFile = [self accountFile]; if ([[NSFileManager defaultManager] fileExistsAtPath:accountFile]) { @@ -665,12 +676,6 @@ - (void)loadAccounts } } -- (void)forceReloadAccounts -{ - MXLogDebug(@"[MXKAccountManager] Force reload existing accounts from local storage"); - [self loadAccounts]; -} - - (NSData*)encryptData:(NSData*)data { // Exceptions are not caught as the key is always needed if the KeyProviderDelegate @@ -723,4 +728,69 @@ - (void)migrateAccounts } } +- (void)readAndWriteCredentials:(void (^)(NSArray * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler +{ + NSError *error; + NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init]; + __block BOOL coordinatorSuccess = NO; + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: purposeIdentifier = %@", fileCoordinator.purposeIdentifier); + NSDate *coordinateStartTime = [NSDate date]; + [fileCoordinator coordinateReadingItemAtURL:[self accountFileUrl] + options:0 + writingItemAtURL:[self accountFileUrl] + options:NSFileCoordinatorWritingForMerging + error:&error + byAccessor:^(NSURL * _Nonnull newReadingURL, NSURL * _Nonnull newWritingURL) { + + NSDate *accessorStartTime = [NSDate date]; + NSTimeInterval acquireInterval = [accessorStartTime timeIntervalSinceDate:coordinateStartTime]; + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: acquireInterval = %f", acquireInterval); + NSError *error = nil; + NSData* data = [NSData dataWithContentsOfURL:newReadingURL options:(NSDataReadingMappedAlways | NSDataReadingUncached) error:&error]; + + // Decrypt data if encryption method is provided + NSData *unciphered = [self decryptData:data]; + NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingFromData:unciphered error:&error]; + decoder.requiresSecureCoding = false; + [decoder setClass:[MXKAccountData class] forClassName:@"MXKAccount"]; + NSMutableArray* mxAccountsData = [decoder decodeObjectForKey:@"mxAccounts"]; + NSMutableArray* mxAccountCredentials = [NSMutableArray arrayWithCapacity:mxAccounts.count]; + for(MXKAccountData *account in mxAccountsData){ + [mxAccountCredentials addObject:account.mxCredentials]; + } + + dispatch_group_t dispatchGroup = dispatch_group_create(); + dispatch_group_enter(dispatchGroup); + + __block BOOL didUpdate = NO; + readAnWriteHandler(mxAccountCredentials, ^(BOOL didUpdateCredentials) { + didUpdate = didUpdateCredentials; + dispatch_group_leave(dispatchGroup); + }); + + dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER); + + if (didUpdate) { + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: did update saving credential data"); + NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO]; + [encoder setClassName:@"MXKAccount" forClass:[MXKAccountData class]]; + [encoder encodeObject:mxAccountsData forKey:@"mxAccounts"]; + NSData *writeData = [self encryptData:[encoder encodedData]]; + coordinatorSuccess = [writeData writeToURL:newWritingURL atomically:YES]; + } else { + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: did not update not saving credential data"); + coordinatorSuccess = YES; + } + NSDate *accessorEndTime = [NSDate date]; + NSTimeInterval lockedTime = [accessorEndTime timeIntervalSinceDate:accessorStartTime]; + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials: lockedTime = %f", lockedTime); + }]; + MXLogDebug(@"[MXKAccountManager] readAndWriteCredentials:exit %d", coordinatorSuccess); +} + +- (NSURL *)accountFileUrl +{ + return [NSURL fileURLWithPath: [self accountFile]]; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m b/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m index 58a460dfec..6e77a6dbdb 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m +++ b/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m @@ -270,7 +270,7 @@ - (void)registerLocationCellsForTableView:(UITableView*)tableView NSDictionary *incomingTextMessageCellsMapping = [self incomingTextMessageCellsMapping]; [cellClasses addEntriesFromDictionary:incomingTextMessageCellsMapping]; - NSDictionary *outgoingTextMessageCellsMapping = [self incomingTextMessageCellsMapping]; + NSDictionary *outgoingTextMessageCellsMapping = [self outgoingTextMessageCellsMapping]; [cellClasses addEntriesFromDictionary:outgoingTextMessageCellsMapping]; // Attachment diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index c77a9dd86b..5435362067 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -56,8 +56,14 @@ class NotificationService: UNNotificationServiceExtension { guard let userAccount = userAccount else { return nil } - return MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil) + let restClient = MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil, persistentTokenDataHandler: { persistTokenDataHandler in + MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler) + }, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in + userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion) + }) + return restClient }() + private static var isLoggerInitialized: Bool = false private lazy var pushGatewayRestClient: MXPushGatewayRestClient = { let url = URL(string: BuildSettings.serverConfigSygnalAPIUrlString)! @@ -91,6 +97,8 @@ class NotificationService: UNNotificationServiceExtension { // log memory at the beginning of the process logMemory() + setupAnalytics() + UNUserNotificationCenter.current().removeUnwantedNotifications() // check if this is a Matrix notification @@ -164,15 +172,26 @@ class NotificationService: UNNotificationServiceExtension { } } + private func setupAnalytics(){ + // Configure our analytics. It will start if the option is enabled + let analytics = Analytics.shared + MXSDKOptions.sharedInstance().analyticsDelegate = analytics + analytics.startIfEnabled() + } + private func setup(withRoomId roomId: String, eventId: String, completion: @escaping () -> Void) { - MXKAccountManager.shared()?.forceReloadAccounts() + MXKAccountManager.sharedManager(withReload: true) self.userAccount = MXKAccountManager.shared()?.activeAccounts.first if let userAccount = userAccount { Self.backgroundServiceInitQueue.sync { if NotificationService.backgroundSyncService?.credentials != userAccount.mxCredentials { MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: BEFORE") self.logMemory() - NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials) + NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials, persistTokenDataHandler: { persistTokenDataHandler in + MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler) + }, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in + userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion) + }) MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: AFTER") self.logMemory() } diff --git a/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h b/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h index 41b0ba485e..6409af92f6 100644 --- a/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h +++ b/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h @@ -21,4 +21,6 @@ #import "MatrixKit-Bridging-Header.h" +#import "BuildInfo.h" + #endif /* RiotNSE_Bridging_Header_h */ diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index de0dd69c1f..68a86ca51f 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -62,6 +62,8 @@ targets: - path: ../Riot/Managers/Widgets/WidgetConstants.m - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift - path: ../Riot/Modules/MatrixKit + - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/AppInfo/ excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift diff --git a/RiotShareExtension/Shared/ShareDataSource.m b/RiotShareExtension/Shared/ShareDataSource.m index 3c3ae631b2..83889c3332 100644 --- a/RiotShareExtension/Shared/ShareDataSource.m +++ b/RiotShareExtension/Shared/ShareDataSource.m @@ -81,8 +81,11 @@ - (void)loadCellData NSMutableArray *cellData = [NSMutableArray array]; + MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:self.credentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; + } andUnauthenticatedHandler:nil]; // Add a fake matrix session to each room summary to provide it a REST client (used to handle correctly the room avatar). - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:self.credentials andOnUnrecognizedCertificateBlock:nil]]; + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:mxRestClient]; for (id summary in summaries) { diff --git a/RiotShareExtension/Shared/ShareManager.m b/RiotShareExtension/Shared/ShareManager.m index d955f9da10..d242891888 100644 --- a/RiotShareExtension/Shared/ShareManager.m +++ b/RiotShareExtension/Shared/ShareManager.m @@ -78,7 +78,14 @@ - (UIViewController *)mainViewController - (void)shareViewController:(ShareViewController *)shareViewController didRequestShareForRoomIdentifiers:(NSSet *)roomIdentifiers { - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil]]; + MXWeakify(self); + MXRestClient *restClient = [[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; + } andUnauthenticatedHandler:^(MXError *error, BOOL isSoftLogout, BOOL isRefreshTokenAuth, void (^completion)(void)) { + MXStrongifyAndReturnIfNil(self); + [self.userAccount handleUnauthenticatedWithError:error isSoftLogout:isSoftLogout isRefreshTokenAuth:isRefreshTokenAuth andCompletion:completion]; + }]; + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:restClient]; [MXFileStore setPreloadOptions:0]; MXWeakify(session); @@ -147,7 +154,7 @@ - (void)showFailureAlert:(NSString *)title - (void)checkUserAccount { // Force account manager to reload account from the local storage. - [[MXKAccountManager sharedManager] forceReloadAccounts]; + [MXKAccountManager sharedManagerWithReload:YES]; if (self.userAccount) { diff --git a/RiotShareExtension/Sources/ShareExtensionRootViewController.m b/RiotShareExtension/Sources/ShareExtensionRootViewController.m index 90b5006e8f..c43eef81ec 100644 --- a/RiotShareExtension/Sources/ShareExtensionRootViewController.m +++ b/RiotShareExtension/Sources/ShareExtensionRootViewController.m @@ -52,6 +52,10 @@ - (void)viewDidLoad [MXLog configure:configuration]; + // Configure our analytics. It will start if the option is enabled + Analytics *analytics = Analytics.shared; + [MXSDKOptions sharedInstance].analyticsDelegate = analytics; + [analytics startIfEnabled]; [ThemeService.shared setThemeId:RiotSettings.shared.userInterfaceTheme]; diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index 2c8f71d647..494407323c 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -69,6 +69,7 @@ targets: - path: ../Riot/Assets/SharedImages.xcassets buildPhase: resources - path: ../Riot/Modules/MatrixKit + - path: ../Riot/Modules/Analytics excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 9c503501dc..fbf26003b3 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -53,6 +53,11 @@ - (instancetype)init } [MXLog configure:configuration]; + + // Configure our analytics. It will start if the option is enabled + Analytics *analytics = Analytics.shared; + [MXSDKOptions sharedInstance].analyticsDelegate = analytics; + [analytics startIfEnabled]; } return self; } diff --git a/SiriIntents/SupportingFiles/SiriIntents-Bridging-Header.h b/SiriIntents/SupportingFiles/SiriIntents-Bridging-Header.h index 306e2e582a..ca6d81962e 100644 --- a/SiriIntents/SupportingFiles/SiriIntents-Bridging-Header.h +++ b/SiriIntents/SupportingFiles/SiriIntents-Bridging-Header.h @@ -15,3 +15,4 @@ // #import "MatrixKit-Bridging-Header.h" +#import "BuildInfo.h" diff --git a/SiriIntents/target.yml b/SiriIntents/target.yml index d184497afd..ca60c3bc1a 100644 --- a/SiriIntents/target.yml +++ b/SiriIntents/target.yml @@ -51,6 +51,10 @@ targets: - path: ../Riot/Managers/Locale/LocaleProviderType.swift - path: ../Riot/Managers/Locale/LocaleProvider.swift - path: ../Riot/Modules/MatrixKit + - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/AppInfo/ + - path: ../Riot/Managers/Locale/LocaleProviderType.swift + - path: ../Riot/Generated/Strings.swift excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift