talk-ios/NextcloudTalk/NCSettingsController.m

640 строки
27 KiB
Objective-C

/**
* @copyright Copyright (c) 2020 Ivan Sein <ivan@nextcloud.com>
*
* @author Ivan Sein <ivan@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#import "NCSettingsController.h"
#import <openssl/rsa.h>
#import <openssl/pem.h>
#import <openssl/bio.h>
#import <openssl/bn.h>
#import <openssl/sha.h>
#import <openssl/err.h>
#import "JDStatusBarNotification.h"
#import "OpenInFirefoxControllerObjC.h"
#import "NCAPIController.h"
#import "NCAppBranding.h"
#import "NCConnectionController.h"
#import "NCDatabaseManager.h"
#import "NCExternalSignalingController.h"
#import "NCKeyChainController.h"
#import "NCRoomsManager.h"
#import "NCUserInterfaceController.h"
#import "NCUserDefaults.h"
#import "NCChatFileController.h"
#import "NotificationCenterNotifications.h"
@interface NCSettingsController ()
{
NSString *_lockScreenPasscode;
NCPasscodeType _lockScreenPasscodeType;
}
@end
@implementation NCSettingsController
NSString * const kUserProfileUserId = @"id";
NSString * const kUserProfileDisplayName = @"displayname";
NSString * const kUserProfileDisplayNameScope = @"displaynameScope";
NSString * const kUserProfileEmail = @"email";
NSString * const kUserProfileEmailScope = @"emailScope";
NSString * const kUserProfilePhone = @"phone";
NSString * const kUserProfilePhoneScope = @"phoneScope";
NSString * const kUserProfileAddress = @"address";
NSString * const kUserProfileAddressScope = @"addressScope";
NSString * const kUserProfileWebsite = @"website";
NSString * const kUserProfileWebsiteScope = @"websiteScope";
NSString * const kUserProfileTwitter = @"twitter";
NSString * const kUserProfileTwitterScope = @"twitterScope";
NSString * const kUserProfileAvatarScope = @"avatarScope";
NSString * const kUserProfileScopePrivate = @"v2-private";
NSString * const kUserProfileScopeLocal = @"v2-local";
NSString * const kUserProfileScopeFederated = @"v2-federated";
NSString * const kUserProfileScopePublished = @"v2-published";
NSInteger const kDefaultChatMaxLength = 1000;
NSString * const kPreferredFileSorting = @"preferredFileSorting";
NSString * const kContactSyncEnabled = @"contactSyncEnabled";
+ (NCSettingsController *)sharedInstance
{
static dispatch_once_t once;
static NCSettingsController *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
_videoSettingsModel = [[ARDSettingsModel alloc] init];
_signalingConfigutations = [NSMutableDictionary new];
_externalSignalingControllers = [NSMutableDictionary new];
[self readValuesFromKeyChain];
[self configureDatabase];
[self checkStoredDataInKechain];
[self configureAppSettings];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tokenRevokedResponseReceived:) name:NCTokenRevokedResponseReceivedNotification object:nil];
}
return self;
}
#pragma mark - Database
- (void)configureDatabase
{
// Init database
[NCDatabaseManager sharedInstance];
// Check possible account migration to database
if (_ncUser && _ncServer) {
NSLog(@"Migrating user to the database");
TalkAccount *account = [[TalkAccount alloc] init];
account.accountId = [NSString stringWithFormat:@"%@@%@", _ncUser, _ncServer];
account.server = _ncServer;
account.user = _ncUser;
account.pushNotificationSubscribed = _pushNotificationSubscribed;
account.pushNotificationPublicKey = _ncPNPublicKey;
account.pushNotificationPublicKey = _ncPNPublicKey;
account.deviceIdentifier = _ncDeviceIdentifier;
account.deviceSignature = _ncDeviceSignature;
account.userPublicKey = _ncUserPublicKey;
account.active = YES;
[[NCKeyChainController sharedInstance] setToken:_ncToken forAccountId:account.accountId];
[[NCKeyChainController sharedInstance] setPushNotificationPrivateKey:_ncPNPrivateKey forAccountId:account.accountId];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:account];
}];
[self cleanUserAndServerStoredValues];
}
}
- (void)checkStoredDataInKechain
{
// Removed data stored in the Keychain if there are no accounts configured
// This step should be always done before the possible account migration
if ([[NCDatabaseManager sharedInstance] numberOfAccounts] == 0) {
NSLog(@"Removing all data stored in Keychain");
[self cleanUserAndServerStoredValues];
[[NCKeyChainController sharedInstance] removeAllItems];
}
}
#pragma mark - User accounts
- (void)addNewAccountForUser:(NSString *)user withToken:(NSString *)token inServer:(NSString *)server
{
NSString *accountId = [[NCDatabaseManager sharedInstance] accountIdForUser:user inServer:server];
TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
if (!account) {
[[NCDatabaseManager sharedInstance] createAccountForUser:user inServer:server];
[[NCDatabaseManager sharedInstance] setActiveAccountWithAccountId:accountId];
[[NCKeyChainController sharedInstance] setToken:token forAccountId:accountId];
TalkAccount *talkAccount = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
[[NCAPIController sharedInstance] createAPISessionManagerForAccount:talkAccount];
[self subscribeForPushNotificationsForAccountId:accountId];
} else {
[self setActiveAccountWithAccountId:accountId];
[JDStatusBarNotification showWithStatus:@"Account already added" dismissAfter:4.0f styleName:JDStatusBarStyleSuccess];
}
}
- (void)setActiveAccountWithAccountId:(NSString *)accountId
{
[[NCUserInterfaceController sharedInstance] presentConversationsList];
[[NCDatabaseManager sharedInstance] setActiveAccountWithAccountId:accountId];
[[NCDatabaseManager sharedInstance] resetUnreadBadgeNumberForAccountId:accountId];
[[NCNotificationController sharedInstance] removeAllNotificationsForAccountId:accountId];
[[NCConnectionController sharedInstance] checkAppState];
}
#pragma mark - Notifications
- (void)tokenRevokedResponseReceived:(NSNotification *)notification
{
NSString *accountId = [notification.userInfo objectForKey:@"accountId"];
[self logoutAccountWithAccountId:accountId withCompletionBlock:^(NSError *error) {
[[NCUserInterfaceController sharedInstance] presentConversationsList];
[[NCUserInterfaceController sharedInstance] presentLoggedOutInvalidCredentialsAlert];
[[NCConnectionController sharedInstance] checkAppState];
}];
}
#pragma mark - User defaults
- (NCPreferredFileSorting)getPreferredFileSorting
{
NCPreferredFileSorting sorting = (NCPreferredFileSorting)[[[NSUserDefaults standardUserDefaults] objectForKey:kPreferredFileSorting] integerValue];
if (!sorting) {
sorting = NCModificationDateSorting;
[[NSUserDefaults standardUserDefaults] setObject:@(sorting) forKey:kPreferredFileSorting];
}
return sorting;
}
- (void)setPreferredFileSorting:(NCPreferredFileSorting)sorting
{
[[NSUserDefaults standardUserDefaults] setObject:@(sorting) forKey:kPreferredFileSorting];
}
- (BOOL)isContactSyncEnabled
{
// Migration from global setting to per-account setting
if ([[[NSUserDefaults standardUserDefaults] objectForKey:kContactSyncEnabled] boolValue]) {
// If global setting was enabled then we enable contact sync for all accounts
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
for (TalkAccount *account in [TalkAccount allObjects]) {
account.hasContactSyncEnabled = YES;
}
[realm commitWriteTransaction];
// Remove global setting
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kContactSyncEnabled];
[[NSUserDefaults standardUserDefaults] synchronize];
return YES;
}
return [[NCDatabaseManager sharedInstance] activeAccount].hasContactSyncEnabled;
}
- (void)setContactSync:(BOOL)enabled
{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
TalkAccount *account = [TalkAccount objectsWhere:(@"active = true")].firstObject;
account.hasContactSyncEnabled = enabled;
[realm commitWriteTransaction];
}
#pragma mark - KeyChain
- (void)readValuesFromKeyChain
{
_ncServer = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCServerKey];
_ncUser = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCUserKey];
_ncUserId = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCUserIdKey];
_ncUserDisplayName = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCUserDisplayNameKey];
_ncToken = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCTokenKey];
_ncPushToken = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCPushTokenKey];
_ncNormalPushToken = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCNormalPushTokenKey];
_ncPushKitToken = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCPushKitTokenKey];
_pushNotificationSubscribed = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCPushSubscribedKey];
_ncPNPublicKey = [[NCKeyChainController sharedInstance].keychain dataForKey:kNCPNPublicKey];
_ncPNPrivateKey = [[NCKeyChainController sharedInstance].keychain dataForKey:kNCPNPrivateKey];
_ncDeviceIdentifier = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCDeviceIdentifier];
_ncDeviceSignature = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCDeviceSignature];
_ncUserPublicKey = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCUserPublicKey];
}
- (void)cleanUserAndServerStoredValues
{
_ncServer = nil;
_ncUser = nil;
_ncUserDisplayName = nil;
_ncToken = nil;
_ncPNPublicKey = nil;
_ncPNPrivateKey = nil;
_ncUserPublicKey = nil;
_ncDeviceIdentifier = nil;
_ncDeviceSignature = nil;
_pushNotificationSubscribed = nil;
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCServerKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCUserKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCUserDisplayNameKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCTokenKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCPushSubscribedKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCPNPublicKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCPNPrivateKey];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCDeviceIdentifier];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCDeviceSignature];
[[NCKeyChainController sharedInstance].keychain removeItemForKey:kNCUserPublicKey];
}
#pragma mark - User Profile
- (void)getUserProfileWithCompletionBlock:(UpdatedProfileCompletionBlock)block
{
[[NCAPIController sharedInstance] getUserProfileForAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSDictionary *userProfile, NSError *error) {
if (!error) {
id emailObject = [userProfile objectForKey:kUserProfileEmail];
NSString *email = emailObject;
if (!emailObject || [emailObject isEqual:[NSNull null]]) {
email = @"";
}
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
TalkAccount *managedActiveAccount = [TalkAccount objectsWhere:(@"active = true")].firstObject;
managedActiveAccount.userId = [userProfile objectForKey:kUserProfileUserId];
// "display-name" is returned by /cloud/user endpoint
// change to kUserProfileDisplayName ("displayName") when using /cloud/users/{userId} endpoint
managedActiveAccount.userDisplayName = [userProfile objectForKey:@"display-name"];
managedActiveAccount.userDisplayNameScope = [userProfile objectForKey:kUserProfileDisplayNameScope];
managedActiveAccount.phone = [userProfile objectForKey:kUserProfilePhone];
managedActiveAccount.phoneScope = [userProfile objectForKey:kUserProfilePhoneScope];
managedActiveAccount.email = email;
managedActiveAccount.emailScope = [userProfile objectForKey:kUserProfileEmailScope];
managedActiveAccount.address = [userProfile objectForKey:kUserProfileAddress];
managedActiveAccount.addressScope = [userProfile objectForKey:kUserProfileAddressScope];
managedActiveAccount.website = [userProfile objectForKey:kUserProfileWebsite];
managedActiveAccount.websiteScope = [userProfile objectForKey:kUserProfileWebsiteScope];
managedActiveAccount.twitter = [userProfile objectForKey:kUserProfileTwitter];
managedActiveAccount.twitterScope = [userProfile objectForKey:kUserProfileTwitterScope];
managedActiveAccount.avatarScope = [userProfile objectForKey:kUserProfileAvatarScope];
[realm commitWriteTransaction];
[[NCAPIController sharedInstance] saveProfileImageForAccount:[[NCDatabaseManager sharedInstance] activeAccount]];
if (block) block(nil);
} else {
NSLog(@"Error while getting the user profile");
if (block) block(error);
}
}];
}
- (void)logoutAccountWithAccountId:(NSString *)accountId withCompletionBlock:(LogoutCompletionBlock)block
{
TalkAccount *removingAccount = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
if (removingAccount.deviceIdentifier) {
[[NCAPIController sharedInstance] unsubscribeAccount:removingAccount fromNextcloudServerWithCompletionBlock:^(NSError *error) {
if (!error) {
NSLog(@"Unsubscribed from NC server!!!");
} else {
NSLog(@"Error while unsubscribing from NC server.");
}
}];
[[NCAPIController sharedInstance] unsubscribeAccount:removingAccount fromPushServerWithCompletionBlock:^(NSError *error) {
if (!error) {
NSLog(@"Unsubscribed from Push Notification server!!!");
} else {
NSLog(@"Error while unsubscribing from Push Notification server.");
}
}];
}
NCExternalSignalingController *extSignalingController = [self externalSignalingControllerForAccountId:removingAccount.accountId];
[extSignalingController disconnect];
[[NCSettingsController sharedInstance] cleanUserAndServerStoredValues];
[[NCAPIController sharedInstance] removeProfileImageForAccount:removingAccount];
[[NCDatabaseManager sharedInstance] removeAccountWithAccountId:removingAccount.accountId];
[[[NCChatFileController alloc] init] deleteDownloadDirectoryForAccount:removingAccount];
[[[NCRoomsManager sharedInstance] chatViewController] leaveChat];
// Activate any of the inactive accounts
NSArray *inactiveAccounts = [[NCDatabaseManager sharedInstance] inactiveAccounts];
if (inactiveAccounts.count > 0) {
TalkAccount *inactiveAccount = [inactiveAccounts objectAtIndex:0];
[self setActiveAccountWithAccountId:inactiveAccount.accountId];
}
if (block) block(nil);
}
#pragma mark - App settings
- (void)configureAppSettings
{
[self configureDefaultBrowser];
[self configureLockScreen];
}
#pragma mark - Default browser
- (void)configureDefaultBrowser
{
// Check supported browsers
NSMutableArray *supportedBrowsers = [[NSMutableArray alloc] initWithObjects:@"Safari", nil];
if ([[OpenInFirefoxControllerObjC sharedInstance] isFirefoxInstalled]) {
[supportedBrowsers addObject:@"Firefox"];
}
_supportedBrowsers = supportedBrowsers;
// Check if default browser is valid
if (![supportedBrowsers containsObject:[NCUserDefaults defaultBrowser]]) {
[NCUserDefaults setDefaultBrowser:@"Safari"];
}
}
#pragma mark - Lock screen
- (void)configureLockScreen
{
_lockScreenPasscode = [[NCKeyChainController sharedInstance].keychain stringForKey:kNCLockScreenPasscode];
}
- (void)setLockScreenPasscode:(NSString *)lockScreenPasscode
{
_lockScreenPasscode = lockScreenPasscode;
[[NCKeyChainController sharedInstance].keychain setString:lockScreenPasscode forKey:kNCLockScreenPasscode];
}
- (NCPasscodeType)lockScreenPasscodeType
{
NCPasscodeType passcodeType = (NCPasscodeType)[[[NSUserDefaults standardUserDefaults] objectForKey:kNCLockScreenPasscodeType] integerValue];
if (!passcodeType) {
passcodeType = NCPasscodeTypeSimple;
[[NSUserDefaults standardUserDefaults] setObject:@(passcodeType) forKey:kNCLockScreenPasscodeType];
}
return passcodeType;
}
- (void)setLockScreenPasscodeType:(NCPasscodeType)lockScreenPasscodeType
{
_lockScreenPasscodeType = lockScreenPasscodeType;
[[NSUserDefaults standardUserDefaults] setObject:@(lockScreenPasscodeType) forKey:kNCLockScreenPasscodeType];
}
#pragma mark - Signaling Configuration
- (void)getSignalingConfigurationWithCompletionBlock:(GetSignalingConfigCompletionBlock)block
{
[[NCAPIController sharedInstance] getSignalingSettingsForAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSDictionary *settings, NSError *error) {
TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
if (!error) {
NSDictionary *signalingConfiguration = [[settings objectForKey:@"ocs"] objectForKey:@"data"];
[self->_signalingConfigutations setObject:signalingConfiguration forKey:activeAccount.accountId];
if (block) block(nil);
} else {
NSLog(@"Error while getting signaling configuration");
if (block) block(error);
}
}];
}
// SetSignalingConfiguration should be called just once
- (void)setSignalingConfigurationForAccountId:(NSString *)accountId
{
NSDictionary *signalingConfiguration = [_signalingConfigutations objectForKey:accountId];
NSString *externalSignalingServer = nil;
id server = [signalingConfiguration objectForKey:@"server"];
if ([server isKindOfClass:[NSString class]] && ![server isEqualToString:@""]) {
externalSignalingServer = server;
}
NSString *externalSignalingTicket = [signalingConfiguration objectForKey:@"ticket"];
if (externalSignalingServer && externalSignalingTicket) {
dispatch_async(dispatch_get_main_queue(), ^{
NCExternalSignalingController *extSignalingController = [self->_externalSignalingControllers objectForKey:accountId];
if (!extSignalingController) {
TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
extSignalingController = [[NCExternalSignalingController alloc] initWithAccount:account server:externalSignalingServer andTicket:externalSignalingTicket];
[self->_externalSignalingControllers setObject:extSignalingController forKey:accountId];
}
});
}
}
- (NCExternalSignalingController *)externalSignalingControllerForAccountId:(NSString *)accountId
{
return [_externalSignalingControllers objectForKey:accountId];
}
#pragma mark - Server Capabilities
- (void)getCapabilitiesWithCompletionBlock:(GetCapabilitiesCompletionBlock)block;
{
[[NCAPIController sharedInstance] getServerCapabilitiesForAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSDictionary *serverCapabilities, NSError *error) {
TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
if (!error && [serverCapabilities isKindOfClass:[NSDictionary class]]) {
[[NCDatabaseManager sharedInstance] setServerCapabilities:serverCapabilities forAccountId:activeAccount.accountId];
[self checkServerCapabilities];
[[NSNotificationCenter defaultCenter] postNotificationName:NCServerCapabilitiesUpdatedNotification
object:self
userInfo:nil];
if (block) block(nil);
} else {
NSLog(@"Error while getting server capabilities");
if (block) block(error);
}
}];
}
- (void)checkServerCapabilities
{
TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:activeAccount.accountId];
if (serverCapabilities) {
NSArray *talkFeatures = [serverCapabilities.talkCapabilities valueForKey:@"self"];
if (!talkFeatures) {
[[NSNotificationCenter defaultCenter] postNotificationName:NCTalkNotInstalledNotification
object:self
userInfo:nil];
}
if (![talkFeatures containsObject:kMinimumRequiredTalkCapability]) {
[[NSNotificationCenter defaultCenter] postNotificationName:NCOutdatedTalkVersionNotification
object:self
userInfo:nil];
}
}
}
- (NSInteger)chatMaxLengthConfigCapability
{
TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:activeAccount.accountId];
if (serverCapabilities) {
NSInteger chatMaxLength = serverCapabilities.chatMaxLength;
return chatMaxLength > 0 ? chatMaxLength : kDefaultChatMaxLength;
}
return kDefaultChatMaxLength;
}
- (BOOL)canCreateGroupAndPublicRooms
{
TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:activeAccount.accountId];
if (serverCapabilities) {
return serverCapabilities.canCreate;
}
return YES;
}
#pragma mark - Push Notifications
- (void)subscribeForPushNotificationsForAccountId:(NSString *)accountId
{
#if !TARGET_IPHONE_SIMULATOR
if ([self generatePushNotificationsKeyPairForAccountId:accountId]) {
[[NCAPIController sharedInstance] subscribeAccount:[[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId] toNextcloudServerWithCompletionBlock:^(NSDictionary *responseDict, NSError *error) {
if (!error) {
NSLog(@"Subscribed to NC server successfully.");
NSString *publicKey = [responseDict objectForKey:@"publicKey"];
NSString *deviceIdentifier = [responseDict objectForKey:@"deviceIdentifier"];
NSString *signature = [responseDict objectForKey:@"signature"];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
managedAccount.userPublicKey = publicKey;
managedAccount.deviceIdentifier = deviceIdentifier;
managedAccount.deviceSignature = signature;
[realm commitWriteTransaction];
[[NCAPIController sharedInstance] subscribeAccount:[[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId] toPushServerWithCompletionBlock:^(NSError *error) {
if (!error) {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
managedAccount.pushNotificationSubscribed = YES;
[realm commitWriteTransaction];
NSLog(@"Subscribed to Push Notification server successfully.");
} else {
NSLog(@"Error while subscribing to Push Notification server.");
}
}];
} else {
NSLog(@"Error while subscribing to NC server.");
}
}];
}
#endif
}
- (BOOL)generatePushNotificationsKeyPairForAccountId:(NSString *)accountId
{
EVP_PKEY *pkey;
NSError *keyError;
pkey = [self generateRSAKey:&keyError];
if (keyError) {
return NO;
}
// Extract publicKey, privateKey
int len;
char *keyBytes;
// PublicKey
BIO *publicKeyBIO = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(publicKeyBIO, pkey);
len = BIO_pending(publicKeyBIO);
keyBytes = malloc(len);
BIO_read(publicKeyBIO, keyBytes, len);
NSData *pnPublicKey = [NSData dataWithBytes:keyBytes length:len];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
managedAccount.pushNotificationPublicKey = pnPublicKey;
[realm commitWriteTransaction];
NSLog(@"Push Notifications Key Pair generated: \n%@", [[NSString alloc] initWithData:pnPublicKey encoding:NSUTF8StringEncoding]);
// PrivateKey
BIO *privateKeyBIO = BIO_new(BIO_s_mem());
PEM_write_bio_PKCS8PrivateKey(privateKeyBIO, pkey, NULL, NULL, 0, NULL, NULL);
len = BIO_pending(privateKeyBIO);
keyBytes = malloc(len);
BIO_read(privateKeyBIO, keyBytes, len);
NSData *pnPrivateKey = [NSData dataWithBytes:keyBytes length:len];
[[NCKeyChainController sharedInstance] setPushNotificationPrivateKey:pnPrivateKey forAccountId:accountId];
EVP_PKEY_free(pkey);
return YES;
}
- (EVP_PKEY *)generateRSAKey:(NSError **)error
{
EVP_PKEY *pkey = EVP_PKEY_new();
if (!pkey) {
return NULL;
}
BIGNUM *bigNumber = BN_new();
int exponent = RSA_F4;
RSA *rsa = RSA_new();
if (BN_set_word(bigNumber, exponent) < 0) {
goto cleanup;
}
if (RSA_generate_key_ex(rsa, 2048, bigNumber, NULL) < 0) {
goto cleanup;
}
if (!EVP_PKEY_set1_RSA(pkey, rsa)) {
goto cleanup;
}
cleanup:
RSA_free(rsa);
BN_free(bigNumber);
return pkey;
}
@end