diff --git a/src/Messages/Interactions/OWSVerificationStateChangeMessage.h b/src/Messages/Interactions/OWSVerificationStateChangeMessage.h new file mode 100644 index 000000000..2f1a3f75a --- /dev/null +++ b/src/Messages/Interactions/OWSVerificationStateChangeMessage.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSRecipientIdentity.h" +#import "TSInfoMessage.h" + +NS_ASSUME_NONNULL_BEGIN + +@class TSThread; + +@interface OWSVerificationStateChangeMessage : TSInfoMessage + +@property (nonatomic, readonly) NSString *recipientId; +@property (nonatomic, readonly) OWSVerificationState verificationState; +@property (nonatomic, readonly) BOOL isLocalChange; + +- (instancetype)initWithTimestamp:(uint64_t)timestamp + thread:(TSThread *)thread + recipientId:(NSString *)recipientId + verificationState:(OWSVerificationState)verificationState + isLocalChange:(BOOL)isLocalChange; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Messages/Interactions/OWSVerificationStateChangeMessage.m b/src/Messages/Interactions/OWSVerificationStateChangeMessage.m new file mode 100644 index 000000000..8b82f51b2 --- /dev/null +++ b/src/Messages/Interactions/OWSVerificationStateChangeMessage.m @@ -0,0 +1,34 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSVerificationStateChangeMessage.h" +#import "OWSDisappearingMessagesConfiguration.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation OWSVerificationStateChangeMessage + +- (instancetype)initWithTimestamp:(uint64_t)timestamp + thread:(TSThread *)thread + recipientId:(NSString *)recipientId + verificationState:(OWSVerificationState)verificationState + isLocalChange:(BOOL)isLocalChange +{ + OWSAssert(recipientId.length > 0); + + self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageVerificationStateChange]; + if (!self) { + return self; + } + + _recipientId = recipientId; + _verificationState = verificationState; + _isLocalChange = isLocalChange; + + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Messages/Interactions/TSInfoMessage.h b/src/Messages/Interactions/TSInfoMessage.h index 7ee7c924a..4bd00c871 100644 --- a/src/Messages/Interactions/TSInfoMessage.h +++ b/src/Messages/Interactions/TSInfoMessage.h @@ -18,6 +18,7 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType) { TSInfoMessageTypeGroupQuit, TSInfoMessageTypeDisappearingMessagesUpdate, TSInfoMessageAddToContactsOffer, + TSInfoMessageVerificationStateChange, }; + (instancetype)userNotRegisteredMessageInThread:(TSThread *)thread; diff --git a/src/Messages/OWSIdentityManager.h b/src/Messages/OWSIdentityManager.h index 17f31ddd7..d5d8afc98 100644 --- a/src/Messages/OWSIdentityManager.h +++ b/src/Messages/OWSIdentityManager.h @@ -37,6 +37,8 @@ extern const NSUInteger kIdentityKeyLength; - (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId; +- (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId; + /** * @param recipientId unique stable identifier for the recipient, e.g. e164 phone number * @returns nil if the recipient does not exist, or is trusted for sending diff --git a/src/Messages/OWSIdentityManager.m b/src/Messages/OWSIdentityManager.m index d94057d72..ceb267895 100644 --- a/src/Messages/OWSIdentityManager.m +++ b/src/Messages/OWSIdentityManager.m @@ -3,9 +3,11 @@ // #import "OWSIdentityManager.h" +#import "NSDate+millisecondTimeStamp.h" #import "NotificationsProtocol.h" #import "OWSMessageSender.h" #import "OWSRecipientIdentity.h" +#import "OWSVerificationStateChangeMessage.h" #import "OWSVerificationStateSyncMessage.h" #import "TSAccountManager.h" #import "TSContactThread.h" @@ -190,6 +192,12 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa // Cancel any pending verification state sync messages for this recipient. [self clearSyncMessageForRecipientId:recipientId]; + if (existingIdentity.verificationState != verificationState) { + [self saveChangeMessagesForRecipientId:recipientId + verificationState:verificationState + isLocalChange:YES]; + } + [self fireIdentityStateChangeNotification]; return YES; @@ -241,6 +249,8 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa // Cancel any pending verification state sync messages for this recipient. [self clearSyncMessageForRecipientId:recipientId]; } + + [self saveChangeMessagesForRecipientId:recipientId verificationState:verificationState isLocalChange:YES]; } [self fireIdentityStateChangeNotification]; @@ -263,6 +273,16 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa } } +- (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.length > 0); + + @synchronized(self) + { + return [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId]; + } +} + - (nullable OWSRecipientIdentity *)untrustedIdentityForSendingToRecipientId:(NSString *)recipientId { OWSAssert(recipientId.length > 0); @@ -385,18 +405,25 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa { OWSAssert(recipientId != nil); + NSMutableArray *messages = [NSMutableArray new]; + TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId]; OWSAssert(contactThread != nil); TSErrorMessage *errorMessage = [TSErrorMessage nonblockingIdentityChangeInThread:contactThread recipientId:recipientId]; - [errorMessage save]; - + [messages addObject:errorMessage]; [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread]; for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId]) { - [[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId] save]; + [messages addObject:[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId]]; } + + [self.storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + for (TSMessage *message in messages) { + [message saveWithTransaction:transaction]; + } + }]; } - (void)enqueueSyncMessageForVerificationState:(OWSVerificationState)verificationState @@ -497,6 +524,14 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa OWSAssert(message); OWSAssert(message.recipientIds.count > 0); + if (YES) { + // Don't actually transmit any verification state sync messages + // until we finalize the proto schema changes. + // + // TODO: Remove. + return; + } + [self.messageSender sendMessage:message success:^{ DDLogInfo(@"%@ Successfully sent verification state sync message", self.tag); @@ -625,6 +660,9 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa OWSVerificationStateToString(verificationState)); [recipientIdentity updateWithVerificationState:verificationState]; + + // No need to call [saveChangeMessagesForRecipientId:..] since this is + // a new recipient. } else { // There's an existing recipient identity for this recipient. // We should update it. @@ -668,10 +706,44 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa } [recipientIdentity updateWithVerificationState:verificationState]; + + [self saveChangeMessagesForRecipientId:recipientId verificationState:verificationState isLocalChange:NO]; } } } +- (void)saveChangeMessagesForRecipientId:(NSString *)recipientId + verificationState:(OWSVerificationState)verificationState + isLocalChange:(BOOL)isLocalChange +{ + OWSAssert(recipientId.length > 0); + + NSMutableArray *messages = [NSMutableArray new]; + + TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId]; + OWSAssert(contactThread); + [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:contactThread + recipientId:recipientId + verificationState:verificationState + isLocalChange:isLocalChange]]; + + for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId]) { + [messages + addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:groupThread + recipientId:recipientId + verificationState:verificationState + isLocalChange:isLocalChange]]; + } + + [self.storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + for (TSMessage *message in messages) { + [message saveWithTransaction:transaction]; + } + }]; +} + #pragma mark - Notifications - (void)applicationDidBecomeActive:(NSNotification *)notification