NCPushNotification.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 = [CCUtility getPushNotificationToken: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 ([CCUtility getPushNotificationPrivateKey:result.account]) {
  66. NSData *decryptionKey = [CCUtility getPushNotificationPrivateKey: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 = [CCUtility getPushNotificationPublicKey:account];
  91. NSString *pushDevicePublicKey = [[NSString alloc] initWithData:pushPublicKey encoding:NSUTF8StringEncoding];
  92. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  93. [[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) {
  94. if (errorCode == 0) {
  95. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [CCUtility getUserAgent]];
  96. [[NCCommunication shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
  97. if (errorCode == 0) {
  98. [[NCCommunicationCommon shared] writeLog:@"Subscribed to Push Notification server & proxy successfully"];
  99. [CCUtility setPushNotificationToken:account token:self.pushKitToken];
  100. [CCUtility setPushNotificationDeviceIdentifier:account deviceIdentifier:deviceIdentifier];
  101. [CCUtility setPushNotificationDeviceIdentifierSignature:account deviceIdentifierSignature:signature];
  102. [CCUtility setPushNotificationSubscribingPublicKey:account publicKey:publicKey];
  103. }
  104. }];
  105. }
  106. }];
  107. }
  108. - (void)unsubscribingNextcloudServerPushNotification:(NSString *)account urlBase:(NSString *)urlBase user:(NSString *)user withSubscribing:(BOOL)subscribing
  109. {
  110. if (appDelegate.account == nil || appDelegate.account.length == 0) { return; }
  111. NSString *deviceIdentifier = [CCUtility getPushNotificationDeviceIdentifier:account];
  112. NSString *signature = [CCUtility getPushNotificationDeviceIdentifierSignature:account];
  113. NSString *publicKey = [CCUtility getPushNotificationSubscribingPublicKey:account];
  114. [[NCCommunication shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] customUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
  115. if (errorCode == 0) {
  116. NSString *userAgent = [NSString stringWithFormat:@"%@ (Strict VoIP)", [CCUtility getUserAgent]];
  117. NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
  118. [[NCCommunication shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
  119. if (errorCode == 0) {
  120. [[NCCommunicationCommon shared] writeLog:@"Unsubscribed to Push Notification server & proxy successfully."];
  121. [CCUtility setPushNotificationPublicKey:account data:nil];
  122. [CCUtility setPushNotificationSubscribingPublicKey:account publicKey:nil];
  123. [CCUtility setPushNotificationPrivateKey:account data:nil];
  124. [CCUtility setPushNotificationToken:account token:nil];
  125. [CCUtility setPushNotificationDeviceIdentifier:account deviceIdentifier:nil];
  126. [CCUtility setPushNotificationDeviceIdentifierSignature:account deviceIdentifierSignature:nil];
  127. if (self.pushKitToken != nil && subscribing) {
  128. [self subscribingNextcloudServerPushNotification:account urlBase:urlBase user:user];
  129. }
  130. }
  131. }];
  132. }
  133. }];
  134. }
  135. - (void)cleanAllNotifications
  136. {
  137. [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
  138. }
  139. - (void)removeNotificationWithNotificationId:(NSInteger)notificationId usingDecryptionKey:(NSData *)key
  140. {
  141. // Check in pending notifications
  142. [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  143. for (UNNotificationRequest *notificationRequest in requests) {
  144. NSString *message = [notificationRequest.content.userInfo objectForKey:@"subject"];
  145. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  146. if (decryptedMessage) {
  147. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  148. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  149. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  150. if (nid == notificationId) {
  151. [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  152. }
  153. }
  154. }
  155. }];
  156. // Check in delivered notifications
  157. [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  158. for (UNNotification *notification in notifications) {
  159. NSString *message = [notification.request.content.userInfo objectForKey:@"subject"];
  160. NSString *decryptedMessage = [[NCPushNotificationEncryption shared] decryptPushNotification:message withDevicePrivateKey:key];
  161. if (decryptedMessage) {
  162. NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding];
  163. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  164. NSInteger nid = [[json objectForKey:@"nid"] integerValue];
  165. if (nid == notificationId) {
  166. [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  167. }
  168. }
  169. }
  170. }];
  171. }
  172. - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
  173. {
  174. self.pushKitToken = [self stringWithDeviceToken:deviceToken];
  175. [self pushNotification];
  176. }
  177. - (NSString *)stringWithDeviceToken:(NSData *)deviceToken
  178. {
  179. const char *data = [deviceToken bytes];
  180. NSMutableString *token = [NSMutableString string];
  181. for (NSUInteger i = 0; i < [deviceToken length]; i++) {
  182. [token appendFormat:@"%02.2hhX", data[i]];
  183. }
  184. return [token copy];
  185. }
  186. @end