From 337b9576f4d388b2c1c4cbc5eb1f2aca33dc9812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 17 Sep 2024 16:50:18 +0200 Subject: [PATCH] Ensure signaling settings availability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCCallController.m | 3 + NextcloudTalk/NCConnectionController.m | 4 +- NextcloudTalk/NCExternalSignalingController.m | 2 +- NextcloudTalk/NCRoomsManagerExtensions.swift | 89 +++++++++++++------ NextcloudTalk/NCSettingsController.h | 15 ++-- NextcloudTalk/NCSettingsController.m | 84 ++++++++++------- 6 files changed, 129 insertions(+), 68 deletions(-) diff --git a/NextcloudTalk/NCCallController.m b/NextcloudTalk/NCCallController.m index dbf8c8a30..017f12f7a 100644 --- a/NextcloudTalk/NCCallController.m +++ b/NextcloudTalk/NCCallController.m @@ -97,6 +97,9 @@ - (instancetype)initWithDelegate:(id)delegate inRoom:( _signalingController.observer = self; _account = [[NCDatabaseManager sharedInstance] activeAccount]; + + // NCCallController is only initialized after joining the room. At that point we ensured that there's + // an external signaling controller set, in case we are using external signaling. _externalSignalingController = [[NCSettingsController sharedInstance] externalSignalingControllerForAccountId:_account.accountId]; _externalSignalingController.delegate = self; diff --git a/NextcloudTalk/NCConnectionController.m b/NextcloudTalk/NCConnectionController.m index e969deb3b..f9d03ce6a 100644 --- a/NextcloudTalk/NCConnectionController.m +++ b/NextcloudTalk/NCConnectionController.m @@ -111,14 +111,12 @@ - (void)checkAppState } [self setAppState:kAppStateMissingSignalingConfiguration]; - [[NCSettingsController sharedInstance] getSignalingConfigurationForAccountId:activeAccount.accountId withCompletionBlock:^(NSError *error) { + [[NCSettingsController sharedInstance] updateSignalingConfigurationForAccountId:activeAccount.accountId withCompletionBlock:^(NCExternalSignalingController * _Nullable signalingServer, NSError *error) { if (error) { [self notifyAppState]; return; } - // SetSignalingConfiguration should be called just once - [[NCSettingsController sharedInstance] setSignalingConfigurationForAccountId:activeAccount.accountId]; [self checkAppState]; }]; }]; diff --git a/NextcloudTalk/NCExternalSignalingController.m b/NextcloudTalk/NCExternalSignalingController.m index c77f64940..ba7656759 100644 --- a/NextcloudTalk/NCExternalSignalingController.m +++ b/NextcloudTalk/NCExternalSignalingController.m @@ -370,7 +370,7 @@ - (void)helloResponseReceived:(NSDictionary *)messageDict sessionChanged = NO; [self.delegate externalSignalingControllerWillRejoinCall:self]; - [[NCRoomsManager sharedInstance] rejoinRoom:_currentRoom completionBlock:^(NSString * _Nullable sessionId, NCRoom * _Nullable room, NSError * _Nullable error, NSInteger statusCode, NSString * _Nullable statusReason) { + [[NCRoomsManager sharedInstance] rejoinRoomForCall:_currentRoom completionBlock:^(NSString * _Nullable sessionId, NCRoom * _Nullable room, NSError * _Nullable error, NSInteger statusCode, NSString * _Nullable statusReason) { [self.delegate externalSignalingControllerShouldRejoinCall:self]; }]; } diff --git a/NextcloudTalk/NCRoomsManagerExtensions.swift b/NextcloudTalk/NCRoomsManagerExtensions.swift index 47f790cdd..541941701 100644 --- a/NextcloudTalk/NCRoomsManagerExtensions.swift +++ b/NextcloudTalk/NCRoomsManagerExtensions.swift @@ -7,12 +7,15 @@ import Foundation @objc extension NCRoomsManager { + public static let statusCodeFailedToJoinExternal = 997 public static let statusCodeShouldIgnoreAttemptButJoinedSuccessfully = 998 public static let statusCodeIgnoreJoinAttempt = 999 // MARK: - Join/Leave room public func joinRoom(_ token: String, forCall call: Bool) { + NCUtils.log("Joining room \(token) for call \(call)") + // Clean up joining room flag and attempts self.joiningRoomToken = nil self.joiningSessionId = nil @@ -33,6 +36,8 @@ import Foundation userInfo["token"] = token if let roomController = self.activeRooms[token] as? NCRoomController { + NCUtils.log("JoinRoomHelper: Found active room controller") + if call { roomController.inCall = true } else { @@ -103,6 +108,8 @@ import Foundation NCUtils.log("Could not join room. Status code: \(statusCode). Error: \(error?.localizedDescription ?? "")") } + self.joiningRoomToken = nil + self.joiningSessionId = nil NotificationCenter.default.post(name: .NCRoomsManagerDidJoinRoom, object: self, userInfo: userInfo) } } @@ -150,19 +157,24 @@ import Foundation NCUtils.log("Joined room \(token) in NC successfully") - guard let extSignalingController = NCSettingsController.sharedInstance().externalSignalingController(forAccountId: activeAccount.accountId) - else { - // Joined room in NC successfully and no external signaling server configured. - completionBlock(sessionId, room, nil, 0, nil) - return - } - - NCUtils.log("Trying to join room \(token) in external signaling server...") - // Remember the latest sessionId we're using to join a room, to be able to check when joining the external signaling server self.joiningSessionId = sessionId - self.getSignalingSettingsHelper(for: activeAccount, forRoom: token) { signalingSettings in + self.getExternalSignalingHelper(for: activeAccount, forRoom: token) { extSignalingController, signalingSettings, error in + guard error == nil else { + // There was an error to ensure we have the correct signaling settings for joining a federated conversation + completionBlock(nil, nil, nil, NCRoomsManager.statusCodeFailedToJoinExternal, nil) + return + } + + guard let extSignalingController else { + // Joined room in NC successfully and no external signaling server configured. + completionBlock(sessionId, room, nil, 0, nil) + return + } + + NCUtils.log("Trying to join room \(token) in external signaling server...") + let federation = signalingSettings?.getFederationJoinDictionary() extSignalingController.joinRoom(token, withSessionId: sessionId, withFederation: federation) { error in @@ -192,18 +204,36 @@ import Foundation }) } - private func getSignalingSettingsHelper(for account: TalkAccount, forRoom token: String, withCompletion completion: @escaping (SignalingSettings?) -> Void) { - // Currently we only need the signaling settings in case the room supports federation-v2 - if let room = NCDatabaseManager.sharedInstance().room(withToken: token, forAccountId: account.accountId), room.supportsFederatedCalling { - NCAPIController.sharedInstance().getSignalingSettings(for: account, forRoom: token) { signalingSettings, _ in - completion(signalingSettings) + private func getExternalSignalingHelper(for account: TalkAccount, forRoom token: String, withCompletion completion: @escaping (NCExternalSignalingController?, SignalingSettings?, Error?) -> Void) { + let room = NCDatabaseManager.sharedInstance().room(withToken: token, forAccountId: account.accountId) + + guard room?.supportsFederatedCalling ?? false else { + // No federated room -> just ensure that we have a signaling configuration and a potential external signaling controller + NCSettingsController.sharedInstance().ensureSignalingConfiguration(forAccountId: account.accountId, with: nil) { extSignalingController in + completion(extSignalingController, nil, nil) + } + + return + } + + // This is a federated conversation (with federated calling supported), so we require signaling settings for joining + // the external signaling controller + NCAPIController.sharedInstance().getSignalingSettings(for: account, forRoom: token) { signalingSettings, _ in + guard let signalingSettings else { + // We need to fail if we are unable to get signaling settings for a federation conversation + completion(nil, nil, NSError(domain: NSCocoaErrorDomain, code: 0)) + return + } + + NCSettingsController.sharedInstance().ensureSignalingConfiguration(forAccountId: account.accountId, with: signalingSettings) { extSignalingController in + completion(extSignalingController, signalingSettings, nil) } - } else { - completion(nil) } } - public func rejoinRoom(_ token: String, completionBlock: @escaping (_ sessionId: String?, _ room: NCRoom?, _ error: Error?, _ statusCode: Int, _ statusReason: String?) -> Void) { + public func rejoinRoomForCall(_ token: String, completionBlock: @escaping (_ sessionId: String?, _ room: NCRoom?, _ error: Error?, _ statusCode: Int, _ statusReason: String?) -> Void) { + NCUtils.log("Rejoining room \(token)") + guard let roomController = self.activeRooms[token] as? NCRoomController else { return } let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() @@ -212,16 +242,21 @@ import Foundation self.joinRoomTask = NCAPIController.sharedInstance().joinRoom(token, for: activeAccount, withCompletionBlock: { sessionId, room, error, statusCode, statusReason in if error == nil { roomController.userSessionId = sessionId - roomController.inChat = true + roomController.inCall = true - guard let extSignalingController = NCSettingsController.sharedInstance().externalSignalingController(forAccountId: activeAccount.accountId) - else { - // Joined room in NC successfully and no external signaling server configured. - completionBlock(sessionId, room, nil, 0, nil) - return - } + self.getExternalSignalingHelper(for: activeAccount, forRoom: token) { extSignalingController, signalingSettings, error in + guard error == nil else { + // There was an error to ensure we have the correct signaling settings for joining a federated conversation + completionBlock(nil, nil, nil, NCRoomsManager.statusCodeFailedToJoinExternal, nil) + return + } + + guard let extSignalingController else { + // Joined room in NC successfully and no external signaling server configured. + completionBlock(sessionId, room, nil, 0, nil) + return + } - self.getSignalingSettingsHelper(for: activeAccount, forRoom: token) { signalingSettings in let federation = signalingSettings?.getFederationJoinDictionary() extSignalingController.joinRoom(token, withSessionId: sessionId, withFederation: federation) { error in @@ -247,6 +282,8 @@ import Foundation public func leaveRoom(_ token: String) { // Check if leaving the room we are joining if self.isJoiningRoom(withToken: token) { + NCUtils.log("Leaving room \(token), but still joining -> cancel") + self.joiningRoomToken = nil self.joiningSessionId = nil self.joinRoomTask?.cancel() diff --git a/NextcloudTalk/NCSettingsController.h b/NextcloudTalk/NCSettingsController.h index 49d37fd7d..36e49f9da 100644 --- a/NextcloudTalk/NCSettingsController.h +++ b/NextcloudTalk/NCSettingsController.h @@ -29,19 +29,21 @@ extern NSString * const kUserProfileScopePublished; extern NSString * const NCSettingsControllerDidChangeActiveAccountNotification; +@class NCExternalSignalingController; +@class SignalingSettings; + typedef void (^UpdatedProfileCompletionBlock)(NSError *error); typedef void (^LogoutCompletionBlock)(NSError *error); typedef void (^GetCapabilitiesCompletionBlock)(NSError *error); -typedef void (^GetSignalingConfigCompletionBlock)(NSError *error); +typedef void (^UpdateSignalingConfigCompletionBlock)(NCExternalSignalingController * _Nullable signalingServer, NSError * _Nullable error); typedef void (^SubscribeForPushNotificationsCompletionBlock)(BOOL success); +typedef void (^EnsureSignalingConfigCompletionBlock)(NCExternalSignalingController * _Nullable signalingServer); typedef NS_ENUM(NSInteger, NCPreferredFileSorting) { NCAlphabeticalSorting = 1, NCModificationDateSorting }; -@class NCExternalSignalingController; - @interface NCSettingsController : NSObject @property (nonatomic, copy) ARDSettingsModel *videoSettingsModel; @@ -56,9 +58,10 @@ typedef NS_ENUM(NSInteger, NCPreferredFileSorting) { - (void)getUserProfileForAccountId:(NSString *)accountId withCompletionBlock:(UpdatedProfileCompletionBlock)block; - (void)logoutAccountWithAccountId:(NSString *)accountId withCompletionBlock:(LogoutCompletionBlock)block; - (void)getCapabilitiesForAccountId:(NSString *)accountId withCompletionBlock:(GetCapabilitiesCompletionBlock)block; -- (void)getSignalingConfigurationForAccountId:(NSString *)accountId withCompletionBlock:(GetSignalingConfigCompletionBlock)block; -- (void)setSignalingConfigurationForAccountId:(NSString *)accountId; -- (NCExternalSignalingController *)externalSignalingControllerForAccountId:(NSString *)accountId; +- (void)updateSignalingConfigurationForAccountId:(NSString * _Nonnull)accountId withCompletionBlock:(UpdateSignalingConfigCompletionBlock _Nonnull)block; +- (NCExternalSignalingController * _Nullable)setSignalingConfigurationForAccountId:(NSString * _Nonnull)accountId withSettings:(SignalingSettings * _Nonnull)settings; +- (void)ensureSignalingConfigurationForAccountId:(NSString * _Nonnull)accountId withSettings:(SignalingSettings * _Nullable)settings withCompletionBlock:(EnsureSignalingConfigCompletionBlock _Nonnull)block; +- (NCExternalSignalingController * _Nullable)externalSignalingControllerForAccountId:(NSString * _Nonnull)accountId; - (void)connectDisconnectedExternalSignalingControllers; - (void)disconnectAllExternalSignalingControllers; - (void)subscribeForPushNotificationsForAccountId:(NSString *)accountId withCompletionBlock:(SubscribeForPushNotificationsCompletionBlock)block; diff --git a/NextcloudTalk/NCSettingsController.m b/NextcloudTalk/NCSettingsController.m index 34858e192..2ca6af636 100644 --- a/NextcloudTalk/NCSettingsController.m +++ b/NextcloudTalk/NCSettingsController.m @@ -88,7 +88,7 @@ - (id)init _videoSettingsModel = [[ARDSettingsModel alloc] init]; _signalingConfigurations = [NSMutableDictionary new]; _externalSignalingControllers = [NSMutableDictionary new]; - + [self configureDatabase]; [self checkStoredDataInKechain]; @@ -276,21 +276,17 @@ - (void)talkConfigurationHasChanged:(NSNotification *)notification return; } - [[NCSettingsController sharedInstance] getCapabilitiesForAccountId:accountId withCompletionBlock:^(NSError *error) { + [self getCapabilitiesForAccountId:accountId withCompletionBlock:^(NSError *error) { if (error) { return; } - [[NCSettingsController sharedInstance] getSignalingConfigurationForAccountId:accountId withCompletionBlock:^(NSError *error) { - if (error) { - return; - } - - BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"NCUpdateSignalingConfiguration" expirationHandler:nil]; + BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"NCUpdateSignalingConfiguration" expirationHandler:nil]; - // SetSignalingConfiguration should be called just once - [[NCSettingsController sharedInstance] setSignalingConfigurationForAccountId:accountId]; - [[NCDatabaseManager sharedInstance] updateTalkConfigurationHashForAccountId:accountId withHash:configurationHash]; + [self updateSignalingConfigurationForAccountId:accountId withCompletionBlock:^(NCExternalSignalingController * _Nullable signalingServer, NSError *error) { + if (!error) { + [[NCDatabaseManager sharedInstance] updateTalkConfigurationHashForAccountId:accountId withHash:configurationHash]; + } [bgTask stopBackgroundTask]; }]; @@ -461,14 +457,14 @@ - (void)switchToAnyInactiveAccount #pragma mark - Signaling Configuration -- (void)getSignalingConfigurationForAccountId:(NSString *)accountId withCompletionBlock:(GetSignalingConfigCompletionBlock)block +- (void)updateSignalingConfigurationForAccountId:(NSString *)accountId withCompletionBlock:(UpdateSignalingConfigCompletionBlock)block { TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId]; if (!account) { if (block) { NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]; - block(error); + block(nil, error); } return; @@ -477,46 +473,70 @@ - (void)getSignalingConfigurationForAccountId:(NSString *)accountId withCompleti [[NCAPIController sharedInstance] getSignalingSettingsFor:account forRoom:nil completionBlock:^(SignalingSettings * _Nullable settings, NSError * _Nullable error) { if (!error) { if (settings && account && account.accountId) { - [self->_signalingConfigurations setObject:settings forKey:account.accountId]; + NCExternalSignalingController *extSignalingController = [self setSignalingConfigurationForAccountId:account.accountId withSettings:settings]; if (block) { - block(nil); + block(extSignalingController, nil); } } else { NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]; if (block) { - block(error); + block(nil, error); } } } else { NSLog(@"Error while getting signaling configuration"); - if (block) block(error); + if (block) { + block(nil, error); + } } }]; } -// SetSignalingConfiguration should be called just once -- (void)setSignalingConfigurationForAccountId:(NSString *)accountId +- (NCExternalSignalingController * _Nullable)setSignalingConfigurationForAccountId:(NSString *)accountId withSettings:(SignalingSettings * _Nonnull)signalingSettings { - SignalingSettings *signalingSettings = [_signalingConfigurations objectForKey:accountId]; + [self->_signalingConfigurations setObject:signalingSettings forKey:accountId]; if (signalingSettings.server && signalingSettings.server.length > 0 && signalingSettings.ticket && signalingSettings.ticket.length > 0) { BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"NCSetSignalingConfiguration" expirationHandler:nil]; + NCExternalSignalingController *extSignalingController = [self->_externalSignalingControllers objectForKey:accountId]; - dispatch_async(dispatch_get_main_queue(), ^{ - NCExternalSignalingController *extSignalingController = [self->_externalSignalingControllers objectForKey:accountId]; - - if (extSignalingController) { - [extSignalingController disconnect]; - } - - TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId]; - extSignalingController = [[NCExternalSignalingController alloc] initWithAccount:account server:signalingSettings.server andTicket:signalingSettings.ticket]; - [self->_externalSignalingControllers setObject:extSignalingController forKey:accountId]; + if (extSignalingController) { + [extSignalingController disconnect]; + } - [bgTask stopBackgroundTask]; - }); + TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId]; + extSignalingController = [[NCExternalSignalingController alloc] initWithAccount:account server:signalingSettings.server andTicket:signalingSettings.ticket]; + [self->_externalSignalingControllers setObject:extSignalingController forKey:accountId]; + + [bgTask stopBackgroundTask]; + + return extSignalingController; + } + + return nil; +} + +- (void)ensureSignalingConfigurationForAccountId:(NSString *)accountId withSettings:(SignalingSettings *)settings withCompletionBlock:(EnsureSignalingConfigCompletionBlock)block +{ + SignalingSettings *currentSignalingSettings = [_signalingConfigurations objectForKey:accountId]; + + if (currentSignalingSettings) { + block([self->_externalSignalingControllers objectForKey:accountId]); + } else { + [NCUtils log:@"Ensure signaling configuration -> Setting configuration"]; + + if (settings) { + // In case settings are provided, we use these provided settings + NCExternalSignalingController *extSignalingController = [self setSignalingConfigurationForAccountId:accountId withSettings:settings]; + block(extSignalingController); + } else { + // There were no settings provided for that call, we have to update the settings + [self updateSignalingConfigurationForAccountId:accountId withCompletionBlock:^(NCExternalSignalingController * _Nullable signalingServer, NSError *error) { + block(signalingServer); + }]; + } } }