NCPushNotification.m 11 KB

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