Merge branch 'mkirk/dedupe-incoming-messages'

This commit is contained in:
Michael Kirk 2017-02-24 19:18:15 -08:00
commit 168639597f
14 changed files with 393 additions and 73 deletions

View file

@ -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 = "<group>"; };
45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageTest.m; path = ../../../tests/Messages/Interactions/TSMessageTest.m; sourceTree = "<group>"; };
45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDeviceProvisionerTest.m; path = ../../../tests/Devices/OWSDeviceProvisionerTest.m; sourceTree = "<group>"; };
45E741B51E5D14E800735842 /* OWSIncomingMessageFinderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSIncomingMessageFinderTest.m; path = ../../../tests/Messages/OWSIncomingMessageFinderTest.m; sourceTree = "<group>"; };
6323E02A33682A8838FE3F27 /* OWSFingerprintTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFingerprintTest.m; path = ../../../tests/Security/OWSFingerprintTest.m; sourceTree = "<group>"; };
6323E3E540CF763D71DACB59 /* SignalRecipientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SignalRecipientTest.m; path = ../../tests/Contacts/SignalRecipientTest.m; sourceTree = "<group>"; };
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 = "<group>";
@ -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 */,

View file

@ -1,10 +1,14 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSYapDatabaseObject.h"
#import <Mantle/MTLJSONAdapter.h>
NS_ASSUME_NONNULL_BEGIN
extern uint32_t const OWSDevicePrimaryDeviceId;
@interface OWSDevice : TSYapDatabaseObject <MTLJSONSerializing>
@property (nonatomic, readonly) NSInteger deviceId;
@ -22,6 +26,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)replaceAll:(NSArray<OWSDevice *> *)devices;
/**
* The id of the device currently running this application
*/
+ (uint32_t)currentDeviceId;
/**
*
* @param transaction

View file

@ -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;
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;

View file

@ -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,30 +33,9 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
authorId:(NSString *)authorId
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 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
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds;
/**
* Inits an incoming group message that expires.
*
@ -64,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
@ -76,6 +59,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<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds NS_DESIGNATED_INITIALIZER;
@ -84,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 thread.
*/
- (instancetype)initWithTimestamp:(uint64_t)timestamp NS_UNAVAILABLE;
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_UNAVAILABLE;
@ -120,6 +104,9 @@ 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;
/*

View file

@ -22,28 +22,22 @@ 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:@[]];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
authorId:(NSString *)authorId
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
return [self initWithTimestamp:timestamp
inThread:thread
authorId:authorId
sourceDeviceId:sourceDeviceId
messageBody:body
attachmentIds:attachmentIds
attachmentIds:@[]
expiresInSeconds:0];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
authorId:(NSString *)authorId
sourceDeviceId:(uint32_t)sourceDeviceId
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
@ -60,6 +54,7 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
}
_authorId = authorId;
_sourceDeviceId = sourceDeviceId;
_read = NO;
OWSAssert(self.receivedAtDate);

View file

@ -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];

View file

@ -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,18 @@ 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
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 +305,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 +626,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 +648,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];
@ -648,25 +665,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
messageBody:body];
} else {
TSContactThread *cThread = (TSContactThread *)thread;
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:cThread
authorId:[cThread contactIdentifier]
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];
}
}

View file

@ -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

View file

@ -0,0 +1,159 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSIncomingMessageFinder.h"
#import "TSIncomingMessage.h"
#import "TSStorageManager.h"
#import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
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;
@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 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
{
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

View file

@ -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];

View file

@ -1,5 +1,8 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDevice.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
#import "TSIncomingMessage.h"
@ -38,6 +41,7 @@
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:10000
inThread:thread
authorId:@"fake-author-id"
sourceDeviceId:OWSDevicePrimaryDeviceId
messageBody:@"Incoming message body"];
[incomingMessage save];
@ -70,12 +74,13 @@
BOOL incomingFileWasCreated = [[NSFileManager defaultManager] fileExistsAtPath:[incomingAttachment filePath]];
XCTAssert(incomingFileWasCreated);
TSIncomingMessage *incomingMessage =
[[TSIncomingMessage alloc] initWithTimestamp:10000
inThread:thread
authorId:@"fake-author-id"
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"];
@ -86,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

View file

@ -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 <XCTest/XCTest.h>
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

View file

@ -1,5 +1,8 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDevice.h"
#import "OWSOrphanedDataCleaner.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
@ -45,6 +48,7 @@
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1
inThread:unsavedThread
authorId:@"fake-author-id"
sourceDeviceId:OWSDevicePrimaryDeviceId
messageBody:@"footch"];
[incomingMessage save];
XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]);
@ -61,6 +65,7 @@
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1
inThread:savedThread
authorId:@"fake-author-id"
sourceDeviceId:OWSDevicePrimaryDeviceId
messageBody:@"footch"];
[incomingMessage save];
XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]);
@ -99,8 +104,10 @@
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:1
inThread:savedThread
authorId:@"fake-author-id"
sourceDeviceId:OWSDevicePrimaryDeviceId
messageBody:@"footch"
attachmentIds:@[ attachmentStream.uniqueId ]];
attachmentIds:@[ attachmentStream.uniqueId ]
expiresInSeconds:0];
[incomingMessage save];
NSString *attachmentFilePath = [attachmentStream filePath];

View file

@ -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 <XCTest/XCTest.h>
@ -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];