NCPushNotification.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. //
  2. // NCPushNotification.m
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 26/12/20.
  6. // Copyright © 2020 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. #import <UserNotifications/UserNotifications.h>
  24. #import "NCBridgeSwift.h"
  25. #import "NCPushNotification.h"
  26. #import "NCPushNotificationEncryption.h"
  27. #import "NCEndToEndEncryption.h"
  28. #import "CCUtility.h"
  29. @interface NCPushNotification ()
  30. {
  31. AppDelegate *appDelegate;
  32. }
  33. @end
  34. @implementation NCPushNotification
  35. + (instancetype)shared
  36. {
  37. static NCPushNotification *pushNotification = nil;
  38. static dispatch_once_t onceToken;
  39. dispatch_once(&onceToken, ^{
  40. pushNotification = [self new];
  41. pushNotification->appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  42. });
  43. return pushNotification;
  44. }
  45. - (void)pushNotification
  46. {
  47. if (self.pushKitToken.length == 0) { return; }
  48. for (tableAccount *result in [[NCManageDatabase shared] getAllAccount]) {
  49. NSString *token = [[[NCKeychain alloc] init] getPushNotificationTokenWithAccount:result.account];
  50. if (![token isEqualToString:self.pushKitToken]) {
  51. if (token != nil) {
  52. // unsubscribing + subscribing
  53. [self unsubscribingNextcloudServerPushNotification:result.account urlBase:result.urlBase user:result.user withSubscribing:true];
  54. } else {
  55. [self subscribingNextcloudServerPushNotification:result.account urlBase:result.urlBase user:result.user];
  56. }
  57. }
  58. }
  59. }
  60. - (void)applicationdidReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  61. NSString *message = [userInfo objectForKey:@"subject"];
  62. if (message) {
  63. NSArray *results = [[NCManageDatabase shared] getAllAccount];
  64. for (tableAccount *result in results) {
  65. if ([[[NCKeychain alloc] init] getPushNotificationPrivateKeyWithAccount:result.account]) {
  66. NSData *decryptionKey = [[[NCKeychain alloc] init] getPushNotificationPrivateKeyWithAccount:result.account];
  67. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:decryptionKey];
  68. if (decryptedMessage) {
  69. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  70. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  71. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  72. BOOL delete = [[json objectForKey:@"delete"] boolValue];
  73. BOOL deleteAll = [[json objectForKey:@"delete-all"] boolValue];
  74. if (delete) {
  75. [[NCPushNotification shared] removeNotificationWithNotificationId:nid usingDecryptionKey:decryptionKey];
  76. } else if (deleteAll) {
  77. [[NCPushNotification shared] cleanAllNotifications];
  78. }
  79. }
  80. }
  81. }
  82. }
  83. completionHandler(UIBackgroundFetchResultNoData);
  84. }
  85. - (void)subscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user
  86. {
  87. if (appDelegate.account == nil || appDelegate.account.length == 0 || self.pushKitToken.length == 0) { return; }
  88. [[NCPushNotificationEncryption shared] generatePushNotificationsKeyPair:account];
  89. NSString *pushTokenHash = [[NCEndToEndEncryption sharedManager] createSHA512:self.pushKitToken];
  90. NSData *pushPublicKey = [[[NCKeychain alloc] init] getPushNotificationPublicKeyWithAccount:account];
  91. NSString *pushDevicePublicKey = [[NSString alloc] initWithData:pushPublicKey encoding:NSUTF8StringEncoding];
  92. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  93. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  94. [[NextcloudKit shared] subscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[[[NCKeychain alloc] init] getPasswordWithAccount:account] pushTokenHash:pushTokenHash devicePublicKey:pushDevicePublicKey proxyServerUrl:proxyServerPath options:options completion:^(NSString *account, NSString *deviceIdentifier, NSString *signature, NSString *publicKey, NSData *data, NKError *error) {
  95. if (error == NKError.success) {
  96. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [[NCBrandOptions shared] getUserAgent]];
  97. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:userAgent contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  98. [[NextcloudKit shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey options:options completion:^(NKError *error) {
  99. if (error == NKError.success) {
  100. [[[NextcloudKit shared] nkCommonInstance] writeLog:@"[INFO] Subscribed to Push Notification server & proxy successfully"];
  101. [[[NCKeychain alloc] init] setPushNotificationTokenWithAccount:account token:self.pushKitToken];
  102. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierWithAccount:account deviceIdentifier:deviceIdentifier];
  103. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierSignatureWithAccount:account deviceIdentifierSignature:signature];
  104. [[[NCKeychain alloc] init] setPushNotificationSubscribingPublicKeyWithAccount:account publicKey:publicKey];
  105. }
  106. }];
  107. }
  108. }];
  109. }
  110. - (void)unsubscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user withSubscribing:(BOOL)subscribing
  111. {
  112. if (appDelegate.account == nil || appDelegate.account.length == 0) { return; }
  113. NSString *deviceIdentifier = [[[NCKeychain alloc] init] getPushNotificationDeviceIdentifierWithAccount:account];
  114. NSString *signature = [[[NCKeychain alloc] init] getPushNotificationDeviceIdentifierSignatureWithAccount:account];
  115. NSString *publicKey = [[[NCKeychain alloc] init] getPushNotificationSubscribingPublicKeyWithAccount:account];
  116. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  117. [[NextcloudKit shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[[[NCKeychain alloc] init] getPasswordWithAccount:account] options:options completion:^(NSString *account, NKError *error) {
  118. if (error == NKError.success) {
  119. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  120. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [[NCBrandOptions shared] getUserAgent]];
  121. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:userAgent contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  122. [[NextcloudKit shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey options:options completion:^(NKError *error) {
  123. if (error == NKError.success) {
  124. [[[NextcloudKit shared] nkCommonInstance] writeLog:@"[INFO] Unsubscribed to Push Notification server & proxy successfully."];
  125. [[[NCKeychain alloc] init] setPushNotificationPublicKeyWithAccount:account data:nil];
  126. [[[NCKeychain alloc] init] setPushNotificationSubscribingPublicKeyWithAccount:account publicKey:nil];
  127. [[[NCKeychain alloc] init] setPushNotificationPrivateKeyWithAccount:account data:nil];
  128. [[[NCKeychain alloc] init] setPushNotificationTokenWithAccount:account token:nil];
  129. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierWithAccount:account deviceIdentifier:nil];
  130. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierSignatureWithAccount:account deviceIdentifierSignature:nil];
  131. if (self.pushKitToken != nil && subscribing) {
  132. [self subscribingNextcloudServerPushNotification:account urlBase:urlBase user:user];
  133. }
  134. }
  135. }];
  136. }
  137. }];
  138. }
  139. - (void)cleanAllNotifications
  140. {
  141. [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
  142. }
  143. - (void)removeNotificationWithNotificationId:(NSInteger)notificationId usingDecryptionKey:(NSData *)key
  144. {
  145. // Check in pending notifications
  146. [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  147. for (UNNotificationRequest *notificationRequest in requests) {
  148. NSString *message = [notificationRequest.content.userInfo objectForKey:@"subject"];
  149. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  150. if (decryptedMessage) {
  151. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  152. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  153. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  154. if (nid == notificationId) {
  155. [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  156. }
  157. }
  158. }
  159. }];
  160. // Check in delivered notifications
  161. [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  162. for (UNNotification *notification in notifications) {
  163. NSString *message = [notification.request.content.userInfo objectForKey:@"subject"];
  164. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  165. if (decryptedMessage) {
  166. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  167. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  168. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  169. if (nid == notificationId) {
  170. [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  171. }
  172. }
  173. }
  174. }];
  175. }
  176. - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
  177. {
  178. self.pushKitToken = [self stringWithDeviceToken:deviceToken];
  179. [self pushNotification];
  180. }
  181. - (NSString *)stringWithDeviceToken:(NSData *)deviceToken
  182. {
  183. const char *data = [deviceToken bytes];
  184. NSMutableString *token = [NSMutableString string];
  185. for (NSUInteger i = 0; i < [deviceToken length]; i++) {
  186. [token appendFormat:@"%02.2hhX", data[i]];
  187. }
  188. return [token copy];
  189. }
  190. @end