NCPushNotification.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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 version: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 taskHandler:^(NSURLSessionTask *task) {
  95. } completion:^(NSString *account, NSString *deviceIdentifier, NSString *signature, NSString *publicKey, NSData *data, NKError *error) {
  96. if (error == NKError.success) {
  97. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [[NCBrandOptions shared] getUserAgent]];
  98. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil version:nil customHeader:nil customUserAgent:userAgent contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  99. [[NextcloudKit shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey options:options taskHandler:^(NSURLSessionTask *task) {
  100. } completion:^(NKError *error) {
  101. if (error == NKError.success) {
  102. [[[NextcloudKit shared] nkCommonInstance] writeLog:@"[INFO] Subscribed to Push Notification server & proxy successfully"];
  103. [[[NCKeychain alloc] init] setPushNotificationTokenWithAccount:account token:self.pushKitToken];
  104. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierWithAccount:account deviceIdentifier:deviceIdentifier];
  105. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierSignatureWithAccount:account deviceIdentifierSignature:signature];
  106. [[[NCKeychain alloc] init] setPushNotificationSubscribingPublicKeyWithAccount:account publicKey:publicKey];
  107. }
  108. }];
  109. }
  110. }];
  111. }
  112. - (void)unsubscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user withSubscribing:(BOOL)subscribing
  113. {
  114. if (appDelegate.account == nil || appDelegate.account.length == 0) { return; }
  115. NSString *deviceIdentifier = [[[NCKeychain alloc] init] getPushNotificationDeviceIdentifierWithAccount:account];
  116. NSString *signature = [[[NCKeychain alloc] init] getPushNotificationDeviceIdentifierSignatureWithAccount:account];
  117. NSString *publicKey = [[[NCKeychain alloc] init] getPushNotificationSubscribingPublicKeyWithAccount:account];
  118. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil version:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  119. [[NextcloudKit shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[[[NCKeychain alloc] init] getPasswordWithAccount:account] options:options taskHandler:^(NSURLSessionTask *task) {
  120. } completion:^(NSString *account, NKError *error) {
  121. if (error == NKError.success) {
  122. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  123. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [[NCBrandOptions shared] getUserAgent]];
  124. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil version:nil customHeader:nil customUserAgent:userAgent contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  125. [[NextcloudKit shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey options:options taskHandler:^(NSURLSessionTask *task) {
  126. } completion:^(NKError *error) {
  127. if (error == NKError.success) {
  128. [[[NextcloudKit shared] nkCommonInstance] writeLog:@"[INFO] Unsubscribed to Push Notification server & proxy successfully."];
  129. [[[NCKeychain alloc] init] setPushNotificationPublicKeyWithAccount:account data:nil];
  130. [[[NCKeychain alloc] init] setPushNotificationSubscribingPublicKeyWithAccount:account publicKey:nil];
  131. [[[NCKeychain alloc] init] setPushNotificationPrivateKeyWithAccount:account data:nil];
  132. [[[NCKeychain alloc] init] setPushNotificationTokenWithAccount:account token:nil];
  133. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierWithAccount:account deviceIdentifier:nil];
  134. [[[NCKeychain alloc] init] setPushNotificationDeviceIdentifierSignatureWithAccount:account deviceIdentifierSignature:nil];
  135. if (self.pushKitToken != nil && subscribing) {
  136. [self subscribingNextcloudServerPushNotification:account urlBase:urlBase user:user];
  137. }
  138. }
  139. }];
  140. }
  141. }];
  142. }
  143. - (void)cleanAllNotifications
  144. {
  145. [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
  146. }
  147. - (void)removeNotificationWithNotificationId:(NSInteger)notificationId usingDecryptionKey:(NSData *)key
  148. {
  149. // Check in pending notifications
  150. [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  151. for (UNNotificationRequest *notificationRequest in requests) {
  152. NSString *message = [notificationRequest.content.userInfo objectForKey:@"subject"];
  153. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  154. if (decryptedMessage) {
  155. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  156. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  157. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  158. if (nid == notificationId) {
  159. [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  160. }
  161. }
  162. }
  163. }];
  164. // Check in delivered notifications
  165. [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  166. for (UNNotification *notification in notifications) {
  167. NSString *message = [notification.request.content.userInfo objectForKey:@"subject"];
  168. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  169. if (decryptedMessage) {
  170. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  171. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  172. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  173. if (nid == notificationId) {
  174. [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  175. }
  176. }
  177. }
  178. }];
  179. }
  180. - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
  181. {
  182. self.pushKitToken = [self stringWithDeviceToken:deviceToken];
  183. [self pushNotification];
  184. }
  185. - (NSString *)stringWithDeviceToken:(NSData *)deviceToken
  186. {
  187. const char *data = [deviceToken bytes];
  188. NSMutableString *token = [NSMutableString string];
  189. for (NSUInteger i = 0; i < [deviceToken length]; i++) {
  190. [token appendFormat:@"%02.2hhX", data[i]];
  191. }
  192. return [token copy];
  193. }
  194. @end