diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm --- a/native/ios/NotificationService/NotificationService.mm +++ b/native/ios/NotificationService/NotificationService.mm @@ -1,6 +1,7 @@ #import "NotificationService.h" #import "Logger.h" #import "NotificationsCryptoModule.h" +#import "StaffUtils.h" #import "TemporaryMessageStorage.h" NSString *const backgroundNotificationTypeKey = @"backgroundNotifType"; @@ -31,15 +32,31 @@ self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; + UNNotificationContent *publicUserContent = self.bestAttemptContent; + + // Step 1: notification decryption. if ([self shouldBeDecrypted:self.bestAttemptContent.userInfo]) { - @try { - [self decryptBestAttemptContent]; - } @catch (NSException *e) { - comm::Logger::log( - "NSE: Received exception: " + std::string([e.name UTF8String]) + - " with reason: " + std::string([e.reason UTF8String]) + - " during notification decryption"); - self.contentHandler([[UNNotificationContent alloc] init]); + std::string decryptErrorMessage; + try { + @try { + [self decryptBestAttemptContent]; + } @catch (NSException *e) { + decryptErrorMessage = "NSE: Received Obj-C exception: " + + std::string([e.name UTF8String]) + + " during notification decryption."; + } + } catch (const std::exception &e) { + decryptErrorMessage = + "NSE: Received C++ exception: " + std::string(e.what()) + + " during notification decryption."; + } + + if (decryptErrorMessage.size()) { + NSString *errorMessage = + [NSString stringWithUTF8String:decryptErrorMessage.c_str()]; + [self callContentHandlerOnErrorMessage:errorMessage + withPublicUserContent:[[UNNotificationContent alloc] + init]]; return; } } else if ([self shouldAlertUnencryptedNotification:self.bestAttemptContent @@ -49,26 +66,75 @@ comm::Logger::log("NSE: Received erroneously unencrypted notitication."); } - [self persistMessagePayload:self.bestAttemptContent.userInfo]; + NSMutableArray *errorMessages = [[NSMutableArray alloc] init]; + + // Step 2: notification persistence in a temporary storage + std::string persistErrorMessage; + try { + @try { + [self persistMessagePayload:self.bestAttemptContent.userInfo]; + } @catch (NSException *e) { + persistErrorMessage = + "Obj-C exception: " + std::string([e.name UTF8String]) + + " during notification persistence."; + } + } catch (const std::exception &e) { + persistErrorMessage = "C++ exception: " + std::string(e.what()) + + " during notification persistence."; + } + + if (persistErrorMessage.size()) { + [errorMessages + addObject:[NSString stringWithUTF8String:persistErrorMessage.c_str()]]; + } + + // Step 3: (optional) rescind read notifications + // Message payload persistence is a higher priority task, so it has // to happen prior to potential notification center clearing. if ([self isRescind:self.bestAttemptContent.userInfo]) { - [self removeNotificationWithIdentifier:self.bestAttemptContent - .userInfo[@"notificationId"]]; - self.contentHandler([[UNNotificationContent alloc] init]); - return; + std::string rescindErrorMessage; + try { + @try { + [self + removeNotificationWithIdentifier:self.bestAttemptContent + .userInfo[@"notificationId"]]; + } @catch (NSException *e) { + rescindErrorMessage = + "Obj-C exception: " + std::string([e.name UTF8String]) + + " during notification rescind."; + } + } catch (const std::exception &e) { + rescindErrorMessage = "C++ exception: " + std::string(e.what()) + + " during notification rescind."; + } + + if (rescindErrorMessage.size()) { + [errorMessages + addObject:[NSString + stringWithUTF8String:persistErrorMessage.c_str()]]; + } + + publicUserContent = [[UNNotificationContent alloc] init]; } if ([self isBadgeOnly:self.bestAttemptContent.userInfo]) { UNMutableNotificationContent *badgeOnlyContent = [[UNMutableNotificationContent alloc] init]; badgeOnlyContent.badge = self.bestAttemptContent.badge; - self.contentHandler(badgeOnlyContent); - return; + self.bestAttemptContent = badgeOnlyContent; + publicUserContent = badgeOnlyContent; } [self sendNewMessageInfosNotification]; // TODO modify self.bestAttemptContent here + if (errorMessages.count) { + NSString *cumulatedErrorMessage = [@"NSE: Received " + stringByAppendingString:[errorMessages componentsJoinedByString:@" "]]; + [self callContentHandlerOnErrorMessage:cumulatedErrorMessage + withPublicUserContent:publicUserContent]; + return; + } self.contentHandler(self.bestAttemptContent); } @@ -82,8 +148,11 @@ // remove relevant notification from notification center in // in time given to NSE to process notification. // It is an extremely unlikely to happen. - comm::Logger::log("NSE: Exceeded time limit to rescind a notification."); - self.contentHandler([[UNNotificationContent alloc] init]); + NSString *errorMessage = + @"NSE: Exceeded time limit to rescind a notification."; + [self + callContentHandlerOnErrorMessage:errorMessage + withPublicUserContent:[[UNNotificationContent alloc] init]]; return; } if ([self shouldBeDecrypted:self.bestAttemptContent.userInfo] && @@ -91,8 +160,11 @@ // If we get to this place it means we were unable to // decrypt encrypted notification content in time // given to NSE to process notification. - comm::Logger::log("NSE: Exceeded time limit to decrypt a notification."); - self.contentHandler([[UNNotificationContent alloc] init]); + NSString *errorMessage = + @"NSE: Exceeded time limit to decrypt a notification."; + [self + callContentHandlerOnErrorMessage:errorMessage + withPublicUserContent:[[UNNotificationContent alloc] init]]; return; } self.contentHandler(self.bestAttemptContent); @@ -260,4 +332,25 @@ self.bestAttemptContent.userInfo = mutableUserInfo; } +- (UNNotificationContent *)buildContentForError:(NSString *)error { + UNMutableNotificationContent *content = + [[UNMutableNotificationContent alloc] init]; + content.body = error; + return content; +} + +- (void)callContentHandlerOnErrorMessage:(NSString *)errorMessage + withPublicUserContent: + (UNNotificationContent *)publicUserContent { + comm::Logger::log(std::string([errorMessage UTF8String])); + + if (!comm::StaffUtils::isStaffRelease()) { + self.contentHandler(publicUserContent); + return; + } + + UNNotificationContent *content = [self buildContentForError:errorMessage]; + self.contentHandler(content); +} + @end