NCDatabaseManager.m 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "NCDatabaseManager.h"
  6. #import "ABContact.h"
  7. #import "NCAppBranding.h"
  8. #import "NCChatBlock.h"
  9. #import "NCChatMessage.h"
  10. #import "NCContact.h"
  11. #import "NCRoom.h"
  12. #import "NextcloudTalk-Swift.h"
  13. NSString *const kTalkDatabaseFolder = @"Library/Application Support/Talk";
  14. NSString *const kTalkDatabaseFileName = @"talk.realm";
  15. uint64_t const kTalkDatabaseSchemaVersion = 69;
  16. NSString * const kCapabilitySystemMessages = @"system-messages";
  17. NSString * const kCapabilityNotificationLevels = @"notification-levels";
  18. NSString * const kCapabilityInviteGroupsAndMails = @"invite-groups-and-mails";
  19. NSString * const kCapabilityLockedOneToOneRooms = @"locked-one-to-one-rooms";
  20. NSString * const kCapabilityWebinaryLobby = @"webinary-lobby";
  21. NSString * const kCapabilityChatReadMarker = @"chat-read-marker";
  22. NSString * const kCapabilityStartCallFlag = @"start-call-flag";
  23. NSString * const kCapabilityCirclesSupport = @"circles-support";
  24. NSString * const kCapabilityChatReferenceId = @"chat-reference-id";
  25. NSString * const kCapabilityPhonebookSearch = @"phonebook-search";
  26. NSString * const kCapabilityChatReadStatus = @"chat-read-status";
  27. NSString * const kCapabilityReadOnlyRooms = @"read-only-rooms";
  28. NSString * const kCapabilityListableRooms = @"listable-rooms";
  29. NSString * const kCapabilityDeleteMessages = @"delete-messages";
  30. NSString * const kCapabilityCallFlags = @"conversation-call-flags";
  31. NSString * const kCapabilityRoomDescription = @"room-description";
  32. NSString * const kCapabilityTempUserAvatarAPI = @"temp-user-avatar-api";
  33. NSString * const kCapabilityLocationSharing = @"geo-location-sharing";
  34. NSString * const kCapabilityConversationV4 = @"conversation-v4";
  35. NSString * const kCapabilitySIPSupport = @"sip-support";
  36. NSString * const kCapabilitySIPSupportNoPIN = @"sip-support-nopin";
  37. NSString * const kCapabilityVoiceMessage = @"voice-message-sharing";
  38. NSString * const kCapabilitySignalingV3 = @"signaling-v3";
  39. NSString * const kCapabilityClearHistory = @"clear-history";
  40. NSString * const kCapabilityDirectMentionFlag = @"direct-mention-flag";
  41. NSString * const kCapabilityNotificationCalls = @"notification-calls";
  42. NSString * const kCapabilityConversationPermissions = @"conversation-permissions";
  43. NSString * const kCapabilityChatUnread = @"chat-unread";
  44. NSString * const kCapabilityReactions = @"reactions";
  45. NSString * const kCapabilityRichObjectListMedia = @"rich-object-list-media";
  46. NSString * const kCapabilityRichObjectDelete = @"rich-object-delete";
  47. NSString * const kCapabilityUnifiedSearch = @"unified-search";
  48. NSString * const kCapabilityChatPermission = @"chat-permission";
  49. NSString * const kCapabilityMessageExpiration = @"message-expiration";
  50. NSString * const kCapabilitySilentSend = @"silent-send";
  51. NSString * const kCapabilitySilentCall = @"silent-call";
  52. NSString * const kCapabilitySendCallNotification = @"send-call-notification";
  53. NSString * const kCapabilityTalkPolls = @"talk-polls";
  54. NSString * const kCapabilityRaiseHand = @"raise-hand";
  55. NSString * const kCapabilityRecordingV1 = @"recording-v1";
  56. NSString * const kCapabilitySingleConvStatus = @"single-conversation-status";
  57. NSString * const kCapabilityChatKeepNotifications = @"chat-keep-notifications";
  58. NSString * const kCapabilityConversationAvatars = @"avatar";
  59. NSString * const kCapabilityTypingIndicators = @"typing-privacy";
  60. NSString * const kCapabilityPublishingPermissions = @"publishing-permissions";
  61. NSString * const kCapabilityRemindMeLater = @"remind-me-later";
  62. NSString * const kCapabilityMarkdownMessages = @"markdown-messages";
  63. NSString * const kCapabilityNoteToSelf = @"note-to-self";
  64. NSString * const kCapabilityMediaCaption = @"media-caption";
  65. NSString * const kCapabilityEditMessages = @"edit-messages";
  66. NSString * const kCapabilityDeleteMessagesUnlimited = @"delete-messages-unlimited";
  67. NSString * const kCapabilityFederationV1 = @"federation-v1";
  68. NSString * const kCapabilityFederationV2 = @"federation-v2";
  69. NSString * const kCapabilityChatReadLast = @"chat-read-last";
  70. NSString * const kCapabilityBanV1 = @"ban-v1";
  71. NSString * const kCapabilityMentionPermissions = @"mention-permissions";
  72. NSString * const kCapabilityEditMessagesNoteToSelf = @"edit-messages-note-to-self";
  73. NSString * const kNotificationsCapabilityExists = @"exists";
  74. NSString * const kMinimumRequiredTalkCapability = kCapabilitySystemMessages; // Talk 4.0 is the minimum required version
  75. NSString * const NCDatabaseManagerPendingFederationInvitationsDidChange = @"NCDatabaseManagerPendingFederationInvitationsDidChange";
  76. NSString * const NCDatabaseManagerRoomCapabilitiesChangedNotification = @"NCDatabaseManagerRoomCapabilitiesChangedNotification";
  77. @implementation NCTranslation
  78. @end
  79. @interface NCDatabaseManager ()
  80. @property (nonatomic, strong) NSCache<NSString *, ServerCapabilities*> *capabilitiesCache;
  81. @end
  82. @implementation NCDatabaseManager
  83. + (NCDatabaseManager *)sharedInstance
  84. {
  85. static dispatch_once_t once;
  86. static NCDatabaseManager *sharedInstance;
  87. dispatch_once(&once, ^{
  88. sharedInstance = [[self alloc] init];
  89. });
  90. return sharedInstance;
  91. }
  92. - (id)init
  93. {
  94. self = [super init];
  95. if (self) {
  96. // Create Talk database directory
  97. NSString *path = [[[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupIdentifier] URLByAppendingPathComponent:kTalkDatabaseFolder] path];
  98. if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
  99. [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
  100. }
  101. [[NSFileManager defaultManager] setAttributes:@{NSFileProtectionKey:NSFileProtectionNone} ofItemAtPath:path error:nil];
  102. // Set Realm configuration
  103. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  104. NSURL *databaseURL = [[NSURL fileURLWithPath:path] URLByAppendingPathComponent:kTalkDatabaseFileName];
  105. configuration.fileURL = databaseURL;
  106. configuration.schemaVersion = kTalkDatabaseSchemaVersion;
  107. configuration.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
  108. // At the very minimum we need to update the version with an empty block to indicate that the schema has been upgraded (automatically) by Realm
  109. };
  110. // Tell Realm to use this new configuration object for the default Realm
  111. [RLMRealmConfiguration setDefaultConfiguration:configuration];
  112. // Now that we've told Realm how to handle the schema change, opening the file
  113. // will automatically perform the migration
  114. [RLMRealm defaultRealm];
  115. #ifdef DEBUG
  116. // Copy Talk DB to Documents directory
  117. NSString *dbCopyPath = [NSString stringWithFormat:@"%@/%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0], kTalkDatabaseFileName];
  118. NSURL *dbCopyURL = [NSURL fileURLWithPath:dbCopyPath];
  119. [[NSFileManager defaultManager] removeItemAtURL:dbCopyURL error:nil];
  120. [[NSFileManager defaultManager] copyItemAtURL:databaseURL toURL:dbCopyURL error:nil];
  121. #endif
  122. self.capabilitiesCache = [[NSCache alloc] init];
  123. }
  124. return self;
  125. }
  126. #pragma mark - Talk accounts
  127. - (NSInteger)numberOfAccounts
  128. {
  129. return [TalkAccount allObjects].count;
  130. }
  131. - (TalkAccount *)activeAccount
  132. {
  133. TalkAccount *managedActiveAccount = [TalkAccount objectsWhere:(@"active = true")].firstObject;
  134. if (managedActiveAccount) {
  135. return [[TalkAccount alloc] initWithValue:managedActiveAccount];
  136. }
  137. return nil;
  138. }
  139. - (NSArray *)allAccounts
  140. {
  141. NSMutableArray *allAccounts = [NSMutableArray new];
  142. for (TalkAccount *managedAccount in [TalkAccount allObjects]) {
  143. TalkAccount *account = [[TalkAccount alloc] initWithValue:managedAccount];
  144. [allAccounts addObject:account];
  145. }
  146. return allAccounts;
  147. }
  148. - (NSArray *)inactiveAccounts
  149. {
  150. NSMutableArray *inactiveAccounts = [NSMutableArray new];
  151. for (TalkAccount *managedInactiveAccount in [TalkAccount objectsWhere:(@"active = false")]) {
  152. TalkAccount *inactiveAccount = [[TalkAccount alloc] initWithValue:managedInactiveAccount];
  153. [inactiveAccounts addObject:inactiveAccount];
  154. }
  155. return inactiveAccounts;
  156. }
  157. - (TalkAccount *)talkAccountForAccountId:(NSString *)accountId
  158. {
  159. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  160. TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  161. if (managedAccount) {
  162. return [[TalkAccount alloc] initWithValue:managedAccount];
  163. }
  164. return nil;
  165. }
  166. - (TalkAccount *)talkAccountForUserId:(NSString *)userId inServer:(NSString *)server
  167. {
  168. NSPredicate *query = [NSPredicate predicateWithFormat:@"userId = %@ AND server = %@", userId, server];
  169. TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  170. if (managedAccount) {
  171. return [[TalkAccount alloc] initWithValue:managedAccount];
  172. }
  173. return nil;
  174. }
  175. - (void)setActiveAccountWithAccountId:(NSString *)accountId
  176. {
  177. RLMRealm *realm = [RLMRealm defaultRealm];
  178. [realm beginWriteTransaction];
  179. for (TalkAccount *account in [TalkAccount allObjects]) {
  180. account.active = NO;
  181. }
  182. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  183. TalkAccount *activeAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  184. activeAccount.active = YES;
  185. [realm commitWriteTransaction];
  186. }
  187. - (NSString *)accountIdForUser:(NSString *)user inServer:(NSString *)server
  188. {
  189. return [NSString stringWithFormat:@"%@@%@", user, server];
  190. }
  191. - (void)createAccountForUser:(NSString *)user inServer:(NSString *)server
  192. {
  193. TalkAccount *account = [[TalkAccount alloc] init];
  194. NSString *accountId = [self accountIdForUser:user inServer:server];
  195. account.accountId = accountId;
  196. account.server = server;
  197. account.user = user;
  198. RLMRealm *realm = [RLMRealm defaultRealm];
  199. [realm transactionWithBlock:^{
  200. [realm addObject:account];
  201. }];
  202. }
  203. - (void)removeAccountWithAccountId:(NSString *)accountId
  204. {
  205. RLMRealm *realm = [RLMRealm defaultRealm];
  206. [realm beginWriteTransaction];
  207. BOOL isLastAccount = [self numberOfAccounts] == 1;
  208. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  209. TalkAccount *removeAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  210. if (removeAccount) {
  211. [realm deleteObject:removeAccount];
  212. }
  213. ServerCapabilities *serverCapabilities = [ServerCapabilities objectsWithPredicate:query].firstObject;
  214. if (serverCapabilities) {
  215. [realm deleteObject:serverCapabilities];
  216. [_capabilitiesCache removeObjectForKey:accountId];
  217. }
  218. [realm deleteObjects:[NCRoom objectsWithPredicate:query]];
  219. [realm deleteObjects:[NCChatMessage objectsWithPredicate:query]];
  220. [realm deleteObjects:[NCChatBlock objectsWithPredicate:query]];
  221. [realm deleteObjects:[NCContact objectsWithPredicate:query]];
  222. [realm deleteObjects:[FederatedCapabilities objectsWithPredicate:query]];
  223. if (isLastAccount) {
  224. [realm deleteObjects:[ABContact allObjects]];
  225. }
  226. [realm commitWriteTransaction];
  227. }
  228. - (void)increaseUnreadBadgeNumberForAccountId:(NSString *)accountId
  229. {
  230. RLMRealm *realm = [RLMRealm defaultRealm];
  231. [realm beginWriteTransaction];
  232. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  233. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  234. account.unreadBadgeNumber += 1;
  235. account.unreadNotification = YES;
  236. [realm commitWriteTransaction];
  237. }
  238. - (void)decreaseUnreadBadgeNumberForAccountId:(NSString *)accountId
  239. {
  240. RLMRealm *realm = [RLMRealm defaultRealm];
  241. [realm beginWriteTransaction];
  242. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  243. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  244. account.unreadBadgeNumber = (account.unreadBadgeNumber > 0) ? account.unreadBadgeNumber - 1 : 0;
  245. account.unreadNotification = (account.unreadBadgeNumber > 0) ? account.unreadNotification : NO;
  246. [realm commitWriteTransaction];
  247. }
  248. - (void)resetUnreadBadgeNumberForAccountId:(NSString *)accountId
  249. {
  250. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"resetUnreadBadgeNumberForAccountId" expirationHandler:nil];
  251. RLMRealm *realm = [RLMRealm defaultRealm];
  252. [realm beginWriteTransaction];
  253. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  254. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  255. account.unreadBadgeNumber = 0;
  256. account.unreadNotification = NO;
  257. [realm commitWriteTransaction];
  258. [bgTask stopBackgroundTask];
  259. }
  260. - (NSInteger)numberOfInactiveAccountsWithUnreadNotifications
  261. {
  262. return [TalkAccount objectsWhere:(@"active = false AND unreadNotification = true")].count;
  263. }
  264. - (NSInteger)numberOfUnreadNotifications
  265. {
  266. NSInteger unreadNotifications = 0;
  267. for (TalkAccount *account in [TalkAccount allObjects]) {
  268. unreadNotifications += account.unreadBadgeNumber;
  269. }
  270. return unreadNotifications;
  271. }
  272. - (void)removeUnreadNotificationForInactiveAccounts
  273. {
  274. RLMRealm *realm = [RLMRealm defaultRealm];
  275. [realm beginWriteTransaction];
  276. for (TalkAccount *account in [TalkAccount allObjects]) {
  277. account.unreadNotification = NO;
  278. }
  279. [realm commitWriteTransaction];
  280. }
  281. - (void)updateTalkConfigurationHashForAccountId:(NSString *)accountId withHash:(nonnull NSString *)hash
  282. {
  283. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"updateTalkConfigurationHashForAccountId" expirationHandler:nil];
  284. RLMRealm *realm = [RLMRealm defaultRealm];
  285. [realm beginWriteTransaction];
  286. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  287. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  288. account.lastReceivedConfigurationHash = hash;
  289. [realm commitWriteTransaction];
  290. [bgTask stopBackgroundTask];
  291. }
  292. - (void)updateLastModifiedSinceForAccountId:(NSString *)accountId with:(nonnull NSString *)modifiedSince
  293. {
  294. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"updateLastModifiedSinceForAccountId" expirationHandler:nil];
  295. RLMRealm *realm = [RLMRealm defaultRealm];
  296. [realm beginWriteTransaction];
  297. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  298. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  299. account.lastReceivedModifiedSince = modifiedSince;
  300. [realm commitWriteTransaction];
  301. [bgTask stopBackgroundTask];
  302. }
  303. #pragma mark - Rooms
  304. - (NCRoom *)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
  305. {
  306. NCRoom *unmanagedRoom = nil;
  307. NSPredicate *query = [NSPredicate predicateWithFormat:@"token = %@ AND accountId = %@", token, accountId];
  308. NCRoom *managedRoom = [NCRoom objectsWithPredicate:query].firstObject;
  309. if (managedRoom) {
  310. unmanagedRoom = [[NCRoom alloc] initWithValue:managedRoom];
  311. }
  312. return unmanagedRoom;
  313. }
  314. #pragma mark - Talk capabilities
  315. - (void)setTalkCapabilities:(NSDictionary *)capabilitiesDict onTalkCapabilitiesObject:(TalkCapabilities *)capabilities
  316. {
  317. capabilities.talkCapabilities = [capabilitiesDict objectForKey:@"features"];
  318. capabilities.hasTranslationProviders = [[[[capabilitiesDict objectForKey:@"config"] objectForKey:@"chat"] objectForKey:@"has-translation-providers"] boolValue];
  319. capabilities.attachmentsAllowed = [[[[capabilitiesDict objectForKey:@"config"] objectForKey:@"attachments"] objectForKey:@"allowed"] boolValue];
  320. capabilities.attachmentsFolder = [[[capabilitiesDict objectForKey:@"config"] objectForKey:@"attachments"] objectForKey:@"folder"];
  321. capabilities.talkVersion = [capabilitiesDict objectForKey:@"version"];
  322. NSDictionary *talkConfig = [capabilitiesDict objectForKey:@"config"];
  323. // Call capabilities
  324. NSDictionary *callConfig = [talkConfig objectForKey:@"call"];
  325. NSArray *callConfigKeys = [callConfig allKeys];
  326. if ([callConfigKeys containsObject:@"enabled"]) {
  327. capabilities.callEnabled = [[callConfig objectForKey:@"enabled"] boolValue];
  328. } else {
  329. capabilities.callEnabled = YES;
  330. }
  331. if ([callConfigKeys containsObject:@"recording"]) {
  332. capabilities.recordingEnabled = [[callConfig objectForKey:@"recording"] boolValue];
  333. } else {
  334. capabilities.recordingEnabled = NO;
  335. }
  336. if ([callConfigKeys containsObject:@"supported-reactions"]) {
  337. capabilities.callReactions = [callConfig objectForKey:@"supported-reactions"];
  338. } else {
  339. capabilities.callReactions = (RLMArray<RLMString> *)@[];
  340. }
  341. // Conversation capabilities
  342. NSDictionary *conversationConfig = [talkConfig objectForKey:@"conversation"];
  343. NSArray *conversationConfigKeys = [conversationConfig allKeys];
  344. if ([conversationConfigKeys containsObject:@"can-create"]) {
  345. capabilities.canCreate = [[conversationConfig objectForKey:@"can-create"] boolValue];
  346. } else {
  347. capabilities.canCreate = YES;
  348. }
  349. // Chat capabilities
  350. NSDictionary *chatConfig = [talkConfig objectForKey:@"chat"];
  351. NSArray *chatConfigKeys = [chatConfig allKeys];
  352. capabilities.readStatusPrivacy = [[chatConfig objectForKey:@"read-privacy"] boolValue];
  353. capabilities.chatMaxLength = [[chatConfig objectForKey:@"max-length"] integerValue];
  354. if ([chatConfigKeys containsObject:@"typing-privacy"]) {
  355. capabilities.typingPrivacy = [[chatConfig objectForKey:@"typing-privacy"] boolValue];
  356. } else {
  357. capabilities.typingPrivacy = YES;
  358. }
  359. // Translations
  360. id translations = [[[capabilitiesDict objectForKey:@"config"] objectForKey:@"chat"] objectForKey:@"translations"];
  361. if ([translations isKindOfClass:[NSArray class]]) {
  362. NSError *error;
  363. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:translations
  364. options:0
  365. error:&error];
  366. if (jsonData) {
  367. capabilities.translations = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
  368. } else {
  369. NSLog(@"Error generating translations JSON string: %@", error);
  370. }
  371. }
  372. // Federation capabilities
  373. NSDictionary *federationConfig = [talkConfig objectForKey:@"federation"];
  374. NSArray *federationConfigKeys = [federationConfig allKeys];
  375. if ([federationConfigKeys containsObject:@"enabled"]) {
  376. capabilities.federationEnabled = [[federationConfig objectForKey:@"enabled"] boolValue];
  377. } else {
  378. capabilities.federationEnabled = NO;
  379. }
  380. if ([federationConfigKeys containsObject:@"incoming-enabled"]) {
  381. capabilities.federationIncomingEnabled = [[federationConfig objectForKey:@"incoming-enabled"] boolValue];
  382. } else {
  383. capabilities.federationIncomingEnabled = NO;
  384. }
  385. if ([federationConfigKeys containsObject:@"outgoing-enabled"]) {
  386. capabilities.federationOutgoingEnabled = [[federationConfig objectForKey:@"outgoing-enabled"] boolValue];
  387. } else {
  388. capabilities.federationOutgoingEnabled = NO;
  389. }
  390. if ([federationConfigKeys containsObject:@"only-trusted-servers"]) {
  391. capabilities.federationOnlyTrustedServers = [[federationConfig objectForKey:@"only-trusted-servers"] boolValue];
  392. } else {
  393. capabilities.federationOnlyTrustedServers = NO;
  394. }
  395. NSDictionary *previewConfig = [talkConfig objectForKey:@"previews"];
  396. NSArray *previewConfigKeys = [previewConfig allKeys];
  397. if ([previewConfigKeys containsObject:@"max-gif-size"]) {
  398. capabilities.maxGifSize = [[previewConfig objectForKey:@"max-gif-size"] intValue];
  399. }
  400. }
  401. #pragma mark - Federated capabilities
  402. - (FederatedCapabilities * __nullable)federatedCapabilitiesForAccountId:(NSString *)accountId remoteServer:(NSString *)remoteServer roomToken:(NSString *)roomToken
  403. {
  404. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@ AND remoteServer = %@ AND roomToken = %@", accountId, remoteServer, roomToken];
  405. FederatedCapabilities *managedFederatedCapabilities = [FederatedCapabilities objectsWithPredicate:query].firstObject;
  406. if (managedFederatedCapabilities) {
  407. FederatedCapabilities *unmanagedFederatedCapabilities = [[FederatedCapabilities alloc] initWithValue:managedFederatedCapabilities];
  408. return unmanagedFederatedCapabilities;
  409. }
  410. return nil;
  411. }
  412. - (void)setFederatedCapabilities:(NSDictionary *)federatedCapabilitiesDict forAccountId:(NSString *)accountId remoteServer:(NSString *)remoteServer roomToken:(NSString *)roomToken withProxyHash:(NSString *)proxyHash
  413. {
  414. FederatedCapabilities *federatedCapabilities = [[FederatedCapabilities alloc] init];
  415. federatedCapabilities.internalId = [NSString stringWithFormat:@"%@@%@@%@", accountId, remoteServer, roomToken];
  416. federatedCapabilities.accountId = accountId;
  417. federatedCapabilities.remoteServer = remoteServer;
  418. federatedCapabilities.roomToken = roomToken;
  419. [self setTalkCapabilities:federatedCapabilitiesDict onTalkCapabilitiesObject:federatedCapabilities];
  420. RLMRealm *realm = [RLMRealm defaultRealm];
  421. [realm transactionWithBlock:^{
  422. [realm addOrUpdateObject:federatedCapabilities];
  423. // Update the hash
  424. NSPredicate *query = [NSPredicate predicateWithFormat:@"token = %@ AND accountId = %@", roomToken, accountId];
  425. NCRoom *managedRoom = [NCRoom objectsWithPredicate:query].firstObject;
  426. if (managedRoom) {
  427. managedRoom.lastReceivedProxyHash = proxyHash;
  428. }
  429. NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
  430. [userInfo setObject:accountId forKey:@"accountId"];
  431. [userInfo setObject:roomToken forKey:@"roomToken"];
  432. [[NSNotificationCenter defaultCenter] postNotificationName:NCDatabaseManagerRoomCapabilitiesChangedNotification
  433. object:self
  434. userInfo:userInfo];
  435. }];
  436. }
  437. #pragma mark - Room capabilities
  438. - (BOOL)roomHasTalkCapability:(NSString *)capability forRoom:(NCRoom *)room
  439. {
  440. if (!room.isFederated) {
  441. return [self serverHasTalkCapability:capability forAccountId:room.accountId];
  442. }
  443. FederatedCapabilities *federatedCapabilities = [self federatedCapabilitiesForAccountId:room.accountId remoteServer:room.remoteServer roomToken:room.token];
  444. if (!federatedCapabilities) {
  445. return NO;
  446. }
  447. NSArray *talkFeatures = [federatedCapabilities.talkCapabilities valueForKey:@"self"];
  448. return [talkFeatures containsObject:capability];
  449. }
  450. - (TalkCapabilities * __nullable)roomTalkCapabilitiesForRoom:(NCRoom *)room
  451. {
  452. if (room.isFederated) {
  453. FederatedCapabilities *federatedCapabilities = [self federatedCapabilitiesForAccountId:room.accountId remoteServer:room.remoteServer roomToken:room.token];
  454. if (federatedCapabilities) {
  455. TalkCapabilities *unmanagedTalkCapabilities = [[TalkCapabilities alloc] initWithValue:federatedCapabilities];
  456. return unmanagedTalkCapabilities;
  457. }
  458. return nil;
  459. }
  460. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:room.accountId];
  461. if (serverCapabilities) {
  462. TalkCapabilities *unmanagedTalkCapabilities = [[TalkCapabilities alloc] initWithValue:serverCapabilities];
  463. return unmanagedTalkCapabilities;
  464. }
  465. return nil;
  466. }
  467. #pragma mark - Server capabilities
  468. - (ServerCapabilities *)serverCapabilities {
  469. TalkAccount *activeAccount = [self activeAccount];
  470. return [self serverCapabilitiesForAccountId:activeAccount.accountId];
  471. }
  472. - (ServerCapabilities *)serverCapabilitiesForAccountId:(NSString *)accountId
  473. {
  474. ServerCapabilities *cachedCapabilities = [self.capabilitiesCache objectForKey:accountId];
  475. if (cachedCapabilities) {
  476. return cachedCapabilities;
  477. }
  478. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  479. ServerCapabilities *managedServerCapabilities = [ServerCapabilities objectsWithPredicate:query].firstObject;
  480. if (managedServerCapabilities) {
  481. ServerCapabilities *unmanagedServerCapabilities = [[ServerCapabilities alloc] initWithValue:managedServerCapabilities];
  482. [self.capabilitiesCache setObject:unmanagedServerCapabilities forKey:accountId];
  483. return unmanagedServerCapabilities;
  484. }
  485. return nil;
  486. }
  487. - (void)setServerCapabilities:(NSDictionary *)serverCapabilities forAccountId:(NSString *)accountId
  488. {
  489. NSDictionary *serverCaps = [serverCapabilities objectForKey:@"capabilities"];
  490. NSDictionary *coreCaps = [serverCaps objectForKey:@"core"];
  491. NSDictionary *version = [serverCapabilities objectForKey:@"version"];
  492. NSDictionary *themingCaps = [serverCaps objectForKey:@"theming"];
  493. NSDictionary *talkCaps = [serverCaps objectForKey:@"spreed"];
  494. NSDictionary *userStatusCaps = [serverCaps objectForKey:@"user_status"];
  495. NSDictionary *provisioningAPICaps = [serverCaps objectForKey:@"provisioning_api"];
  496. NSDictionary *guestsCaps = [serverCaps objectForKey:@"guests"];
  497. NSDictionary *notificationsCaps = [serverCaps objectForKey:@"notifications"];
  498. ServerCapabilities *capabilities = [[ServerCapabilities alloc] init];
  499. capabilities.accountId = accountId;
  500. capabilities.name = [themingCaps objectForKey:@"name"];
  501. capabilities.slogan = [themingCaps objectForKey:@"slogan"];
  502. capabilities.url = [themingCaps objectForKey:@"url"];
  503. capabilities.logo = [themingCaps objectForKey:@"logo"];
  504. capabilities.color = [themingCaps objectForKey:@"color"];
  505. capabilities.colorElement = [themingCaps objectForKey:@"color-element"];
  506. capabilities.colorElementBright = [themingCaps objectForKey:@"color-element-bright"];
  507. capabilities.colorElementDark = [themingCaps objectForKey:@"color-element-dark"];
  508. capabilities.colorText = [themingCaps objectForKey:@"color-text"];
  509. capabilities.background = [themingCaps objectForKey:@"background"];
  510. capabilities.backgroundDefault = [[themingCaps objectForKey:@"background-default"] boolValue];
  511. capabilities.backgroundPlain = [[themingCaps objectForKey:@"background-plain"] boolValue];
  512. capabilities.version = [version objectForKey:@"string"];
  513. capabilities.versionMajor = [[version objectForKey:@"major"] integerValue];
  514. capabilities.versionMinor = [[version objectForKey:@"minor"] integerValue];
  515. capabilities.versionMicro = [[version objectForKey:@"micro"] integerValue];
  516. capabilities.edition = [version objectForKey:@"edition"];
  517. capabilities.userStatus = [[userStatusCaps objectForKey:@"enabled"] boolValue];
  518. capabilities.extendedSupport = [[version objectForKey:@"extendedSupport"] boolValue];
  519. capabilities.accountPropertyScopesVersion2 = [[provisioningAPICaps objectForKey:@"AccountPropertyScopesVersion"] integerValue] == 2;
  520. capabilities.accountPropertyScopesFederationEnabled = [[provisioningAPICaps objectForKey:@"AccountPropertyScopesFederationEnabled"] boolValue];
  521. capabilities.accountPropertyScopesFederatedEnabled = [[provisioningAPICaps objectForKey:@"AccountPropertyScopesFederatedEnabled"] boolValue];
  522. capabilities.accountPropertyScopesPublishedEnabled = [[provisioningAPICaps objectForKey:@"AccountPropertyScopesPublishedEnabled"] boolValue];
  523. capabilities.guestsAppEnabled = [[guestsCaps objectForKey:@"enabled"] boolValue];
  524. capabilities.referenceApiSupported = [[coreCaps objectForKey:@"reference-api"] boolValue];
  525. capabilities.modRewriteWorking = [[coreCaps objectForKey:@"mod-rewrite-working"] boolValue];
  526. capabilities.notificationsCapabilities = [notificationsCaps objectForKey:@"ocs-endpoints"];
  527. [self setTalkCapabilities:talkCaps onTalkCapabilitiesObject:capabilities];
  528. RLMRealm *realm = [RLMRealm defaultRealm];
  529. [realm transactionWithBlock:^{
  530. [realm addOrUpdateObject:capabilities];
  531. }];
  532. ServerCapabilities *unmanagedServerCapabilities = [[ServerCapabilities alloc] initWithValue:capabilities];
  533. [self.capabilitiesCache setObject:unmanagedServerCapabilities forKey:accountId];
  534. }
  535. - (BOOL)serverHasTalkCapability:(NSString *)capability
  536. {
  537. TalkAccount *activeAccount = [self activeAccount];
  538. return [self serverHasTalkCapability:capability forAccountId:activeAccount.accountId];
  539. }
  540. - (BOOL)serverHasTalkCapability:(NSString *)capability forAccountId:(NSString *)accountId
  541. {
  542. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:accountId];
  543. if (serverCapabilities) {
  544. NSArray *talkFeatures = [serverCapabilities.talkCapabilities valueForKey:@"self"];
  545. if ([talkFeatures containsObject:capability]) {
  546. return YES;
  547. }
  548. }
  549. return NO;
  550. }
  551. - (BOOL)serverHasNotificationsCapability:(NSString *)capability forAccountId:(NSString *)accountId
  552. {
  553. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:accountId];
  554. if (serverCapabilities) {
  555. NSArray *notificationsFeatures = [serverCapabilities.notificationsCapabilities valueForKey:@"self"];
  556. if ([notificationsFeatures containsObject:capability]) {
  557. return YES;
  558. }
  559. }
  560. return NO;
  561. }
  562. - (BOOL)serverCanInviteFederatedUsersforAccountId:(NSString *)accountId
  563. {
  564. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:accountId];
  565. if (serverCapabilities && [self serverHasTalkCapability:kCapabilityFederationV1 forAccountId:accountId]) {
  566. return serverCapabilities.federationEnabled && serverCapabilities.federationOutgoingEnabled;
  567. }
  568. return NO;
  569. }
  570. - (void)setExternalSignalingServerVersion:(NSString *)version forAccountId:(NSString *)accountId
  571. {
  572. RLMRealm *realm = [RLMRealm defaultRealm];
  573. [realm transactionWithBlock:^{
  574. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  575. ServerCapabilities *managedServerCapabilities = [ServerCapabilities objectsWithPredicate:query].firstObject;
  576. if (managedServerCapabilities && managedServerCapabilities.externalSignalingServerVersion != version) {
  577. managedServerCapabilities.externalSignalingServerVersion = version;
  578. ServerCapabilities *unmanagedServerCapabilities = [[ServerCapabilities alloc] initWithValue:managedServerCapabilities];
  579. [self.capabilitiesCache setObject:unmanagedServerCapabilities forKey:accountId];
  580. }
  581. }];
  582. }
  583. - (BOOL)hasAvailableTranslationsForAccountId:(NSString *)accountId
  584. {
  585. return [self hasTranslationProvidersForAccountId:accountId] || [self availableTranslationsForAccountId:accountId].count > 0;
  586. }
  587. - (BOOL)hasTranslationProvidersForAccountId:(NSString *)accountId
  588. {
  589. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:accountId];
  590. return serverCapabilities.hasTranslationProviders;
  591. }
  592. - (NSArray *)availableTranslationsForAccountId:(NSString *)accountId
  593. {
  594. ServerCapabilities *serverCapabilities = [self serverCapabilitiesForAccountId:accountId];
  595. if (serverCapabilities) {
  596. NSArray *translations = [self translationsArrayFromTranslationsJSONString:serverCapabilities.translations];
  597. return [self translationsFromTranslationsArray:translations];
  598. }
  599. return @[];
  600. }
  601. - (NSArray *)translationsArrayFromTranslationsJSONString:(NSString *)translations
  602. {
  603. NSArray *translationsArray = @[];
  604. NSData *data = [translations dataUsingEncoding:NSUTF8StringEncoding];
  605. if (data) {
  606. NSError* error;
  607. NSArray* jsonData = [NSJSONSerialization JSONObjectWithData:data
  608. options:0
  609. error:&error];
  610. if (jsonData) {
  611. translationsArray = jsonData;
  612. } else {
  613. NSLog(@"Error retrieving translations JSON data: %@", error);
  614. }
  615. }
  616. return translationsArray;
  617. }
  618. - (NSArray *)translationsFromTranslationsArray:(NSArray *)translations
  619. {
  620. NSMutableArray *availableTranslations = [NSMutableArray new];
  621. for (NSDictionary *translationDict in translations) {
  622. NCTranslation *translation = [[NCTranslation alloc] init];
  623. translation.from = [translationDict objectForKey:@"from"];
  624. translation.fromLabel = [translationDict objectForKey:@"fromLabel"];
  625. translation.to = [translationDict objectForKey:@"to"];
  626. translation.toLabel = [translationDict objectForKey:@"toLabel"];
  627. [availableTranslations addObject:translation];
  628. }
  629. return availableTranslations;
  630. }
  631. - (void)increasePendingFederationInvitationForAccountId:(NSString *)accountId
  632. {
  633. RLMRealm *realm = [RLMRealm defaultRealm];
  634. [realm beginWriteTransaction];
  635. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  636. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  637. account.pendingFederationInvitations += 1;
  638. [realm commitWriteTransaction];
  639. }
  640. - (void)decreasePendingFederationInvitationForAccountId:(NSString *)accountId
  641. {
  642. RLMRealm *realm = [RLMRealm defaultRealm];
  643. [realm beginWriteTransaction];
  644. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  645. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  646. account.pendingFederationInvitations = (account.pendingFederationInvitations > 0) ? account.pendingFederationInvitations - 1 : 0;
  647. [realm commitWriteTransaction];
  648. [[NSNotificationCenter defaultCenter] postNotificationName:NCDatabaseManagerPendingFederationInvitationsDidChange
  649. object:self
  650. userInfo:nil];
  651. }
  652. - (void)setPendingFederationInvitationForAccountId:(NSString *)accountId with:(NSInteger)numberOfPendingInvitations
  653. {
  654. RLMRealm *realm = [RLMRealm defaultRealm];
  655. [realm beginWriteTransaction];
  656. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  657. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  658. account.pendingFederationInvitations = numberOfPendingInvitations;
  659. [realm commitWriteTransaction];
  660. [[NSNotificationCenter defaultCenter] postNotificationName:NCDatabaseManagerPendingFederationInvitationsDidChange
  661. object:self
  662. userInfo:nil];
  663. }
  664. - (void)updateLastFederationInvitationUpdateForAccountId:(NSString *)accountId withTimestamp:(NSInteger)timestamp
  665. {
  666. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"updateLastFederationInvitationUpdateForAccountId" expirationHandler:nil];
  667. RLMRealm *realm = [RLMRealm defaultRealm];
  668. [realm beginWriteTransaction];
  669. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
  670. TalkAccount *account = [TalkAccount objectsWithPredicate:query].firstObject;
  671. account.lastPendingFederationInvitationFetch = timestamp;
  672. [realm commitWriteTransaction];
  673. [bgTask stopBackgroundTask];
  674. }
  675. @end