From c5981b164bc4391a3aa6f2528b9601678928f210 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 17 Apr 2018 15:23:05 -0400 Subject: [PATCH] Notify users of corrupt messages. --- Signal/src/environment/NotificationsManager.m | 42 ++++++++++++++++++- Signal/src/network/PushManager.m | 3 ++ .../NoopNotificationsManager.swift | 4 ++ .../Messages/Interactions/TSErrorMessage.h | 2 + .../Messages/Interactions/TSErrorMessage.m | 10 +++++ .../src/Messages/OWSBatchMessageProcessor.m | 6 +++ .../src/Messages/OWSMessageDecrypter.m | 7 ++++ .../src/Messages/OWSMessageReceiver.m | 11 +++++ .../src/Network/WebSockets/TSSocketManager.m | 11 +++++ .../src/Protocols/NotificationsProtocol.h | 3 ++ 10 files changed, 98 insertions(+), 1 deletion(-) diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index 1bd2f6f74..cde2590d8 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -206,7 +206,13 @@ transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(message); - OWSAssert(thread); + + if (!thread) { + OWSProdLogAndFail( + @"%@ unexpected notification not associated with a thread: %@.", self.logTag, [message class]); + [self notifyUserForThreadlessErrorMessage:message transaction:transaction]; + return; + } NSString *messageText = [message previewTextWithTransaction:transaction]; @@ -253,6 +259,40 @@ }]; } +- (void)notifyUserForThreadlessErrorMessage:(TSErrorMessage *)message + transaction:(YapDatabaseReadWriteTransaction *)transaction; +{ + OWSAssert(message); + + NSString *messageText = [message previewTextWithTransaction:transaction]; + + [transaction + addCompletionQueue:nil + completionBlock:^() { + BOOL shouldPlaySound = [self shouldPlaySoundForNotification]; + + if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageText) { + UILocalNotification *notification = [[UILocalNotification alloc] init]; + if (shouldPlaySound) { + OWSSound sound = [OWSSounds globalNotificationSound]; + notification.soundName = [OWSSounds filenameForSound:sound]; + } + + NSString *alertBodyString = messageText; + notification.alertBody = alertBodyString; + + [[PushManager sharedManager] presentNotification:notification checkForCancel:NO]; + } else { + if (shouldPlaySound && [Environment.preferences soundInForeground]) { + OWSSound sound = [OWSSounds globalNotificationSound]; + SystemSoundID soundId = [OWSSounds systemSoundIDForSound:sound quiet:YES]; + // Vibrate, respect silent switch, respect "Alert" volume, not media volume. + AudioServicesPlayAlertSound(soundId); + } + } + }]; +} + - (void)notifyUserForIncomingMessage:(TSIncomingMessage *)message inThread:(TSThread *)thread contactsManager:(id)contactsManager diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index 0e950bbed..30b99f302 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -279,6 +279,9 @@ NSString *const Signal_Message_MarkAsRead_Identifier = @"Signal_Message_MarkAsRe - (void)markAllInThreadAsRead:(NSDictionary *)userInfo completionHandler:(void (^)(void))completionHandler { NSString *threadId = userInfo[Signal_Thread_UserInfo_Key]; + if (!threadId) { + return; + } TSThread *thread = [TSThread fetchObjectWithUniqueID:threadId]; [OWSPrimaryStorage.dbReadWriteConnection diff --git a/SignalMessaging/environment/NoopNotificationsManager.swift b/SignalMessaging/environment/NoopNotificationsManager.swift index a801a7d8d..4d1ccf6e1 100644 --- a/SignalMessaging/environment/NoopNotificationsManager.swift +++ b/SignalMessaging/environment/NoopNotificationsManager.swift @@ -14,4 +14,8 @@ public class NoopNotificationsManager: NSObject, NotificationsProtocol { public func notifyUser(for error: TSErrorMessage, thread: TSThread, transaction: YapDatabaseReadWriteTransaction) { Logger.warn("\(self.logTag) in \(#function), skipping notification for: \(error.description)") } + + public func notifyUser(forThreadlessErrorMessage error: TSErrorMessage, transaction: YapDatabaseReadWriteTransaction) { + Logger.warn("\(self.logTag) in \(#function), skipping notification for: \(error.description)") + } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index b9ce08ff5..91523a1a7 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -56,6 +56,8 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) { + (instancetype)corruptedMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; ++ (instancetype)corruptedMessageInUnknownThread; + + (instancetype)invalidVersionWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 1f130877a..d2b4d3915 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -92,6 +92,11 @@ NSUInteger TSErrorMessageSchemaVersion = 1; return [self initWithTimestamp:envelope.timestamp inThread:contactThread failedMessageType:errorMessageType]; } +- (instancetype)initWithFailedMessageType:(TSErrorMessageType)errorMessageType +{ + return [self initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:nil failedMessageType:errorMessageType]; +} + - (OWSInteractionType)interactionType { return OWSInteractionType_Error; @@ -147,6 +152,11 @@ NSUInteger TSErrorMessageSchemaVersion = 1; failedMessageType:TSErrorMessageInvalidMessage]; } ++ (instancetype)corruptedMessageInUnknownThread +{ + return [[self alloc] initWithFailedMessageType:TSErrorMessageInvalidMessage]; +} + + (instancetype)invalidVersionWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction { diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index fc41a9ec8..4649ebc16 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -6,6 +6,7 @@ #import "AppContext.h" #import "AppReadiness.h" #import "NSArray+OWS.h" +#import "NotificationsProtocol.h" #import "OWSBackgroundTask.h" #import "OWSMessageManager.h" #import "OWSPrimaryStorage+SessionStore.h" @@ -14,7 +15,9 @@ #import "OWSSignalServiceProtos.pb.h" #import "OWSStorage.h" #import "TSDatabaseView.h" +#import "TSErrorMessage.h" #import "TSYapDatabaseObject.h" +#import "TextSecureKitEnv.h" #import "Threading.h" #import #import @@ -382,6 +385,9 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo } @catch (NSException *exception) { OWSProdLogAndFail(@"%@ Received an invalid envelope: %@", self.logTag, exception.debugDescription); // TODO: Add analytics. + TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForThreadlessErrorMessage:errorMessage + transaction:transaction]; } [processedJobs addObject:job]; diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index dffbad90e..0320705dc 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -170,6 +170,13 @@ NS_ASSUME_NONNULL_BEGIN } @catch (NSException *exception) { OWSProdLogAndFail(@"%@ Received an invalid envelope: %@", self.logTag, exception.debugDescription); OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]); + + [[OWSPrimaryStorage.sharedManager newDatabaseConnection] + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForThreadlessErrorMessage:errorMessage + transaction:transaction]; + }]; } failureBlock(); diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index 0af8b6baa..7c90b108a 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -6,6 +6,7 @@ #import "AppContext.h" #import "AppReadiness.h" #import "NSArray+OWS.h" +#import "NotificationsProtocol.h" #import "OWSBackgroundTask.h" #import "OWSBatchMessageProcessor.h" #import "OWSMessageDecrypter.h" @@ -14,7 +15,9 @@ #import "OWSSignalServiceProtos.pb.h" #import "OWSStorage.h" #import "TSDatabaseView.h" +#import "TSErrorMessage.h" #import "TSYapDatabaseObject.h" +#import "TextSecureKitEnv.h" #import "Threading.h" #import #import @@ -322,6 +325,14 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin } @catch (NSException *exception) { OWSProdLogAndFail(@"%@ Could not parse proto: %@", self.logTag, exception.debugDescription); // TODO: Add analytics. + + [[OWSPrimaryStorage.sharedManager newDatabaseConnection] + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForThreadlessErrorMessage:errorMessage + transaction:transaction]; + }]; + dispatch_async(self.serialQueue, ^{ completion(NO); }); diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m index 8074e8e24..b183a6df2 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m @@ -8,15 +8,19 @@ #import "Cryptography.h" #import "NSNotificationCenter+OWS.h" #import "NSTimer+OWS.h" +#import "NotificationsProtocol.h" #import "OWSBackgroundTask.h" #import "OWSMessageManager.h" #import "OWSMessageReceiver.h" +#import "OWSPrimaryStorage.h" #import "OWSSignalService.h" #import "OWSSignalServiceProtos.pb.h" #import "OWSWebsocketSecurityPolicy.h" #import "SubProtocol.pb.h" #import "TSAccountManager.h" #import "TSConstants.h" +#import "TSErrorMessage.h" +#import "TextSecureKitEnv.h" #import "Threading.h" static const CGFloat kSocketHeartbeatPeriodSeconds = 30.f; @@ -403,6 +407,13 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ } @catch (NSException *exception) { OWSProdLogAndFail(@"%@ Received an invalid envelope: %@", self.logTag, exception.debugDescription); // TODO: Add analytics. + + [[OWSPrimaryStorage.sharedManager newDatabaseConnection] readWriteWithBlock:^( + YapDatabaseReadWriteTransaction *transaction) { + TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForThreadlessErrorMessage:errorMessage + transaction:transaction]; + }]; } dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/SignalServiceKit/src/Protocols/NotificationsProtocol.h b/SignalServiceKit/src/Protocols/NotificationsProtocol.h index 892c80eed..5a9abba27 100644 --- a/SignalServiceKit/src/Protocols/NotificationsProtocol.h +++ b/SignalServiceKit/src/Protocols/NotificationsProtocol.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN thread:(TSThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction; +- (void)notifyUserForThreadlessErrorMessage:(TSErrorMessage *)error + transaction:(YapDatabaseReadWriteTransaction *)transaction; + @end NS_ASSUME_NONNULL_END