diff --git a/CHANGES.rst b/CHANGES.rst index 66e29c7aa..a3ec1d05c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,10 +7,16 @@ Changes to be released in next version 🙌 Improvements * Social login: Handle new identity provider brand field in order to customize buttons (#3980). * Widgets: Support $matrix_room_id and $matrix_widget_id parameters (#3987). + * matrix.to: Support room preview when the permalink has parameters (like "via="). + * Avoid megolm share requests if the device is not verified (#3969) * Handle User-Interactive Authentication fallback (#3995). 🐛 Bugfix * Push: Fix PushKit crashes due to undecryptable call invites (#3986). + * matrix.to: Cannot open links with query parameters (#3990). + * matrix.to: Cannot open/preview a new room given by alias (#3991). + * matrix.to: The app does not open a permalink from matrix.to (#3993). + * Logs: Add a size limitation so that we can upload them in bug reports (#3903). ⚠️ API Changes * diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 21bb7c98c..1ab6cc0da 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -254,7 +254,8 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Redirect NSLogs to files only if we are not debugging if (!isatty(STDERR_FILENO)) { - [MXLogger redirectNSLogToFiles:YES numberOfFiles:50]; + NSUInteger sizeLimit = 100 * 1024 * 1024; // 100MB + [MXLogger redirectNSLogToFiles:YES numberOfFiles:50 sizeLimit:sizeLimit]; } NSLog(@"[AppDelegate] initialize: Done"); @@ -1146,9 +1147,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [[NSNotificationCenter defaultCenter] postNotificationName:AppDelegateUniversalLinkDidChangeNotification object:nil]; } - if ([webURL.path isEqualToString:@"/"]) + if ([self handleServerProvionningLink:webURL]) { - return [self handleServerProvionningLink:webURL]; + return YES; } NSString *validateEmailSubmitTokenPath = @"validate/email/submitToken"; @@ -1255,6 +1256,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni NSLog(@"[AppDelegate] Universal link: handleUniversalLinkFragment: %@", fragment); + // Make sure we have plain utf8 character for separators + fragment = [fragment stringByRemovingPercentEncoding]; + NSLog(@"[AppDelegate] Universal link: handleUniversalLinkFragment: %@", fragment); + // The app manages only one universal link at a time // Discard any pending one [self resetPendingUniversalLink]; @@ -1376,9 +1381,27 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni withString:[MXTools encodeURIComponent:roomId] ]; - universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; + // The previous operation can fail because of percent encoding + // TBH we are not clean on data inputs. For the moment, just give another try with no encoding + // TODO: Have a dedicated module and tests to handle universal links (matrix.to, email link, etc) + if ([newUniversalLinkFragment isEqualToString:fragment]) + { + newUniversalLinkFragment = + [fragment stringByReplacingOccurrencesOfString:roomIdOrAlias + withString:[MXTools encodeURIComponent:roomId]]; + } - [self handleUniversalLinkFragment:newUniversalLinkFragment]; + if (![newUniversalLinkFragment isEqualToString:fragment]) + { + universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; + + [self handleUniversalLinkFragment:newUniversalLinkFragment]; + } + else + { + // Do not continue. Else we will loop forever + NSLog(@"[AppDelegate] Universal link: Error: Cannot resolve alias in %@ to the room id %@", fragment, roomId); + } } } failure:^(NSError *error) { @@ -1422,40 +1445,32 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // FIXME: In case of multi-account, ask the user which one to use MXKAccount* account = accountManager.activeAccounts.firstObject; - RoomPreviewData *roomPreviewData; + RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomIdOrAlias + andSession:account.mxSession]; if (queryParams) { + roomPreviewData.viaServers = queryParams[@"via"]; + } + + // Is it a link to an event of a room? + // If yes, the event will be displayed once the room is joined + roomPreviewData.eventId = (pathParams.count >= 3) ? pathParams[2] : nil; + + // Try to get more information about the room before opening its preview + [roomPreviewData peekInRoom:^(BOOL succeeded) { + // Note: the activity indicator will not disappear if the session is not ready [homeViewController stopActivityIndicator]; - roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomIdOrAlias emailInvitationParams:queryParams andSession:account.mxSession]; - roomPreviewData.viaServers = queryParams[@"via"]; + // If no data is available for this room, we name it with the known room alias (if any). + if (!succeeded && universalLinkFragmentPendingRoomAlias[roomIdOrAlias]) + { + roomPreviewData.roomName = universalLinkFragmentPendingRoomAlias[roomIdOrAlias]; + } + universalLinkFragmentPendingRoomAlias = nil; + [self showRoomPreview:roomPreviewData]; - } - else - { - roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomIdOrAlias andSession:account.mxSession]; - - // Is it a link to an event of a room? - // If yes, the event will be displayed once the room is joined - roomPreviewData.eventId = (pathParams.count >= 3) ? pathParams[2] : nil; - - // Try to get more information about the room before opening its preview - [roomPreviewData peekInRoom:^(BOOL succeeded) { - - // Note: the activity indicator will not disappear if the session is not ready - [homeViewController stopActivityIndicator]; - - // If no data is available for this room, we name it with the known room alias (if any). - if (!succeeded && universalLinkFragmentPendingRoomAlias[roomIdOrAlias]) - { - roomPreviewData.roomName = universalLinkFragmentPendingRoomAlias[roomIdOrAlias]; - } - universalLinkFragmentPendingRoomAlias = nil; - - [self showRoomPreview:roomPreviewData]; - }]; - } + }]; } } @@ -2333,6 +2348,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni case MXSessionStateSyncInProgress: // Stay in launching during the first server sync if the store is empty. isLaunching = (mainSession.rooms.count == 0 && launchAnimationContainerView); + + if (mainSession.crypto.crossSigning && mainSession.crypto.crossSigning.state == MXCrossSigningStateCrossSigningExists) + { + [mainSession.crypto setOutgoingKeyRequestsEnabled:NO onComplete:nil]; + } break; default: isLaunching = NO; @@ -3726,81 +3746,100 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni return; } + MXWeakify(self); [mxSession.crypto pendingKeyRequests:^(MXUsersDevicesMap *> *pendingKeyRequests) { - - NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: pendingKeyRequests.count: %@. Already displayed: %@", + + MXStrongifyAndReturnIfNil(self); + NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: cross-signing state: %ld, pendingKeyRequests.count: %@. Already displayed: %@", + mxSession.crypto.crossSigning.state, @(pendingKeyRequests.count), - roomKeyRequestViewController ? @"YES" : @"NO"); + self->roomKeyRequestViewController ? @"YES" : @"NO"); - if (roomKeyRequestViewController) + if (!mxSession.crypto.crossSigning || mxSession.crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped) { - // Check if the current RoomKeyRequestViewController is still valid - MXSession *currentMXSession = roomKeyRequestViewController.mxSession; - NSString *currentUser = roomKeyRequestViewController.device.userId; - NSString *currentDevice = roomKeyRequestViewController.device.deviceId; - - NSArray *currentPendingRequest = [pendingKeyRequests objectForDevice:currentDevice forUser:currentUser]; - - if (currentMXSession == mxSession && currentPendingRequest.count == 0) + if (self->roomKeyRequestViewController) { - NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Cancel current dialog"); + // Check if the current RoomKeyRequestViewController is still valid + MXSession *currentMXSession = self->roomKeyRequestViewController.mxSession; + NSString *currentUser = self->roomKeyRequestViewController.device.userId; + NSString *currentDevice = self->roomKeyRequestViewController.device.deviceId; - // The key request has been probably cancelled, remove the popup - [roomKeyRequestViewController hide]; - roomKeyRequestViewController = nil; + NSArray *currentPendingRequest = [pendingKeyRequests objectForDevice:currentDevice forUser:currentUser]; + + if (currentMXSession == mxSession && currentPendingRequest.count == 0) + { + NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Cancel current dialog"); + + // The key request has been probably cancelled, remove the popup + [self->roomKeyRequestViewController hide]; + self->roomKeyRequestViewController = nil; + } } } - if (!roomKeyRequestViewController && pendingKeyRequests.count) + if (!self->roomKeyRequestViewController && pendingKeyRequests.count) { // Pick the first coming user/device pair NSString *userId = pendingKeyRequests.userIds.firstObject; NSString *deviceId = [pendingKeyRequests deviceIdsForUser:userId].firstObject; - + // Give the client a chance to refresh the device list + MXWeakify(self); [mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - + + MXStrongifyAndReturnIfNil(self); MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:deviceId forUser:userId]; if (deviceInfo) { - BOOL wasNewDevice = (deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown); - - void (^openDialog)(void) = ^void() + if (!mxSession.crypto.crossSigning || mxSession.crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped) { - NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@", deviceInfo); + BOOL wasNewDevice = (deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown); + + void (^openDialog)(void) = ^void() + { + NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@", deviceInfo); - roomKeyRequestViewController = [[RoomKeyRequestViewController alloc] initWithDeviceInfo:deviceInfo wasNewDevice:wasNewDevice andMatrixSession:mxSession onComplete:^{ + self->roomKeyRequestViewController = [[RoomKeyRequestViewController alloc] initWithDeviceInfo:deviceInfo wasNewDevice:wasNewDevice andMatrixSession:mxSession onComplete:^{ - roomKeyRequestViewController = nil; + self->roomKeyRequestViewController = nil; - // Check next pending key request, if any + // Check next pending key request, if any + [self checkPendingRoomKeyRequests]; + }]; + + [self->roomKeyRequestViewController show]; + }; + + // If the device was new before, it's not any more. + if (wasNewDevice) + { + [mxSession.crypto setDeviceVerification:MXDeviceUnverified forDevice:deviceId ofUser:userId success:openDialog failure:nil]; + } + else + { + openDialog(); + } + } + else if (deviceInfo.trustLevel.isVerified) + { + [mxSession.crypto acceptAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{ [self checkPendingRoomKeyRequests]; }]; - - [roomKeyRequestViewController show]; - }; - - // If the device was new before, it's not any more. - if (wasNewDevice) - { - [mxSession.crypto setDeviceVerification:MXDeviceUnverified forDevice:deviceId ofUser:userId success:openDialog failure:nil]; } else { - openDialog(); + [mxSession.crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{ + [self checkPendingRoomKeyRequests]; + }]; } } else { NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: No details found for device %@:%@", userId, deviceId); - - // Ignore this device to avoid to loop on it [mxSession.crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{ - // And check next requests [self checkPendingRoomKeyRequests]; }]; } - } failure:^(NSError *error) { // Retry later NSLog(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Failed to download device keys. Retry"); @@ -3972,6 +4011,12 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni - (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId { + MXCrypto *crypto = coordinatorBridgePresenter.session.crypto; + if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled) + { + NSLog(@"[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys"); + [crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; + } [self dismissKeyVerificationCoordinatorBridgePresenter]; } diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index c44b79f92..f769c5e48 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -1398,18 +1398,23 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; - (void)sessionStateDidChangeNotification:(NSNotification*)notification { MXSession *session = (MXSession*)notification.object; - - if (session.state == MXSessionStateRunning) + + if (session.state == MXSessionStateStoreDataReady) { - [self unregisterSessionStateChangeNotification]; - if (session.crypto.crossSigning) { // Do not make key share requests while the "Complete security" is not complete. // If the device is self-verified, the SDK will restore the existing key backup. // Then, it will re-enable outgoing key share requests [session.crypto setOutgoingKeyRequestsEnabled:NO onComplete:nil]; - + } + } + else if (session.state == MXSessionStateRunning) + { + [self unregisterSessionStateChangeNotification]; + + if (session.crypto.crossSigning) + { [session.crypto.crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) { NSLog(@"[AuthenticationVC] sessionStateDidChange: crossSigning.state: %@", @(session.crypto.crossSigning.state)); @@ -1619,14 +1624,17 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; - (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId { + MXCrypto *crypto = coordinatorBridgePresenter.session.crypto; + if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled) + { + NSLog(@"[AuthenticationVC][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys"); + [crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; + } [self dismiss]; } - (void)keyVerificationCoordinatorBridgePresenterDelegateDidCancel:(KeyVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter { - // Set outgoing key requests back - [coordinatorBridgePresenter.session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; } diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 27b14118f..65f156d32 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -121,6 +121,9 @@ #import "EventFormatter.h" #import +#import "SettingsViewController.h" +#import "SecurityViewController.h" + #import "Riot-Swift.h" @interface RoomViewController ()