From 975726e022d89872b3c781769efb9c4e45dbb6d4 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 15 Feb 2017 18:32:27 -0500 Subject: [PATCH 1/3] Dedupe incoming messags // FREEBIE --- src/Devices/OWSDevice.h | 9 +- src/Devices/OWSDevice.m | 13 +- src/Messages/Interactions/TSIncomingMessage.h | 10 ++ src/Messages/Interactions/TSIncomingMessage.m | 12 +- src/Messages/OWSMessageSender.m | 2 + src/Messages/TSMessagesManager.m | 18 +- src/Storage/OWSIncomingMessageFinder.h | 28 ++++ src/Storage/OWSIncomingMessageFinder.m | 156 ++++++++++++++++++ src/Storage/TSStorageManager.m | 2 + tests/Contacts/TSThreadTest.m | 6 +- tests/Storage/OWSOrphanedDataCleanerTest.m | 7 +- tests/Storage/TSMessageStorageTests.m | 9 +- 12 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 src/Storage/OWSIncomingMessageFinder.h create mode 100644 src/Storage/OWSIncomingMessageFinder.m diff --git a/src/Devices/OWSDevice.h b/src/Devices/OWSDevice.h index 774faa521..54209673b 100644 --- a/src/Devices/OWSDevice.h +++ b/src/Devices/OWSDevice.h @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "TSYapDatabaseObject.h" #import @@ -22,6 +24,11 @@ NS_ASSUME_NONNULL_BEGIN */ + (void)replaceAll:(NSArray *)devices; +/** + * The id of the device currently running this application + */ ++ (uint32_t)currentDeviceId; + /** * * @param transaction diff --git a/src/Devices/OWSDevice.m b/src/Devices/OWSDevice.m index a8f710ff1..14842667e 100644 --- a/src/Devices/OWSDevice.m +++ b/src/Devices/OWSDevice.m @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSDevice.h" #import "NSDate+millisecondTimeStamp.h" @@ -10,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN static MTLValueTransformer *_millisecondTimestampToDateTransformer; -static int const OWSDevicePrimaryDeviceId = 1; +static uint32_t const OWSDevicePrimaryDeviceId = 1; @interface OWSDevice () @@ -108,6 +110,13 @@ static int const OWSDevicePrimaryDeviceId = 1; return _millisecondTimestampToDateTransformer; } ++ (uint32_t)currentDeviceId +{ + // Someday it may be possible to have a non-primary iOS device, but for now + // any iOS device must be the primary device. + return OWSDevicePrimaryDeviceId; +} + - (BOOL)isPrimaryDevice { return self.deviceId == OWSDevicePrimaryDeviceId; diff --git a/src/Messages/Interactions/TSIncomingMessage.h b/src/Messages/Interactions/TSIncomingMessage.h index f7804ea2b..afa4e3319 100644 --- a/src/Messages/Interactions/TSIncomingMessage.h +++ b/src/Messages/Interactions/TSIncomingMessage.h @@ -23,6 +23,8 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; * Thread to which the message belongs * @param authorId * Signal ID (i.e. e164) of the user who sent the message + * @param sourceDeviceId + * Numeric ID of the device used to send the message. Used to detect duplicate messages. * @param body * Body of the message * @@ -31,6 +33,7 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body; /** @@ -42,6 +45,8 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; * Thread to which the message belongs * @param authorId * Signal ID (i.e. e164) of the user who sent the message + * @param sourceDeviceId + * Numeric ID of the device used to send the message. Used to detect duplicate messages. * @param body * Body of the message * @param attachmentIds @@ -52,6 +57,7 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body attachmentIds:(NSArray *)attachmentIds; @@ -64,6 +70,8 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; * Thread to which the message belongs * @param authorId * Signal ID (i.e. e164) of the user who sent the message + * @param sourceDeviceId + * Numeric ID of the device used to send the message. Used to detect duplicate messages. * @param body * Body of the message * @param attachmentIds @@ -76,6 +84,7 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body attachmentIds:(NSArray *)attachmentIds expiresInSeconds:(uint32_t)expiresInSeconds NS_DESIGNATED_INITIALIZER; @@ -120,6 +129,7 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; + (nullable instancetype)findMessageWithAuthorId:(NSString *)authorId timestamp:(uint64_t)timestamp; @property (nonatomic, readonly) NSString *authorId; +@property (nonatomic, readonly) UInt32 sourceDeviceId; @property (nonatomic, readonly, getter=wasRead) BOOL read; /* diff --git a/src/Messages/Interactions/TSIncomingMessage.m b/src/Messages/Interactions/TSIncomingMessage.m index e10acedc8..d03df08fc 100644 --- a/src/Messages/Interactions/TSIncomingMessage.m +++ b/src/Messages/Interactions/TSIncomingMessage.m @@ -22,20 +22,28 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body { - return [self initWithTimestamp:timestamp inThread:thread authorId:authorId messageBody:body attachmentIds:@[]]; + return [self initWithTimestamp:timestamp + inThread:thread + authorId:authorId + sourceDeviceId:sourceDeviceId + messageBody:body + attachmentIds:@[]]; } - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body attachmentIds:(NSArray *)attachmentIds { return [self initWithTimestamp:timestamp inThread:thread authorId:authorId + sourceDeviceId:sourceDeviceId messageBody:body attachmentIds:attachmentIds expiresInSeconds:0]; @@ -44,6 +52,7 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread authorId:(NSString *)authorId + sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body attachmentIds:(NSArray *)attachmentIds expiresInSeconds:(uint32_t)expiresInSeconds @@ -60,6 +69,7 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM } _authorId = authorId; + _sourceDeviceId = sourceDeviceId; _read = NO; OWSAssert(self.receivedAtDate); diff --git a/src/Messages/OWSMessageSender.m b/src/Messages/OWSMessageSender.m index 4705c0960..01d3f7f95 100644 --- a/src/Messages/OWSMessageSender.m +++ b/src/Messages/OWSMessageSender.m @@ -5,6 +5,7 @@ #import "OWSMessageSender.h" #import "ContactsUpdater.h" #import "NSData+messagePadding.h" +#import "OWSDevice.h" #import "OWSDisappearingMessagesJob.h" #import "OWSError.h" #import "OWSLegacyMessageServiceParams.h" @@ -622,6 +623,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [[TSIncomingMessage alloc] initWithTimestamp:(outgoingMessage.timestamp + 1) inThread:cThread authorId:[cThread contactIdentifier] + sourceDeviceId:[OWSDevice currentDeviceId] messageBody:outgoingMessage.body attachmentIds:outgoingMessage.attachmentIds expiresInSeconds:outgoingMessage.expiresInSeconds]; diff --git a/src/Messages/TSMessagesManager.m b/src/Messages/TSMessagesManager.m index 28098f572..00114498e 100644 --- a/src/Messages/TSMessagesManager.m +++ b/src/Messages/TSMessagesManager.m @@ -15,6 +15,7 @@ #import "OWSDisappearingMessagesConfiguration.h" #import "OWSDisappearingMessagesJob.h" #import "OWSError.h" +#import "OWSIncomingMessageFinder.h" #import "OWSIncomingSentMessageTranscript.h" #import "OWSMessageSender.h" #import "OWSReadReceiptsProcessor.h" @@ -46,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob; +@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder; @end @@ -102,6 +104,7 @@ NS_ASSUME_NONNULL_BEGIN _dbConnection = storageManager.newDatabaseConnection; _disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager]; + _incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithDatabase:storageManager.database]; return self; } @@ -281,6 +284,15 @@ NS_ASSUME_NONNULL_BEGIN - (void)handleEnvelope:(OWSSignalServiceProtosEnvelope *)envelope plaintextData:(NSData *)plaintextData { OWSAssert([NSThread isMainThread]); + + BOOL duplicateEnvelope = [self.incomingMessageFinder existsMessageWithTimestamp:envelope.timestamp + sourceId:envelope.source + sourceDeviceId:envelope.sourceDevice]; + if (duplicateEnvelope) { + DDLogInfo(@"%@ Ignoring previously received envelope with timestamp: %llu", self.tag, envelope.timestamp); + return; + } + if (envelope.hasContent) { OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData]; if (content.hasSyncMessage) { @@ -290,7 +302,7 @@ NS_ASSUME_NONNULL_BEGIN } else if (content.hasCallMessage) { [self handleIncomingEnvelope:envelope withCallMessage:content.callMessage]; } else { - DDLogWarn(@"%@ Ignoring envelope.Content with no known payload", self.tag); + DDLogWarn(@"%@ Ignoring envelope. Content with no known payload", self.tag); } } else if (envelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded. OWSSignalServiceProtosDataMessage *dataMessage = @@ -611,6 +623,7 @@ NS_ASSUME_NONNULL_BEGIN incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp inThread:gThread authorId:envelope.source + sourceDeviceId:envelope.sourceDevice messageBody:body attachmentIds:attachmentIds expiresInSeconds:dataMessage.expireTimer]; @@ -632,6 +645,7 @@ NS_ASSUME_NONNULL_BEGIN incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp inThread:cThread authorId:[cThread contactIdentifier] + sourceDeviceId:envelope.sourceDevice messageBody:body attachmentIds:attachmentIds expiresInSeconds:dataMessage.expireTimer]; @@ -658,12 +672,14 @@ NS_ASSUME_NONNULL_BEGIN textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp inThread:gThread authorId:envelope.source + sourceDeviceId:envelope.sourceDevice messageBody:body]; } else { TSContactThread *cThread = (TSContactThread *)thread; textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp inThread:cThread authorId:[cThread contactIdentifier] + sourceDeviceId:envelope.sourceDevice messageBody:body]; } textMessage.expiresInSeconds = dataMessage.expireTimer; diff --git a/src/Storage/OWSIncomingMessageFinder.h b/src/Storage/OWSIncomingMessageFinder.h new file mode 100644 index 000000000..49c16c5de --- /dev/null +++ b/src/Storage/OWSIncomingMessageFinder.h @@ -0,0 +1,28 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@class YapDatabase; +@class YapDatabaseReadTransaction; + +@interface OWSIncomingMessageFinder : NSObject + +- (instancetype)initWithDatabase:(YapDatabase *)database NS_DESIGNATED_INITIALIZER; + +/** + * Must be called before using this finder. + */ +- (void)asyncRegisterExtension; + +/** + * Detects existance of a duplicate incoming message. + */ +- (BOOL)existsMessageWithTimestamp:(uint64_t)timestamp + sourceId:(NSString *)sourceId + sourceDeviceId:(uint32_t)sourceDeviceId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Storage/OWSIncomingMessageFinder.m b/src/Storage/OWSIncomingMessageFinder.m new file mode 100644 index 000000000..3c6161474 --- /dev/null +++ b/src/Storage/OWSIncomingMessageFinder.m @@ -0,0 +1,156 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSIncomingMessageFinder.h" +#import "TSIncomingMessage.h" +#import "TSStorageManager.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +NSString *const OWSIncomingMessageFinderExtensionName = @"OWSIncomingMessageFinderExtensionName"; + +NSString *const OWSIncomingMessageFinderColumnTimestamp = @"OWSIncomingMessageFinderColumnTimestamp"; +NSString *const OWSIncomingMessageFinderColumnSourceId = @"OWSIncomingMessageFinderColumnSourceId"; +NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMessageFinderColumnSourceDeviceId"; + +@interface OWSIncomingMessageFinder () + +@property (nonatomic, readonly) YapDatabase *database; +@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; +@property (nonatomic, readonly) BOOL isExtensionRegistered; + +@end + +@implementation OWSIncomingMessageFinder + +@synthesize dbConnection = _dbConnection; + +#pragma mark - init + +- (instancetype)init +{ + OWSAssert([TSStorageManager sharedManager].database != nil); + + return [self initWithDatabase:[TSStorageManager sharedManager].database]; +} + +- (instancetype)initWithDatabase:(YapDatabase *)database +{ + self = [super init]; + if (!self) { + return self; + } + + _database = database; + + return self; +} + +#pragma mark - properties + +- (YapDatabaseConnection *)dbConnection +{ + @synchronized (self) { + if (!_dbConnection) { + _dbConnection = self.database.newConnection; + } + } + return _dbConnection; +} + +#pragma mark - YAP integration + +- (YapDatabaseSecondaryIndex *)indexExtension +{ + YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new]; + + [setup addColumn:OWSIncomingMessageFinderColumnTimestamp withType:YapDatabaseSecondaryIndexTypeInteger]; + [setup addColumn:OWSIncomingMessageFinderColumnSourceId withType:YapDatabaseSecondaryIndexTypeText]; + [setup addColumn:OWSIncomingMessageFinderColumnSourceDeviceId withType:YapDatabaseSecondaryIndexTypeInteger]; + + YapDatabaseSecondaryIndexWithObjectBlock block = ^(YapDatabaseReadTransaction *transaction, + NSMutableDictionary *dict, + NSString *collection, + NSString *key, + id object) { + if ([object isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object; + + [dict setObject:@(incomingMessage.timestamp) forKey:OWSIncomingMessageFinderColumnTimestamp]; + [dict setObject:incomingMessage.authorId forKey:OWSIncomingMessageFinderColumnSourceId]; + [dict setObject:@(incomingMessage.sourceDeviceId) forKey:OWSIncomingMessageFinderColumnSourceDeviceId]; + } + }; + + YapDatabaseSecondaryIndexHandler *handler = [YapDatabaseSecondaryIndexHandler withObjectBlock:block]; + + return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; +} + +- (void)asyncRegisterExtension +{ + DDLogInfo(@"%@ registering async.", self.tag); + [self.database registerExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName]; +} + +- (void)registerExtension +{ + OWSAssert(NO); + DDLogError(@"%@ registering SYNC. We should prefer async when possible.", self.tag); + [self.database registerExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName]; +} + +#pragma mark - instance methods + +- (BOOL)existsMessageWithTimestamp:(uint64_t)timestamp + sourceId:(NSString *)sourceId + sourceDeviceId:(uint32_t)sourceDeviceId +{ + if (![self.database registeredExtension:OWSIncomingMessageFinderExtensionName]) { + DDLogError(@"%@ in %s but extension is not registered", self.tag, __PRETTY_FUNCTION__); + OWSAssert(NO); + + // we should be initializing this at startup rather than have an unexpectedly slow lazy setup at random. + [self registerExtension]; + } + + NSString *queryFormat = [NSString stringWithFormat:@"WHERE %@ = ? AND %@ = ? AND %@ = ?", + OWSIncomingMessageFinderColumnTimestamp, + OWSIncomingMessageFinderColumnSourceId, + OWSIncomingMessageFinderColumnSourceDeviceId]; + // YapDatabaseQuery params must be objects + YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:queryFormat, @(timestamp), sourceId, @(sourceDeviceId)]; + + __block NSUInteger count; + __block BOOL success; + + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + success = [[transaction ext:OWSIncomingMessageFinderExtensionName] getNumberOfRows:&count matchingQuery:query]; + }]; + + if (!success) { + OWSAssert(NO); + return NO; + } + + return count > 0; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Storage/TSStorageManager.m b/src/Storage/TSStorageManager.m index b02951112..c3d9a613d 100644 --- a/src/Storage/TSStorageManager.m +++ b/src/Storage/TSStorageManager.m @@ -7,6 +7,7 @@ #import "OWSAnalytics.h" #import "OWSDisappearingMessagesFinder.h" #import "OWSFailedMessagesJob.h" +#import "OWSIncomingMessageFinder.h" #import "OWSReadReceipt.h" #import "SignalRecipient.h" #import "TSAttachmentStream.h" @@ -197,6 +198,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; [self.database registerExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"]; // Register extensions which aren't essential for rendering threads async + [[OWSIncomingMessageFinder new] asyncRegisterExtension]; [TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView]; [OWSReadReceipt asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:self.database]; OWSDisappearingMessagesFinder *finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self]; diff --git a/tests/Contacts/TSThreadTest.m b/tests/Contacts/TSThreadTest.m index 19936de98..117e30ba6 100644 --- a/tests/Contacts/TSThreadTest.m +++ b/tests/Contacts/TSThreadTest.m @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "TSAttachmentStream.h" #import "TSContactThread.h" @@ -38,6 +40,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:10000 inThread:thread authorId:@"fake-author-id" + sourceDeviceId:1 messageBody:@"Incoming message body"]; [incomingMessage save]; @@ -74,6 +77,7 @@ [[TSIncomingMessage alloc] initWithTimestamp:10000 inThread:thread authorId:@"fake-author-id" + sourceDeviceId:1 messageBody:@"incoming message body" attachmentIds:[NSMutableArray arrayWithObject:incomingAttachment.uniqueId]]; [incomingMessage save]; diff --git a/tests/Storage/OWSOrphanedDataCleanerTest.m b/tests/Storage/OWSOrphanedDataCleanerTest.m index e6be9cfb4..85c674fcc 100644 --- a/tests/Storage/OWSOrphanedDataCleanerTest.m +++ b/tests/Storage/OWSOrphanedDataCleanerTest.m @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSOrphanedDataCleaner.h" #import "TSAttachmentStream.h" @@ -45,6 +47,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:unsavedThread authorId:@"fake-author-id" + sourceDeviceId:1 messageBody:@"footch"]; [incomingMessage save]; XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); @@ -61,6 +64,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:savedThread authorId:@"fake-author-id" + sourceDeviceId:1 messageBody:@"footch"]; [incomingMessage save]; XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); @@ -99,6 +103,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:savedThread authorId:@"fake-author-id" + sourceDeviceId:1 messageBody:@"footch" attachmentIds:@[ attachmentStream.uniqueId ]]; [incomingMessage save]; diff --git a/tests/Storage/TSMessageStorageTests.m b/tests/Storage/TSMessageStorageTests.m index 1850107b0..ac35145e3 100644 --- a/tests/Storage/TSMessageStorageTests.m +++ b/tests/Storage/TSMessageStorageTests.m @@ -1,9 +1,5 @@ // -// TSMessageStorageTests.m -// TextSecureKit -// -// Created by Frederic Jacobs on 16/11/14. -// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import @@ -102,6 +98,7 @@ TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp inThread:self.thread authorId:[self.thread contactIdentifier] + sourceDeviceId:1 messageBody:body]; [[TSStorageManager sharedManager].newDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [newMessage saveWithTransaction:transaction]; @@ -126,6 +123,7 @@ TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:i inThread:self.thread authorId:[self.thread contactIdentifier] + sourceDeviceId:1 messageBody:body]; [messages addObject:newMessage]; [newMessage save]; @@ -174,6 +172,7 @@ TSIncomingMessage *newMessage = [[TSIncomingMessage alloc] initWithTimestamp:i inThread:thread authorId:@"Ed" + sourceDeviceId:1 messageBody:body]; [newMessage save]; [messages addObject:newMessage]; From b389bb3bb8dcbc659f487b79240a413c672bd7c6 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 17 Feb 2017 18:58:08 -0500 Subject: [PATCH 2/3] Code cleanup. No need to use contactThread method to get recipient ID since we still have the envelope. And with that, it's pretty easy to justify getting rid of one of our now barely used IncomingMessage initializers. // FREEBIE --- src/Messages/Interactions/TSIncomingMessage.h | 29 ++----------------- src/Messages/Interactions/TSIncomingMessage.m | 17 +---------- src/Messages/TSMessagesManager.m | 27 ++++++----------- 3 files changed, 12 insertions(+), 61 deletions(-) diff --git a/src/Messages/Interactions/TSIncomingMessage.h b/src/Messages/Interactions/TSIncomingMessage.h index afa4e3319..d9062f199 100644 --- a/src/Messages/Interactions/TSIncomingMessage.h +++ b/src/Messages/Interactions/TSIncomingMessage.h @@ -36,31 +36,6 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; sourceDeviceId:(uint32_t)sourceDeviceId messageBody:(nullable NSString *)body; -/** - * Inits an incoming group message with attachments - * - * @param timestamp - * When the message was created in milliseconds since epoch - * @param thread - * Thread to which the message belongs - * @param authorId - * Signal ID (i.e. e164) of the user who sent the message - * @param sourceDeviceId - * Numeric ID of the device used to send the message. Used to detect duplicate messages. - * @param body - * Body of the message - * @param attachmentIds - * The uniqueIds for the message's attachments - * - * @return initiated incoming group message - */ -- (instancetype)initWithTimestamp:(uint64_t)timestamp - inThread:(TSThread *)thread - authorId:(NSString *)authorId - sourceDeviceId:(uint32_t)sourceDeviceId - messageBody:(nullable NSString *)body - attachmentIds:(NSArray *)attachmentIds; - /** * Inits an incoming group message that expires. * @@ -93,8 +68,8 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; /** - * For sake of a smaller API, you must specify an author id for all incoming messages - * though we technically could get the author id from a contact thread. + * For sake of a smaller API, and simplifying assumptions elsewhere, you must specify an author id for *all* incoming + * messages, even though we technically could infer the author id for a contact threads. */ - (instancetype)initWithTimestamp:(uint64_t)timestamp NS_UNAVAILABLE; - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_UNAVAILABLE; diff --git a/src/Messages/Interactions/TSIncomingMessage.m b/src/Messages/Interactions/TSIncomingMessage.m index d03df08fc..c91ea7cce 100644 --- a/src/Messages/Interactions/TSIncomingMessage.m +++ b/src/Messages/Interactions/TSIncomingMessage.m @@ -30,22 +30,7 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM authorId:authorId sourceDeviceId:sourceDeviceId messageBody:body - attachmentIds:@[]]; -} - -- (instancetype)initWithTimestamp:(uint64_t)timestamp - inThread:(TSThread *)thread - authorId:(NSString *)authorId - sourceDeviceId:(uint32_t)sourceDeviceId - messageBody:(nullable NSString *)body - attachmentIds:(NSArray *)attachmentIds -{ - return [self initWithTimestamp:timestamp - inThread:thread - authorId:authorId - sourceDeviceId:sourceDeviceId - messageBody:body - attachmentIds:attachmentIds + attachmentIds:@[] expiresInSeconds:0]; } diff --git a/src/Messages/TSMessagesManager.m b/src/Messages/TSMessagesManager.m index 00114498e..7eddd0124 100644 --- a/src/Messages/TSMessagesManager.m +++ b/src/Messages/TSMessagesManager.m @@ -662,27 +662,18 @@ NS_ASSUME_NONNULL_BEGIN [incomingMessage markAsReadLocallyWithTransaction:transaction]; } - // Android allows attachments to be sent with body. + // Other clients allow attachments to be sent along with body, we want the text displayed as a separate + // message if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) { // We want the text to be displayed under the attachment uint64_t textMessageTimestamp = timestamp + 1; - TSIncomingMessage *textMessage; - if ([thread isGroupThread]) { - TSGroupThread *gThread = (TSGroupThread *)thread; - textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp - inThread:gThread - authorId:envelope.source - sourceDeviceId:envelope.sourceDevice - messageBody:body]; - } else { - TSContactThread *cThread = (TSContactThread *)thread; - textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp - inThread:cThread - authorId:[cThread contactIdentifier] - sourceDeviceId:envelope.sourceDevice - messageBody:body]; - } - textMessage.expiresInSeconds = dataMessage.expireTimer; + TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp + inThread:thread + authorId:envelope.source + sourceDeviceId:envelope.sourceDevice + messageBody:body + attachmentIds:@[] + expiresInSeconds:dataMessage.expireTimer]; [textMessage saveWithTransaction:transaction]; } } From a92158ef16d775f3da2aad3bb30a8d0d08a421e2 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 21 Feb 2017 19:21:09 -0500 Subject: [PATCH 3/3] CR: fix register `async` where specified * fix thread test * add IncomingMessageFinder test * use constants * clearer comments // FREEBIE --- .../TSKitiOSTestApp.xcodeproj/project.pbxproj | 4 + src/Devices/OWSDevice.h | 2 + src/Devices/OWSDevice.m | 2 +- src/Messages/Interactions/TSIncomingMessage.h | 4 +- src/Messages/TSMessagesManager.m | 3 + src/Storage/OWSIncomingMessageFinder.m | 9 +- tests/Contacts/TSThreadTest.m | 26 ++--- tests/Messages/OWSIncomingMessageFinderTest.m | 105 ++++++++++++++++++ tests/Storage/OWSOrphanedDataCleanerTest.m | 10 +- 9 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 tests/Messages/OWSIncomingMessageFinderTest.m diff --git a/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj b/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj index 3ac6a8eab..46206806b 100644 --- a/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj +++ b/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 45B840211D988DA100F9E938 /* OWSReadReceiptTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */; }; 45C6A09A1D2F029B007D8AC0 /* TSMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */; }; 45D7243F1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */; }; + 45E741B61E5D14E800735842 /* OWSIncomingMessageFinderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45E741B51E5D14E800735842 /* OWSIncomingMessageFinderTest.m */; }; 51520592F83F2440F2DE4D67 /* libPods-TSKitiOSTestApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8362AB8E280E0F64352F08A /* libPods-TSKitiOSTestApp.a */; }; 6323E1F7730289398452E5C5 /* OWSFingerprintTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6323E02A33682A8838FE3F27 /* OWSFingerprintTest.m */; }; 6323E339D5B8F4CB77EB3ED4 /* SignalRecipientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6323E3E540CF763D71DACB59 /* SignalRecipientTest.m */; }; @@ -99,6 +100,7 @@ 45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSReadReceiptTest.m; path = ../../../tests/Devices/OWSReadReceiptTest.m; sourceTree = ""; }; 45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageTest.m; path = ../../../tests/Messages/Interactions/TSMessageTest.m; sourceTree = ""; }; 45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDeviceProvisionerTest.m; path = ../../../tests/Devices/OWSDeviceProvisionerTest.m; sourceTree = ""; }; + 45E741B51E5D14E800735842 /* OWSIncomingMessageFinderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSIncomingMessageFinderTest.m; path = ../../../tests/Messages/OWSIncomingMessageFinderTest.m; sourceTree = ""; }; 6323E02A33682A8838FE3F27 /* OWSFingerprintTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFingerprintTest.m; path = ../../../tests/Security/OWSFingerprintTest.m; sourceTree = ""; }; 6323E3E540CF763D71DACB59 /* SignalRecipientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SignalRecipientTest.m; path = ../../tests/Contacts/SignalRecipientTest.m; sourceTree = ""; }; B6273DD11C13A2E500738558 /* TSKitiOSTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TSKitiOSTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -237,6 +239,7 @@ 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */, 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */, 453E1FCE1DA8313100DDD7B7 /* OWSMessageSenderTest.m */, + 45E741B51E5D14E800735842 /* OWSIncomingMessageFinderTest.m */, ); name = Messages; sourceTree = ""; @@ -561,6 +564,7 @@ 45D7243F1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m in Sources */, 4516E3E81DD153CC00DC4206 /* TSGroupThreadTest.m in Sources */, 45458B791CC342B600A02153 /* TSStoragePreKeyStoreTests.m in Sources */, + 45E741B61E5D14E800735842 /* OWSIncomingMessageFinderTest.m in Sources */, 452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */, 450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */, 452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */, diff --git a/src/Devices/OWSDevice.h b/src/Devices/OWSDevice.h index 54209673b..5caf78e4b 100644 --- a/src/Devices/OWSDevice.h +++ b/src/Devices/OWSDevice.h @@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN +extern uint32_t const OWSDevicePrimaryDeviceId; + @interface OWSDevice : TSYapDatabaseObject @property (nonatomic, readonly) NSInteger deviceId; diff --git a/src/Devices/OWSDevice.m b/src/Devices/OWSDevice.m index 14842667e..3f3e9ba6b 100644 --- a/src/Devices/OWSDevice.m +++ b/src/Devices/OWSDevice.m @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN static MTLValueTransformer *_millisecondTimestampToDateTransformer; -static uint32_t const OWSDevicePrimaryDeviceId = 1; +uint32_t const OWSDevicePrimaryDeviceId = 1; @interface OWSDevice () diff --git a/src/Messages/Interactions/TSIncomingMessage.h b/src/Messages/Interactions/TSIncomingMessage.h index d9062f199..cb1b43d83 100644 --- a/src/Messages/Interactions/TSIncomingMessage.h +++ b/src/Messages/Interactions/TSIncomingMessage.h @@ -69,7 +69,7 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; /** * For sake of a smaller API, and simplifying assumptions elsewhere, you must specify an author id for *all* incoming - * messages, even though we technically could infer the author id for a contact threads. + * messages, even though we technically could infer the author id for a contact thread. */ - (instancetype)initWithTimestamp:(uint64_t)timestamp NS_UNAVAILABLE; - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_UNAVAILABLE; @@ -104,6 +104,8 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification; + (nullable instancetype)findMessageWithAuthorId:(NSString *)authorId timestamp:(uint64_t)timestamp; @property (nonatomic, readonly) NSString *authorId; + +// This will be 0 for messages created before we were tracking sourceDeviceId @property (nonatomic, readonly) UInt32 sourceDeviceId; @property (nonatomic, readonly, getter=wasRead) BOOL read; diff --git a/src/Messages/TSMessagesManager.m b/src/Messages/TSMessagesManager.m index 7eddd0124..3daad6d44 100644 --- a/src/Messages/TSMessagesManager.m +++ b/src/Messages/TSMessagesManager.m @@ -284,6 +284,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)handleEnvelope:(OWSSignalServiceProtosEnvelope *)envelope plaintextData:(NSData *)plaintextData { OWSAssert([NSThread isMainThread]); + OWSAssert(envelope.hasTimestamp && envelope.timestamp > 0); + OWSAssert(envelope.hasSource && envelope.source.length > 0); + OWSAssert(envelope.hasSourceDevice && envelope.sourceDevice > 0); BOOL duplicateEnvelope = [self.incomingMessageFinder existsMessageWithTimestamp:envelope.timestamp sourceId:envelope.source diff --git a/src/Storage/OWSIncomingMessageFinder.m b/src/Storage/OWSIncomingMessageFinder.m index 3c6161474..af28990de 100644 --- a/src/Storage/OWSIncomingMessageFinder.m +++ b/src/Storage/OWSIncomingMessageFinder.m @@ -20,7 +20,6 @@ NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMess @property (nonatomic, readonly) YapDatabase *database; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; -@property (nonatomic, readonly) BOOL isExtensionRegistered; @end @@ -93,12 +92,16 @@ NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMess - (void)asyncRegisterExtension { DDLogInfo(@"%@ registering async.", self.tag); - [self.database registerExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName]; + [self.database asyncRegisterExtension:self.indexExtension + withName:OWSIncomingMessageFinderExtensionName + completionBlock:^(BOOL ready) { + DDLogInfo(@"%@ finished registering async.", self.tag); + }]; } +// We should not normally hit this, as we should have prefer registering async, but it is useful for testing. - (void)registerExtension { - OWSAssert(NO); DDLogError(@"%@ registering SYNC. We should prefer async when possible.", self.tag); [self.database registerExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName]; } diff --git a/tests/Contacts/TSThreadTest.m b/tests/Contacts/TSThreadTest.m index 117e30ba6..4f21d770d 100644 --- a/tests/Contacts/TSThreadTest.m +++ b/tests/Contacts/TSThreadTest.m @@ -2,6 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // +#import "OWSDevice.h" #import "TSAttachmentStream.h" #import "TSContactThread.h" #import "TSIncomingMessage.h" @@ -40,7 +41,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:10000 inThread:thread authorId:@"fake-author-id" - sourceDeviceId:1 + sourceDeviceId:OWSDevicePrimaryDeviceId messageBody:@"Incoming message body"]; [incomingMessage save]; @@ -73,13 +74,13 @@ BOOL incomingFileWasCreated = [[NSFileManager defaultManager] fileExistsAtPath:[incomingAttachment filePath]]; XCTAssert(incomingFileWasCreated); - TSIncomingMessage *incomingMessage = - [[TSIncomingMessage alloc] initWithTimestamp:10000 - inThread:thread - authorId:@"fake-author-id" - sourceDeviceId:1 - messageBody:@"incoming message body" - attachmentIds:[NSMutableArray arrayWithObject:incomingAttachment.uniqueId]]; + TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:10000 + inThread:thread + authorId:@"fake-author-id" + sourceDeviceId:OWSDevicePrimaryDeviceId + messageBody:@"incoming message body" + attachmentIds:@[ incomingAttachment.uniqueId ] + expiresInSeconds:0]; [incomingMessage save]; TSAttachmentStream *outgoingAttachment = [[TSAttachmentStream alloc] initWithContentType:@"image/jpeg"]; @@ -90,11 +91,10 @@ BOOL outgoingFileWasCreated = [[NSFileManager defaultManager] fileExistsAtPath:[outgoingAttachment filePath]]; XCTAssert(outgoingFileWasCreated); - TSOutgoingMessage *outgoingMessage = - [[TSOutgoingMessage alloc] initWithTimestamp:10000 - inThread:thread - messageBody:@"outgoing message body" - attachmentIds:[NSMutableArray arrayWithObject:outgoingAttachment.uniqueId]]; + TSOutgoingMessage *outgoingMessage = [[TSOutgoingMessage alloc] initWithTimestamp:10000 + inThread:thread + messageBody:@"outgoing message body" + attachmentIds:@[ outgoingAttachment.uniqueId ]]; [outgoingMessage save]; // Sanity check diff --git a/tests/Messages/OWSIncomingMessageFinderTest.m b/tests/Messages/OWSIncomingMessageFinderTest.m new file mode 100644 index 000000000..ba859ad95 --- /dev/null +++ b/tests/Messages/OWSIncomingMessageFinderTest.m @@ -0,0 +1,105 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSDevice.h" +#import "OWSIncomingMessageFinder.h" +#import "TSContactThread.h" +#import "TSIncomingMessage.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSIncomingMessageFinder (Testing) + +- (void)registerExtension; + +@end + +@interface OWSIncomingMessageFinderTest : XCTestCase + +@property (nonatomic) NSString *sourceId; +@property (nonatomic) TSThread *thread; +@property (nonatomic) OWSIncomingMessageFinder *finder; + +@end + +@implementation OWSIncomingMessageFinderTest + +- (void)setUp +{ + [super setUp]; + self.sourceId = @"some-source-id"; + self.thread = [TSContactThread getOrCreateThreadWithContactId:self.sourceId]; + self.finder = [OWSIncomingMessageFinder new]; + [self.finder registerExtension]; +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExistingMessages +{ + + uint64_t timestamp = 1234; + BOOL result = [self.finder existsMessageWithTimestamp:timestamp + sourceId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId]; + + // Sanity check. + XCTAssertFalse(result); + + // Different timestamp + [[[TSIncomingMessage alloc] initWithTimestamp:timestamp + 1 + inThread:self.thread + authorId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId + messageBody:@"foo"] save]; + result = [self.finder existsMessageWithTimestamp:timestamp + sourceId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId]; + XCTAssertFalse(result); + + // Different authorId + [[[TSIncomingMessage alloc] initWithTimestamp:timestamp + inThread:self.thread + authorId:@"some-other-author-id" + sourceDeviceId:OWSDevicePrimaryDeviceId + messageBody:@"foo"] save]; + + result = [self.finder existsMessageWithTimestamp:timestamp + sourceId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId]; + XCTAssertFalse(result); + + // Different device + [[[TSIncomingMessage alloc] initWithTimestamp:timestamp + inThread:self.thread + authorId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId + 1 + messageBody:@"foo"] save]; + + result = [self.finder existsMessageWithTimestamp:timestamp + sourceId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId]; + XCTAssertFalse(result); + + // The real deal... + [[[TSIncomingMessage alloc] initWithTimestamp:timestamp + inThread:self.thread + authorId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId + messageBody:@"foo"] save]; + + result = [self.finder existsMessageWithTimestamp:timestamp + sourceId:self.sourceId + sourceDeviceId:OWSDevicePrimaryDeviceId]; + XCTAssertTrue(result); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/tests/Storage/OWSOrphanedDataCleanerTest.m b/tests/Storage/OWSOrphanedDataCleanerTest.m index 85c674fcc..432d9e1ea 100644 --- a/tests/Storage/OWSOrphanedDataCleanerTest.m +++ b/tests/Storage/OWSOrphanedDataCleanerTest.m @@ -2,6 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // +#import "OWSDevice.h" #import "OWSOrphanedDataCleaner.h" #import "TSAttachmentStream.h" #import "TSContactThread.h" @@ -47,7 +48,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:unsavedThread authorId:@"fake-author-id" - sourceDeviceId:1 + sourceDeviceId:OWSDevicePrimaryDeviceId messageBody:@"footch"]; [incomingMessage save]; XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); @@ -64,7 +65,7 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:savedThread authorId:@"fake-author-id" - sourceDeviceId:1 + sourceDeviceId:OWSDevicePrimaryDeviceId messageBody:@"footch"]; [incomingMessage save]; XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); @@ -103,9 +104,10 @@ TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1 inThread:savedThread authorId:@"fake-author-id" - sourceDeviceId:1 + sourceDeviceId:OWSDevicePrimaryDeviceId messageBody:@"footch" - attachmentIds:@[ attachmentStream.uniqueId ]]; + attachmentIds:@[ attachmentStream.uniqueId ] + expiresInSeconds:0]; [incomingMessage save]; NSString *attachmentFilePath = [attachmentStream filePath];