NCPushNotification.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 "AppDelegate.h"
  24. #import "NCBridgeSwift.h"
  25. #import "NCPushNotification.h"
  26. #import "NCPushNotificationEncryption.h"
  27. @interface NCPushNotification ()
  28. {
  29. AppDelegate *appDelegate;
  30. }
  31. @end
  32. @implementation NCPushNotification
  33. + (instancetype)shared {
  34. static NCPushNotification *pushNotification = nil;
  35. static dispatch_once_t onceToken;
  36. dispatch_once(&onceToken, ^{
  37. pushNotification = [self new];
  38. pushNotification->appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  39. });
  40. return pushNotification;
  41. }
  42. - (void)pushNotification
  43. {
  44. if (appDelegate.account.length == 0 || self.pushKitToken.length == 0) { return; }
  45. for (tableAccount *result in [[NCManageDatabase shared] getAllAccount]) {
  46. NSString *token = [CCUtility getPushNotificationToken:result.account];
  47. if (![token isEqualToString:self.pushKitToken]) {
  48. if (token != nil) {
  49. // unsubscribing + subscribing
  50. [self unsubscribingNextcloudServerPushNotification:result.account urlBase:result.urlBase user:result.user withSubscribing:true];
  51. } else {
  52. [self subscribingNextcloudServerPushNotification:result.account urlBase:result.urlBase user:result.user];
  53. }
  54. }
  55. }
  56. }
  57. - (void)applicationdidReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  58. NSString *message = [userInfo objectForKey:@"subject"];
  59. if (message) {
  60. NSArray *results = [[NCManageDatabase shared] getAllAccount];
  61. for (tableAccount *result in results) {
  62. if ([CCUtility getPushNotificationPrivateKey:result.account]) {
  63. NSData *decryptionKey = [CCUtility getPushNotificationPrivateKey:result.account];
  64. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:decryptionKey];
  65. if (decryptedMessage) {
  66. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  67. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  68. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  69. BOOL delete = [[json objectForKey:@"delete"] boolValue];
  70. BOOL deleteAll = [[json objectForKey:@"delete-all"] boolValue];
  71. if (delete) {
  72. [[NCPushNotification shared] removeNotificationWithNotificationId:nid usingDecryptionKey:decryptionKey];
  73. } else if (deleteAll) {
  74. [[NCPushNotification shared] cleanAllNotifications];
  75. }
  76. }
  77. }
  78. }
  79. }
  80. completionHandler(UIBackgroundFetchResultNoData);
  81. }
  82. - (void)subscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user
  83. {
  84. if (appDelegate.account.length == 0 || self.pushKitToken.length == 0) { return; }
  85. [[NCPushNotificationEncryption shared] generatePushNotificationsKeyPair:account];
  86. NSString *pushTokenHash = [[NCEndToEndEncryption sharedManager] createSHA512:self.pushKitToken];
  87. NSData *pushPublicKey = [CCUtility getPushNotificationPublicKey:account];
  88. NSString *pushDevicePublicKey = [[NSString alloc] initWithData:pushPublicKey encoding:NSUTF8StringEncoding];
  89. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  90. [[NCCommunication shared] subscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] pushTokenHash:pushTokenHash devicePublicKey:pushDevicePublicKey proxyServerUrl:proxyServerPath customUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSString *deviceIdentifier, NSString *signature, NSString *publicKey, NSInteger errorCode, NSString *errorDescription) {
  91. if (errorCode == 0) {
  92. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [CCUtility getUserAgent]];
  93. [[NCCommunication shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
  94. if (errorCode == 0) {
  95. [[NCCommunicationCommon shared] writeLog:@"Subscribed to Push Notification server & proxy successfully"];
  96. [CCUtility setPushNotificationToken:account token:self.pushKitToken];
  97. [CCUtility setPushNotificationDeviceIdentifier:account deviceIdentifier:deviceIdentifier];
  98. [CCUtility setPushNotificationDeviceIdentifierSignature:account deviceIdentifierSignature:signature];
  99. [CCUtility setPushNotificationSubscribingPublicKey:account publicKey:publicKey];
  100. }
  101. }];
  102. }
  103. }];
  104. }
  105. - (void)unsubscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user withSubscribing:(BOOL)subscribing
  106. {
  107. if (appDelegate.account.length == 0) { return; }
  108. NSString *deviceIdentifier = [CCUtility getPushNotificationDeviceIdentifier:account];
  109. NSString *signature = [CCUtility getPushNotificationDeviceIdentifierSignature:account];
  110. NSString *publicKey = [CCUtility getPushNotificationSubscribingPublicKey:account];
  111. [[NCCommunication shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] customUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
  112. if (errorCode == 0) {
  113. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [CCUtility getUserAgent]];
  114. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  115. [[NCCommunication shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
  116. if (errorCode == 0) {
  117. [[NCCommunicationCommon shared] writeLog:@"Unsubscribed to Push Notification server & proxy successfully."];
  118. [CCUtility setPushNotificationPublicKey:account data:nil];
  119. [CCUtility setPushNotificationSubscribingPublicKey:account publicKey:nil];
  120. [CCUtility setPushNotificationPrivateKey:account data:nil];
  121. [CCUtility setPushNotificationToken:account token:nil];
  122. [CCUtility setPushNotificationDeviceIdentifier:account deviceIdentifier:nil];
  123. [CCUtility setPushNotificationDeviceIdentifierSignature:account deviceIdentifierSignature:nil];
  124. if (self.pushKitToken != nil && subscribing) {
  125. [self subscribingNextcloudServerPushNotification:account urlBase:urlBase user:user];
  126. }
  127. }
  128. }];
  129. }
  130. }];
  131. }
  132. - (void)cleanAllNotifications
  133. {
  134. [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
  135. }
  136. - (void)removeNotificationWithNotificationId:(NSInteger)notificationId usingDecryptionKey:(NSData *)key
  137. {
  138. // Check in pending notifications
  139. [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  140. for (UNNotificationRequest *notificationRequest in requests) {
  141. NSString *message = [notificationRequest.content.userInfo objectForKey:@"subject"];
  142. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  143. if (decryptedMessage) {
  144. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  145. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  146. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  147. if (nid == notificationId) {
  148. [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  149. }
  150. }
  151. }
  152. }];
  153. // Check in delivered notifications
  154. [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  155. for (UNNotification *notification in notifications) {
  156. NSString *message = [notification.request.content.userInfo objectForKey:@"subject"];
  157. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  158. if (decryptedMessage) {
  159. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  160. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  161. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  162. if (nid == notificationId) {
  163. [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  164. }
  165. }
  166. }
  167. }];
  168. }
  169. - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
  170. {
  171. self.pushKitToken = [self stringWithDeviceToken:deviceToken];
  172. [self pushNotification];
  173. }
  174. - (NSString *)stringWithDeviceToken:(NSData *)deviceToken
  175. {
  176. const char *data = [deviceToken bytes];
  177. NSMutableString *token = [NSMutableString string];
  178. for (NSUInteger i = 0; i < [deviceToken length]; i++) {
  179. [token appendFormat:@"%02.2hhX", data[i]];
  180. }
  181. return [token copy];
  182. }
  183. @end