disappearing messages

* Support for disappearing messages
* update inbox thread preview when receiving message

// FREEBIE
This commit is contained in:
Michael Kirk 2016-10-05 11:42:44 -04:00
parent c1ade86a8b
commit 40cdc7f224
57 changed files with 2879 additions and 252 deletions

View File

@ -9,8 +9,10 @@
/* Begin PBXBuildFile section */
308D7DFA789594CEA62740D9 /* libPods-TSKitiOSTestAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0DC1A83C39CBC09FB2405A3 /* libPods-TSKitiOSTestAppTests.a */; };
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */; };
450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */; };
452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6CE1D4A754C00E934BA /* TSThreadTest.m */; };
452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */; };
454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */; };
45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */; };
45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */; };
45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6E1CC342B600A02153 /* TSMessageStorageTests.m */; };
@ -21,6 +23,8 @@
45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B741CC342B600A02153 /* MessagePaddingTests.m */; };
459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */; };
45A856AC1D220BFF0056CD4D /* TSAttributesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */; };
45B700971D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */; };
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 */; };
51520592F83F2440F2DE4D67 /* libPods-TSKitiOSTestApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8362AB8E280E0F64352F08A /* libPods-TSKitiOSTestApp.a */; };
@ -49,8 +53,10 @@
31DFDA8F9523F5B15EA2376B /* Pods-TSKitiOSTestApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestApp/Pods-TSKitiOSTestApp.debug.xcconfig"; sourceTree = "<group>"; };
36DA6C703F99948D553F4E3F /* Pods-TSKitiOSTestAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestAppTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestAppTests/Pods-TSKitiOSTestAppTests.debug.xcconfig"; sourceTree = "<group>"; };
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesManagerTest.m; path = ../../../tests/Messages/TSMessagesManagerTest.m; sourceTree = "<group>"; };
450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesJobTest.m; path = ../../../tests/Messages/OWSDisappearingMessagesJobTest.m; sourceTree = "<group>"; };
452EE6CE1D4A754C00E934BA /* TSThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThreadTest.m; sourceTree = "<group>"; };
452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanedDataCleanerTest.m; sourceTree = "<group>"; };
454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessageFinderTest.m; path = ../../../tests/Messages/OWSDisappearingMessageFinderTest.m; sourceTree = "<group>"; };
45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPreKeyDeletionTests.m; sourceTree = "<group>"; };
45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentsTest.m; sourceTree = "<group>"; };
45458B6E1CC342B600A02153 /* TSMessageStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageStorageTests.m; sourceTree = "<group>"; };
@ -62,6 +68,8 @@
459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PhoneNumberTest.m; path = ../../../tests/Contacts/PhoneNumberTest.m; sourceTree = "<group>"; };
459FE0DA1D4AD49E00E1071A /* TSKitiOSTestApp-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TSKitiOSTestApp-Prefix.pch"; sourceTree = "<group>"; };
45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttributesTest.m; sourceTree = "<group>"; };
45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesConfigurationTest.m; path = ../../../tests/Contacts/OWSDisappearingMessagesConfigurationTest.m; sourceTree = "<group>"; };
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>"; };
6323E02A33682A8838FE3F27 /* OWSFingerprintTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFingerprintTest.m; path = ../../../tests/Security/OWSFingerprintTest.m; sourceTree = "<group>"; };
@ -158,6 +166,7 @@
children = (
459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */,
452EE6CE1D4A754C00E934BA /* TSThreadTest.m */,
45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */,
);
name = Contacts;
sourceTree = "<group>";
@ -165,8 +174,10 @@
45C6A0971D2F0254007D8AC0 /* Messages */ = {
isa = PBXGroup;
children = (
450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */,
45C6A0981D2F0264007D8AC0 /* Interactions */,
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */,
454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */,
);
name = Messages;
sourceTree = "<group>";
@ -183,6 +194,7 @@
isa = PBXGroup;
children = (
45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */,
45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */,
);
name = Devices;
sourceTree = "<group>";
@ -479,17 +491,21 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
45B840211D988DA100F9E938 /* OWSReadReceiptTest.m in Sources */,
45458B781CC342B600A02153 /* TSStorageIdentityKeyStoreTests.m in Sources */,
45B700971D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m in Sources */,
45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */,
45458B7B1CC342B600A02153 /* CryptographyTests.m in Sources */,
45D7243F1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m in Sources */,
45458B791CC342B600A02153 /* TSStoragePreKeyStoreTests.m in Sources */,
452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */,
450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */,
452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */,
45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */,
45C6A09A1D2F029B007D8AC0 /* TSMessageTest.m in Sources */,
459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */,
45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */,
454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */,
45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */,
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */,
45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */,

View File

@ -1,3 +1,9 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package signalservice;
option java_package = "org.whispersystems.signalservice.internal.push";
@ -32,19 +38,22 @@ message Content {
message DataMessage {
enum Flags {
END_SESSION = 1;
EXPIRATION_TIMER_UPDATE = 2;
}
optional string body = 1;
repeated AttachmentPointer attachments = 2;
optional GroupContext group = 3;
optional uint32 flags = 4;
optional uint32 expireTimer = 5;
}
message SyncMessage {
message Sent {
optional string destination = 1;
optional uint64 timestamp = 2;
optional DataMessage message = 3;
optional string destination = 1;
optional uint64 timestamp = 2;
optional DataMessage message = 3;
optional uint64 expirationStartTimestamp = 4;
}
message Contacts {
@ -55,11 +64,16 @@ message SyncMessage {
optional AttachmentPointer blob = 1;
}
message Blocked {
repeated string numbers = 1;
}
message Request {
enum Type {
UNKNOWN = 0;
CONTACTS = 1;
GROUPS = 2;
BLOCKED = 3;
}
optional Type type = 1;
@ -75,6 +89,7 @@ message SyncMessage {
optional Groups groups = 3;
optional Request request = 4;
repeated Read read = 5;
optional Blocked blocked = 6;
}
message AttachmentPointer {
@ -105,9 +120,10 @@ message ContactDetails {
optional uint32 length = 2;
}
optional string number = 1;
optional string name = 2;
optional Avatar avatar = 3;
optional string number = 1;
optional string name = 2;
optional Avatar avatar = 3;
optional string color = 4;
}
message GroupDetails {

View File

@ -0,0 +1,28 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSYapDatabaseObject.h"
NS_ASSUME_NONNULL_BEGIN
extern const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration;
@interface OWSDisappearingMessagesConfiguration : TSYapDatabaseObject
- (instancetype)initDefaultWithThreadId:(NSString *)threadId;
- (instancetype)initWithThreadId:(NSString *)threadId enabled:(BOOL)isEnabled durationSeconds:(uint32_t)seconds;
@property (nonatomic, getter=isEnabled) BOOL enabled;
@property (nonatomic) uint32_t durationSeconds;
@property (nonatomic, readonly) NSUInteger durationIndex;
@property (nonatomic, readonly) NSString *durationString;
@property (nonatomic, readonly) BOOL dictionaryValueDidChange;
@property (readonly, getter=isNewRecord) BOOL newRecord;
+ (NSArray<NSNumber *> *)validDurationsSeconds;
+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,171 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesConfiguration.h"
NS_ASSUME_NONNULL_BEGIN
const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = 60 * 60 * 24; // 1 day.
@interface OWSDisappearingMessagesConfiguration ()
// Transient record lifecycle attributes.
@property (atomic) NSDictionary *originalDictionaryValue;
@property (atomic, getter=isNewRecord) BOOL newRecord;
@end
@implementation OWSDisappearingMessagesConfiguration
- (instancetype)initDefaultWithThreadId:(NSString *)threadId
{
return [self initWithThreadId:threadId
enabled:NO
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
_originalDictionaryValue = [self dictionaryValue];
_newRecord = NO;
return self;
}
- (instancetype)initWithThreadId:(NSString *)threadId enabled:(BOOL)isEnabled durationSeconds:(uint32_t)seconds
{
self = [super initWithUniqueId:threadId];
if (!self) {
return self;
}
_enabled = isEnabled;
_durationSeconds = seconds;
_originalDictionaryValue = [NSDictionary new];
_newRecord = YES;
return self;
}
+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds
{
NSString *amountFormat;
uint32_t duration;
uint32_t secondsPerMinute = 60;
uint32_t secondsPerHour = secondsPerMinute * 60;
uint32_t secondsPerDay = secondsPerHour * 24;
uint32_t secondsPerWeek = secondsPerDay * 7;
if (durationSeconds < secondsPerMinute) { // XX Seconds
amountFormat = NSLocalizedString(@"SECONDS_TIME_AMOUNT",
@"{{number of seconds}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{5 seconds}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds;
} else if (durationSeconds < secondsPerMinute * 1.5) { // 1 Minute
amountFormat = NSLocalizedString(@"SINGLE_MINUTE_TIME_AMOUNT",
@"{{1 minute}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{1 minute}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerMinute;
} else if (durationSeconds < secondsPerHour) { // Multiple Minutes
amountFormat = NSLocalizedString(@"MINUTES_TIME_AMOUNT",
@"{{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerMinute;
} else if (durationSeconds < secondsPerHour * 1.5) { // 1 Hour
amountFormat = NSLocalizedString(@"SINGLE_HOUR_TIME_AMOUNT",
@"{{1 hour}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{1 hour}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerHour;
} else if (durationSeconds < secondsPerDay) { // Multiple Hours
amountFormat = NSLocalizedString(@"HOURS_TIME_AMOUNT",
@"{{number of hours}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{5 hours}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerHour;
} else if (durationSeconds < secondsPerDay * 1.5) { // 1 Day
amountFormat = NSLocalizedString(@"SINGLE_DAY_TIME_AMOUNT",
@"{{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{1 day}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerDay;
} else if (durationSeconds < secondsPerWeek) { // Multiple Days
amountFormat = NSLocalizedString(@"DAYS_TIME_AMOUNT",
@"{{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{5 days}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerDay;
} else if (durationSeconds < secondsPerWeek * 1.5) { // 1 Week
amountFormat = NSLocalizedString(@"SINGLE_WEEK_TIME_AMOUNT",
@"{{1 week}} embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{1 week}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerWeek;
} else { // Multiple weeks
amountFormat = NSLocalizedString(@"WEEKS_TIME_AMOUNT",
@"{{number of weeks}}, embedded in strings, e.g. 'Alice updated disappearing messages "
@"expiration to {{5 weeks}}'. See other *_TIME_AMOUNT strings");
duration = durationSeconds / secondsPerWeek;
}
return [NSString stringWithFormat:amountFormat, duration];
}
+ (NSArray<NSNumber *> *)validDurationsSeconds
{
return @[ @(5),
@(10),
@(30),
@(60),
@(300),
@(1800),
@(3600),
@(21600),
@(43200),
@(86400),
@(604800) ];
}
- (NSUInteger)durationIndex
{
return [[self.class validDurationsSeconds] indexOfObject:@(self.durationSeconds)];
}
- (NSString *)durationString
{
return [self.class stringForDurationSeconds:self.durationSeconds];
}
#pragma mark - Dirty Tracking
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey
{
// Don't persist transient properties
if ([propertyKey isEqualToString:@"originalDictionaryValue"]
||[propertyKey isEqualToString:@"newRecord"]) {
return MTLPropertyStorageNone;
} else {
return [super storageBehaviorForPropertyWithKey:propertyKey];
}
}
- (BOOL)dictionaryValueDidChange
{
return ![self.originalDictionaryValue isEqual:[self dictionaryValue]];
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[super saveWithTransaction:transaction];
self.originalDictionaryValue = [self dictionaryValue];
self.newRecord = NO;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -29,9 +29,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSString *)name;
/**
* @returns
* Signal Id (e164) of the contact if it's a contact thread.
*/
- (nullable NSString *)contactIdentifier;
#if TARGET_OS_IOS
/**
* Returns the image representing the thread. Nil if not available.
*
@ -59,6 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (BOOL)hasUnreadMessages;
- (void)markAllAsRead;
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**

View File

@ -180,18 +180,44 @@ NS_ASSUME_NONNULL_BEGIN
return hasUnread;
}
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
YapDatabaseViewTransaction *viewTransaction = [transaction ext:TSUnreadDatabaseViewExtensionName];
NSMutableArray *array = [NSMutableArray array];
[viewTransaction
- (NSArray<TSIncomingMessage *> *)unreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
{
NSMutableArray<TSIncomingMessage *> *messages = [NSMutableArray new];
[[transaction ext:TSUnreadDatabaseViewExtensionName]
enumerateRowsInGroup:self.uniqueId
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
[array addObject:object];
if (![object isKindOfClass:[TSIncomingMessage class]]) {
DDLogError(@"%@ Unexpected object in unread messages: %@", self.tag, object);
}
[messages addObject:(TSIncomingMessage *)object];
}];
for (TSIncomingMessage *message in array) {
[message markAsReadWithTransaction:transaction];
return [messages copy];
}
- (NSArray<TSIncomingMessage *> *)unreadMessages
{
__block NSArray<TSIncomingMessage *> *messages;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
messages = [self unreadMessagesWithTransaction:transaction];
}];
return messages;
}
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (TSIncomingMessage *message in [self unreadMessagesWithTransaction:transaction]) {
[message markAsReadLocallyWithTransaction:transaction];
}
}
- (void)markAllAsRead
{
for (TSIncomingMessage *message in [self unreadMessages]) {
[message markAsReadLocally];
}
}
@ -274,6 +300,18 @@ NS_ASSUME_NONNULL_BEGIN
[thread saveWithTransaction:transaction];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -74,9 +74,9 @@ NS_ASSUME_NONNULL_BEGIN
return false;
}
- (NSString *)name {
NSString *contactId = [self contactIdentifier];
return [[TextSecureKitEnv sharedEnv].contactsManager nameStringForPhoneIdentifier:contactId];
- (NSString *)name
{
return [[TextSecureKitEnv sharedEnv].contactsManager nameStringForPhoneIdentifier:self.contactIdentifier];
}
#if TARGET_OS_IPHONE

View File

@ -84,7 +84,7 @@
- (NSString *)name
{
return self.groupModel.groupName;
return self.groupModel.groupName ? self.groupModel.groupName : NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
}
@end

View File

@ -50,15 +50,14 @@ NSString *const OWSReadReceiptColumnSenderId = @"senderId";
return self;
}
+ (NSDictionary *)encodingBehaviorsByPropertyKey
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey
{
NSMutableDictionary *behaviorsByPropertyKey = [[super encodingBehaviorsByPropertyKey] mutableCopy];
// Don't persist transient properties used in validation.
behaviorsByPropertyKey[@"valid"] = @(MTLModelEncodingBehaviorExcluded);
behaviorsByPropertyKey[@"validationErrorMessages"] = @(MTLModelEncodingBehaviorExcluded);
return [behaviorsByPropertyKey copy];
// Don't store ephemeral properties.
if ([propertyKey isEqualToString:@"valid"] || [propertyKey isEqualToString:@"validationErrorMessages"]) {
return MTLPropertyStorageNone;
} else {
return [super storageBehaviorForPropertyWithKey:propertyKey];
}
}
+ (void)asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:(YapDatabase *)database
@ -122,6 +121,8 @@ NSString *const OWSReadReceiptColumnSenderId = @"senderId";
return foundReadReceipt;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];

View File

@ -5,14 +5,28 @@ NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosSyncMessageRead;
@class OWSReadReceipt;
@class TSIncomingMessage;
@class TSStorageManager;
extern NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification;
@interface OWSReadReceiptsProcessor : NSObject
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos;
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)incomingMessage;
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts NS_DESIGNATED_INITIALIZER;
/**
* Mark existing messages as read from the given received read receipts.
*/
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
storageManager:(TSStorageManager *)storageManager;
/**
* Mark a new message as read in the rare (but does happen!) case that we receive the read receipt before the message
* the read receipt refers to.
*/
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)incomingMessage
storageManager:(TSStorageManager *)storageManager;
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts
storageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (void)process;

View File

@ -1,6 +1,7 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSReadReceiptsProcessor.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSReadReceipt.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSContactThread.h"
@ -14,12 +15,15 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
@interface OWSReadReceiptsProcessor ()
@property (nonatomic, readonly) NSArray<OWSReadReceipt *> *readReceipts;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@end
@implementation OWSReadReceiptsProcessor
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts
storageManager:(TSStorageManager *)storageManager;
{
self = [super init];
if (!self) {
@ -27,11 +31,14 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
}
_readReceipts = [readReceipts copy];
_storageManager = storageManager;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
return self;
}
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
storageManager:(TSStorageManager *)storageManager
{
NSMutableArray<OWSReadReceipt *> *readReceipts = [NSMutableArray new];
for (OWSSignalServiceProtosSyncMessageRead *readReceiptProto in readReceiptProtos) {
@ -44,10 +51,10 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
}
}
return [self initWithReadReceipts:[readReceipts copy]];
return [self initWithReadReceipts:[readReceipts copy] storageManager:storageManager];
}
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)message
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)message storageManager:(TSStorageManager *)storageManager
{
// Only groupthread sets authorId, thus this crappy code.
// TODO ALL incoming messages should have an authorId.
@ -61,9 +68,10 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
OWSReadReceipt *readReceipt = [OWSReadReceipt firstWithSenderId:messageAuthorId timestamp:message.timestamp];
if (readReceipt) {
DDLogInfo(@"%@ Found prior read receipt for incoming message.", self.tag);
return [self initWithReadReceipts:@[ readReceipt ]];
return [self initWithReadReceipts:@[ readReceipt ] storageManager:storageManager];
} else {
return [self initWithReadReceipts:@[]];
// no-op
return [self initWithReadReceipts:@[] storageManager:storageManager];
}
}
@ -75,11 +83,13 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
[TSIncomingMessage findMessageWithAuthorId:readReceipt.senderId timestamp:readReceipt.timestamp];
if (message) {
[message markAsReadFromReadReceipt];
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:readReceipt.timestamp];
// If it was previously saved, no need to keep it around any longer.
[readReceipt remove];
[[NSNotificationCenter defaultCenter]
postNotificationName:OWSReadReceiptsProcessorMarkedMessageAsReadNotification
object:message];
} else {
DDLogDebug(@"%@ Received read receipt for an unkown message. Saving it for later.", self.tag);
[readReceipt save];

View File

@ -0,0 +1,19 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class OWSIncomingSentMessageTranscript;
@class TSMessagesManager;
@interface OWSRecordTranscriptJob : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
incomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSendtMessageTranscript NS_DESIGNATED_INITIALIZER;
- (void)run;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,94 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSRecordTranscriptJob.h"
#import "OWSAttachmentsProcessor.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSIncomingSentMessageTranscript.h"
#import "TSMessagesManager+sendMessages.h"
#import "TSOutgoingMessage.h"
#import "TSStorageManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSRecordTranscriptJob ()
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
@property (nonatomic, readonly) OWSIncomingSentMessageTranscript *incomingSendtMessageTranscript;
@end
@implementation OWSRecordTranscriptJob
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
incomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSendtMessageTranscript
{
self = [super init];
if (!self) {
return self;
}
_messagesManager = messagesManager;
_incomingSendtMessageTranscript = incomingSendtMessageTranscript;
return self;
}
- (void)run
{
OWSIncomingSentMessageTranscript *transcript = self.incomingSendtMessageTranscript;
DDLogDebug(@"%@ Recording transcript: %@", self.tag, transcript);
TSThread *thread = transcript.thread;
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:transcript.attachmentPointerProtos
timestamp:transcript.timestamp
relay:transcript.relay
avatarGroupId:transcript.groupId
inThread:thread
messagesManager:self.messagesManager];
// TODO group updates. Currently desktop doesn't support group updates, so not a problem yet.
TSOutgoingMessage *outgoingMessage = [[TSOutgoingMessage alloc] initWithTimestamp:transcript.timestamp
inThread:thread
messageBody:transcript.body
attachmentIds:attachmentsProcessor.attachmentIds
expiresInSeconds:transcript.expirationDuration
expireStartedAt:transcript.expirationStartedAt];
if (transcript.isExpirationTimerUpdate) {
[self.messagesManager becomeConsistentWithDisappearingConfigurationForMessage:outgoingMessage];
// early return to avoid saving an empty incoming message.
return;
}
[self.messagesManager handleMessageSentRemotely:outgoingMessage sentAt:transcript.expirationStartedAt];
[attachmentsProcessor fetchAttachmentsForMessageId:outgoingMessage.uniqueId];
// If there is an attachment + text, render the text here, as Signal-iOS renders two messages.
if (attachmentsProcessor.hasSupportedAttachments && transcript.body && ![transcript.body isEqualToString:@""]) {
// render text *after* the attachment
uint64_t textMessageTimestamp = transcript.timestamp + 1;
TSOutgoingMessage *textMessage = [[TSOutgoingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:thread
messageBody:transcript.body];
textMessage.messageState = TSOutgoingMessageStateDelivered;
[textMessage save];
}
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,13 +1,17 @@
// Created by Michael Kirk on 9/24/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSMessagesManager;
@class TSIncomingMessage;
@interface OWSReadReceiptObserver : NSObject
@interface OWSSendReadReceiptsJob : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
- (void)startObserving;
- (void)runWith:(TSIncomingMessage *)message;
@end

View File

@ -1,6 +1,7 @@
// Created by Michael Kirk on 9/24/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSReadReceiptObserver.h"
#import "OWSSendReadReceiptsJob.h"
#import "OWSReadReceipt.h"
#import "OWSReadReceiptsMessage.h"
#import "TSContactThread.h"
@ -9,7 +10,7 @@
NS_ASSUME_NONNULL_BEGIN
@interface OWSReadReceiptObserver ()
@interface OWSSendReadReceiptsJob ()
@property (atomic) NSMutableArray<OWSReadReceipt *> *readReceiptsQueue;
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
@ -17,18 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
@end
@implementation OWSReadReceiptObserver
@implementation OWSSendReadReceiptsJob
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)init
{
return [self initWithMessagesManager:[TSMessagesManager sharedManager]];
}
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
{
self = [super init];
@ -43,30 +39,10 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (void)startObserving
- (void)runWith:(TSIncomingMessage *)message
{
if (self.isObserving) {
return;
}
self.isObserving = true;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleReadNotification:)
name:TSIncomingMessageWasReadOnThisDeviceNotification
object:nil];
}
- (void)handleReadNotification:(NSNotification *)notification
{
if (![notification.object isKindOfClass:[TSIncomingMessage class]]) {
DDLogError(@"Read receipt notifier got unexpected object: %@", notification.object);
return;
}
TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
// Only groupthread sets authorId, thus this crappy code.
// TODO ALL incoming messages should have an authorId.
// TODO Refactor so that ALL incoming messages have an authorId.
NSString *messageAuthorId;
if (message.authorId) { // Group Thread
messageAuthorId = message.authorId;
@ -85,11 +61,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)sendAllReadReceiptsInQueue
{
// Synchronized so we don't lose any read receipts while replacing the queue
__block NSArray<OWSReadReceipt *> *receiptsToSend;
__block NSArray<OWSReadReceipt *> *_Nullable receiptsToSend;
@synchronized(self)
{
if (self.readReceiptsQueue.count > 0) {
receiptsToSend = [self.readReceiptsQueue copy];
receiptsToSend = self.readReceiptsQueue;
self.readReceiptsQueue = [NSMutableArray new];
}
}

View File

@ -3,6 +3,9 @@
NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosSyncMessageSent;
@class OWSSignalServiceProtosDataMessage;
@class OWSSignalServiceProtosAttachmentPointer;
@class TSThread;
/**
* Represents notification of a message sent on our behalf from another device.
@ -12,10 +15,18 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay;
/**
* Record an outgoing message based on the transcription
*/
- (void)record;
@property (nonatomic, readonly) NSString *relay;
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) uint64_t expirationStartedAt;
@property (nonatomic, readonly) uint32_t expirationDuration;
@property (nonatomic, readonly) TSThread *thread;
@property (nonatomic, readonly) BOOL isGroupUpdate;
@property (nonatomic, readonly) BOOL isExpirationTimerUpdate;
@property (nullable, nonatomic, readonly) NSData *groupId;
@property (nonatomic, readonly) NSString *body;
@property (nonatomic, readonly) NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
@end

View File

@ -14,15 +14,6 @@
NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingSentMessageTranscript ()
@property (nonatomic, readonly) NSString *relay;
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) uint64_t timestamp;
@end
@implementation OWSIncomingSentMessageTranscript
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay
@ -36,60 +27,31 @@ NS_ASSUME_NONNULL_BEGIN
_dataMessage = sentProto.message;
_recipientId = sentProto.destination;
_timestamp = sentProto.timestamp;
_expirationStartedAt = sentProto.expirationStartTimestamp;
_expirationDuration = sentProto.message.expireTimer;
_body = _dataMessage.body;
_groupId = _dataMessage.group.id;
_isGroupUpdate = _dataMessage.hasGroup && (_dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate);
_isExpirationTimerUpdate = (_dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0;
return self;
}
- (void)record
- (NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentPointerProtos
{
TSThread *thread;
if (self.isGroupUpdate && self.dataMessage.group.hasAvatar) {
return @[ self.dataMessage.group.avatar ];
} else {
return self.dataMessage.attachments;
}
}
- (TSThread *)thread
{
if (self.dataMessage.hasGroup) {
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:self.dataMessage.group.id];
return [TSGroupThread getOrCreateThreadWithGroupIdData:self.dataMessage.group.id];
} else {
thread = [TSContactThread getOrCreateThreadWithContactId:self.recipientId];
}
NSData *avatarGroupId;
NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
if (self.dataMessage.hasGroup && (self.dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate)) {
avatarGroupId = self.dataMessage.group.id;
attachmentPointerProtos = @[ self.dataMessage.group.avatar ];
} else {
attachmentPointerProtos = self.dataMessage.attachments;
}
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:attachmentPointerProtos
timestamp:self.timestamp
relay:self.relay
avatarGroupId:avatarGroupId
inThread:thread
messagesManager:[TSMessagesManager sharedManager]];
// TODO group updates. Currently desktop doesn't support group updates, so not a problem yet.
TSOutgoingMessage *outgoingMessage =
[[TSOutgoingMessage alloc] initWithTimestamp:self.timestamp
inThread:thread
messageBody:self.dataMessage.body
attachmentIds:attachmentsProcessor.attachmentIds];
outgoingMessage.messageState = TSOutgoingMessageStateDelivered;
[outgoingMessage save];
[attachmentsProcessor fetchAttachmentsForMessageId:outgoingMessage.uniqueId];
// If there is an attachment + text, render the text here, as Signal-iOS renders two messages.
if (attachmentsProcessor.hasSupportedAttachments && self.dataMessage.body != nil
&& ![self.dataMessage.body isEqualToString:@""]) {
// render text *after* the attachment
uint64_t textMessageTimestamp = self.timestamp + 1000;
TSOutgoingMessage *textMessage = [[TSOutgoingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:thread
messageBody:self.dataMessage.body];
textMessage.messageState = TSOutgoingMessageStateDelivered;
[textMessage save];
return [TSContactThread getOrCreateThreadWithContactId:self.recipientId];
}
}

View File

@ -45,6 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
[sentBuilder setTimestamp:self.message.timestamp];
[sentBuilder setDestination:self.message.recipientIdentifier];
[sentBuilder setMessage:[self.message buildDataMessage]];
[sentBuilder setExpirationStartTimestamp:self.message.timestamp];
[syncMessageBuilder setSentBuilder:sentBuilder];

View File

@ -0,0 +1,30 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSInfoMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class TSThread;
@class OWSDisappearingMessagesConfiguration;
@interface OWSDisappearingConfigurationUpdateInfoMessage : TSInfoMessage
/**
* When remote user updates configuration
*/
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
configuration:(OWSDisappearingMessagesConfiguration *)configuration
createdByRemoteName:(NSString *)name;
/**
* When local user updates configuration
*/
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
configuration:(OWSDisappearingMessagesConfiguration *)configuration;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,86 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingConfigurationUpdateInfoMessage ()
@property (nullable, nonatomic, readonly) NSString *createdByRemoteName;
@property (nonatomic, readonly) BOOL configurationIsEnabled;
@property (nonatomic, readonly) uint32_t configurationDurationSeconds;
@end
@implementation OWSDisappearingConfigurationUpdateInfoMessage
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
configuration:(OWSDisappearingMessagesConfiguration *)configuration
createdByRemoteName:(NSString *)name
{
self = [super initWithTimestamp:timestamp inThread:thread];
if (!self) {
return self;
}
_createdByRemoteName = name;
_configurationIsEnabled = configuration.isEnabled;
_configurationDurationSeconds = configuration.durationSeconds;
return self;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
configuration:(OWSDisappearingMessagesConfiguration *)configuration
{
self = [super initWithTimestamp:timestamp inThread:thread];
if (!self) {
return self;
}
_configurationIsEnabled = configuration.isEnabled;
_configurationDurationSeconds = configuration.durationSeconds;
return self;
}
- (NSString *)description
{
if (self.createdByRemoteName) {
if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) {
NSString *infoFormat = NSLocalizedString(@"OTHER_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION",
@"Info Message when {{other user}} updates message expiration to {{time amount}}, see the "
@"*_TIME_AMOUNT "
@"strings for context.");
NSString *durationString =
[OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds];
return [NSString stringWithFormat:infoFormat, self.createdByRemoteName, durationString];
} else {
NSString *infoFormat = NSLocalizedString(@"OTHER_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION",
@"Info Message when {{other user}} disables or doesn't support disappearing messages");
return [NSString stringWithFormat:infoFormat, self.createdByRemoteName];
}
} else { // Changed by local request
if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) {
NSString *infoFormat = NSLocalizedString(@"YOU_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION",
@"Info message embedding a {{time amount}}, see the *_TIME_AMOUNT strings for context.");
NSString *durationString =
[OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds];
return [NSString stringWithFormat:infoFormat, durationString];
} else {
return NSLocalizedString(@"YOU_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION",
@"Info Message when you disable disappearing messages");
}
}
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,16 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSOutgoingMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class OWSDisappearingMessagesConfiguration;
@interface OWSDisappearingMessagesConfigurationMessage : TSOutgoingMessage
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,54 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesConfigurationMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSSignalServiceProtos.pb.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesConfigurationMessage ()
@property (nonatomic, readonly) OWSDisappearingMessagesConfiguration *configuration;
@end
@implementation OWSDisappearingMessagesConfigurationMessage
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
// override superclass with no-op.
//
// There's no need to save this message, since it's not displayed to the user.
}
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread
{
self = [super initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread];
if (!self) {
return self;
}
_configuration = configuration;
return self;
}
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder
{
OWSSignalServiceProtosDataMessageBuilder *dataMessageBuilder = [super dataMessageBuilder];
[dataMessageBuilder setFlags:OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate];
if (self.configuration.isEnabled) {
[dataMessageBuilder setExpireTimer:self.configuration.durationSeconds];
} else {
[dataMessageBuilder setExpireTimer:0];
}
return dataMessageBuilder;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -16,6 +16,18 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
TSErrorMessageInvalidVersion,
};
-(instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
messageBody:(NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt NS_UNAVAILABLE;
+ (instancetype)corruptedMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;

View File

@ -14,11 +14,21 @@
@implementation TSErrorMessage
- (instancetype)initWithCoder:(NSCoder *)coder
{
return [super initWithCoder:coder];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType
{
self = [super initWithTimestamp:timestamp inThread:thread];
self = [super initWithTimestamp:timestamp
inThread:thread
messageBody:nil
attachmentIds:@[]
expiresInSeconds:0
expireStartedAt:0];
if (!self) {
return self;

View File

@ -90,6 +90,31 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds;
/**
* Inits an incoming group message that expires.
*
* @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, possibly an empty list.
* @param expiresInSeconds
* Seconds from when the message is read until it is deleted.
*
* @return initiated incoming group message
*/
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSGroupThread *)thread
authorId:(nullable NSString *)authorId
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds;
/*
* Find a message matching the senderId and timestamp, if any.
*
@ -109,7 +134,9 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
* Marks a message as having been read on this device (as opposed to responding to a remote read receipt).
*
*/
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)markAsReadLocally;
// TODO possible to remove?
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**
* Similar to markAsReadWithTransaction, but doesn't send out read receipts.

View File

@ -17,7 +17,42 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
inThread:(nullable TSContactThread *)thread
messageBody:(nullable NSString *)body
{
return [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[]];
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[]];
if (!self) {
return self;
}
// _authorId was nil for contact thread messages prior to 2.6.0
_authorId = [thread contactIdentifier];
_read = NO;
_receivedAt = [NSDate date];
return self;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
{
self = [self initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:expiresInSeconds
expireStartedAt:0];
if (!self) {
return self;
}
// _authorId was nil for contact thread messages prior to 2.6.0
_authorId = [thread contactIdentifier];
_read = NO;
_receivedAt = [NSDate date];
return self;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
@ -31,7 +66,8 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
return self;
}
_authorId = nil;
// _authorId was nil for contact thread messages prior to 2.6.0
_authorId = [thread contactIdentifier];
_read = NO;
_receivedAt = [NSDate date];
@ -52,7 +88,25 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:attachmentIds];
return [self initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:0];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSGroupThread *)thread
authorId:(nullable NSString *)authorId
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
{
self = [super initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:expiresInSeconds];
if (!self) {
return self;
@ -107,13 +161,24 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
}];
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[self markAsReadWithoutNotificationWithTransaction:transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
}
- (void)markAsReadLocally
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadWithoutNotificationWithTransaction:transaction];
}];
// Notification must happen outside of the transaction, else we'll likely crash when the notification receiver
// tries to do anything with the DB.
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
}
- (void)markAsReadWithoutNotificationWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
_read = YES;

View File

@ -16,11 +16,16 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
TSGroupMessageDeliver,
TSGroupMessageQuit
};
@interface TSMessage : TSInteraction
@property (nonatomic, readonly) NSMutableArray<NSString *> *attachmentIds;
@property (nullable, nonatomic) NSString *body;
@property (nonatomic) TSGroupMetaMessage groupMetaMessage;
@property (nonatomic) uint32_t expiresInSeconds;
@property (nonatomic) uint64_t expireStartedAt;
@property (nonatomic, readonly) uint64_t expiresAt;
@property (nonatomic, readonly) BOOL isExpiringMessage;
- (instancetype)initWithTimestamp:(uint64_t)timestamp;
@ -35,6 +40,21 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (BOOL)hasAttachments;
@end

View File

@ -2,12 +2,15 @@
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesJob.h"
#import "TSAttachment.h"
#import "TSThread.h"
#import <YapDatabase/YapDatabaseTransaction.h>
NS_ASSUME_NONNULL_BEGIN
static const NSUInteger OWSMessageSchemaVersion = 2;
static const NSUInteger OWSMessageSchemaVersion = 3;
@interface TSMessage ()
@ -55,6 +58,34 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
return [self initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:0];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
{
return [self initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:expiresInSeconds
expireStartedAt:0];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt
{
self = [super initWithTimestamp:timestamp inThread:thread];
@ -62,19 +93,30 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
return self;
}
_schemaVersion = OWSMessageSchemaVersion;
_body = body;
_attachmentIds = attachmentIds ? [attachmentIds mutableCopy] : [NSMutableArray new];
_expiresInSeconds = expiresInSeconds;
_expireStartedAt = expireStartedAt;
[self updateExpiresAt];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (_schemaVersion < 3) {
_expiresInSeconds = 0;
_expireStartedAt = 0;
_expiresAt = 0;
}
if (_schemaVersion < 2) {
// renamed _attachments to _attachmentIds
if (!_attachmentIds) {
@ -83,7 +125,7 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
}
if (!_attachmentIds) {
// used to allow nil _attachmentIds
// previously allowed nil _attachmentIds
_attachmentIds = [NSMutableArray new];
}
@ -91,6 +133,28 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
return self;
}
// Seconds.
- (void)setexpiresInSeconds:(uint32_t)expiresInSeconds
{
_expiresInSeconds = expiresInSeconds;
[self updateExpiresAt];
}
- (void)setExpireStartedAt:(uint64_t)expireStartedAt
{
_expireStartedAt = expireStartedAt;
[self updateExpiresAt];
}
// TODO a downloaded media doesn't start counting until download is complete.
- (void)updateExpiresAt
{
if (_expiresInSeconds > 0 && _expireStartedAt > 0) {
_expiresAt = _expireStartedAt + _expiresInSeconds * 1000;
} else {
_expiresAt = 0;
}
}
- (BOOL)hasAttachments
{
@ -125,10 +189,24 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[super removeWithTransaction:transaction];
for (NSString *attachmentId in self.attachmentIds) {
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction];
[attachment removeWithTransaction:transaction];
};
// Updates inbox thread preview
[self touchThreadWithTransaction:transaction];
}
- (void)touchThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[transaction touchObjectForKey:self.uniqueThreadId inCollection:[TSThread collection]];
}
- (BOOL)isExpiringMessage
{
return self.expiresInSeconds > 0;
}
@end

View File

@ -6,6 +6,7 @@
NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosAttachmentPointer;
@class OWSSignalServiceProtosDataMessageBuilder;
@interface TSOutgoingMessage : TSMessage
@ -29,6 +30,12 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
*/
- (NSData *)buildPlainTextData;
/**
* Intermediate protobuf representation
* Subclasses can augment if they want to manipulate the data message before building.
*/
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder;
/**
* Should this message be synced to the users other registered devices? This is
* generally always true, except in the case of the sync messages themseleves

View File

@ -2,6 +2,7 @@
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSOutgoingMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
@ -23,8 +24,23 @@ NS_ASSUME_NONNULL_BEGIN
messageBody:(nullable NSString *)body
attachmentIds:(NSMutableArray<NSString *> *)attachmentIds
{
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:attachmentIds];
return [self initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[] expiresInSeconds:0];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
attachmentIds:(NSMutableArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
self = [super initWithTimestamp:timestamp
inThread:thread
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:expiresInSeconds
expireStartedAt:now];
if (!self) {
return self;
}
@ -46,10 +62,9 @@ NS_ASSUME_NONNULL_BEGIN
return self.thread.contactIdentifier;
}
- (OWSSignalServiceProtosDataMessage *)buildDataMessage
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder
{
TSThread *thread = self.thread;
OWSSignalServiceProtosDataMessageBuilder *builder = [OWSSignalServiceProtosDataMessageBuilder new];
[builder setBody:self.body];
BOOL attachmentWasGroupAvatar = NO;
@ -87,7 +102,13 @@ NS_ASSUME_NONNULL_BEGIN
}
[builder setAttachmentsArray:attachments];
}
return [builder build];
[builder setExpireTimer:self.expiresInSeconds];
return builder;
}
- (OWSSignalServiceProtosDataMessage *)buildDataMessage
{
return [[self dataMessageBuilder] build];
}
- (NSData *)buildPlainTextData

View File

@ -0,0 +1,39 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class TSMessage;
@class TSThread;
@interface OWSDisappearingMessagesFinder : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
+ (instancetype)defaultInstance;
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block;
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block;
/**
* @return
* uint64_t millisecond timestamp wrapped in a number. Retrieve with `unsignedLongLongvalue`.
* or nil if there are no upcoming expired messages
*/
- (nullable NSNumber *)nextExpirationTimestamp;
/**
* Database extensions required for class to work.
*/
- (void)asyncRegisterDatabaseExtensions;
/**
* Only use the sync version for testing, generally we'll want to register extensions async
*/
- (void)blockingRegisterDatabaseExtensions;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,233 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesFinder.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSMessage.h"
#import "TSStorageManager.h"
#import "TSThread.h"
#import <YapDatabase/YapDatabaseConnection.h>
#import <YapDatabase/YapDatabaseQuery.h>
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
NS_ASSUME_NONNULL_BEGIN
static NSString *const OWSDisappearingMessageFinderThreadIdColumn = @"thread_id";
static NSString *const OWSDisappearingMessageFinderExpiresAtColumn = @"expires_at";
static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id";
@interface OWSDisappearingMessagesFinder ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end
@implementation OWSDisappearingMessagesFinder
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super init];
if (!self) {
return self;
}
_storageManager = storageManager;
_dbConnection = [storageManager newDatabaseConnection];
return self;
}
+ (instancetype)defaultInstance
{
static OWSDisappearingMessagesFinder *defaultInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultInstance = [[self alloc] initWithStorageManager:[TSStorageManager sharedManager]];
});
return defaultInstance;
}
- (NSArray<NSString *> *)fetchUnstartedExpiringMessageIdsInThread:(TSThread *)thread
{
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ = 0 AND %@ = \"%@\"",
OWSDisappearingMessageFinderExpiresAtColumn,
OWSDisappearingMessageFinderThreadIdColumn,
thread.uniqueId];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
}];
return [messageIds copy];
}
- (NSArray<NSString *> *)fetchExpiredMessageIds
{
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
uint64_t now = [NSDate ows_millisecondTimeStamp];
// When (expiresAt == 0) the message SHOULD NOT expire. Careful ;)
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 AND %@ <= %lld",
OWSDisappearingMessageFinderExpiresAtColumn,
OWSDisappearingMessageFinderExpiresAtColumn,
now];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
}];
return [messageIds copy];
}
- (nullable NSNumber *)nextExpirationTimestamp
{
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 ORDER BY %@ ASC",
OWSDisappearingMessageFinderExpiresAtColumn,
OWSDisappearingMessageFinderExpiresAtColumn];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
__block TSMessage *firstMessage;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysAndObjectsMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
firstMessage = (TSMessage *)object;
*stop = YES;
}];
}];
if (firstMessage && firstMessage.expiresAt > 0) {
return [NSNumber numberWithUnsignedLongLong:firstMessage.expiresAt];
}
return nil;
}
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block
{
for (NSString *expiringMessageId in [self fetchUnstartedExpiringMessageIdsInThread:thread]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId];
if ([message isKindOfClass:[TSMessage class]]) {
block(message);
} else {
DDLogError(@"%@ unexpected object: %@", self.tag, message);
}
}
}
/**
* Don't use this in production. Useful for testing.
* We don't want to instantiate potentially many messages at once.
*/
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread
{
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
[self enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
[messages addObject:message];
}];
return [messages copy];
}
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
{
// Since we can't directly mutate the enumerated expired messages, we store only their ids in hopes of saving a
// little memory and then enumerate the (larger) TSMessage objects one at a time.
for (NSString *expiredMessageId in [self fetchExpiredMessageIds]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiredMessageId];
if ([message isKindOfClass:[TSMessage class]]) {
block(message);
} else {
DDLogError(@"%@ unexpected object: %@", self.tag, message);
}
}
}
/**
* Don't use this in production. Useful for testing.
* We don't want to instantiate potentially many messages at once.
*/
- (NSArray<TSMessage *> *)fetchExpiredMessages
{
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
[self enumerateExpiredMessagesWithBlock:^(TSMessage *_Nonnull message) {
[messages addObject:message];
}];
return [messages copy];
}
#pragma mark - YapDatabaseExtension
- (YapDatabaseSecondaryIndex *)indexDatabaseExtension
{
YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new];
[setup addColumn:OWSDisappearingMessageFinderExpiresAtColumn withType:YapDatabaseSecondaryIndexTypeInteger];
[setup addColumn:OWSDisappearingMessageFinderThreadIdColumn withType:YapDatabaseSecondaryIndexTypeText];
YapDatabaseSecondaryIndexHandler *handler =
[YapDatabaseSecondaryIndexHandler withObjectBlock:^(YapDatabaseReadTransaction *transaction,
NSMutableDictionary *dict,
NSString *collection,
NSString *key,
id object) {
if ([object isKindOfClass:[TSMessage class]]) {
TSMessage *message = (TSMessage *)object;
if (message.expiresInSeconds > 0) {
dict[OWSDisappearingMessageFinderExpiresAtColumn] = @(message.expiresAt);
dict[OWSDisappearingMessageFinderThreadIdColumn] = message.uniqueThreadId;
} // else don't index non-expiring messages.
}
}];
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler];
}
// Useful for tests, don't use in app startup path because it's slow.
- (void)blockingRegisterDatabaseExtensions
{
[self.storageManager.database registerExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex];
}
- (void)asyncRegisterDatabaseExtensions
{
[self.storageManager.database asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogInfo(@"%@ completed registering extension async.", self.tag);
} else {
DDLogError(@"%@ failed registering extension async.", self.tag);
}
}];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,23 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class TSMessage;
@class TSThread;
@interface OWSDisappearingMessagesJob : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
- (void)run;
- (void)setExpirationsForThread:(TSThread *)thread;
- (void)setExpirationForMessage:(TSMessage *)message;
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt;
- (void)runBy:(uint64_t)millisecondTimestamp;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,171 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesJob.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesFinder.h"
#import "TSMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesJob ()
@property (nonatomic, readonly) OWSDisappearingMessagesFinder *disappearingMessagesFinder;
@property (atomic) uint64_t scheduledAt;
@end
@implementation OWSDisappearingMessagesJob
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super init];
if (!self) {
return self;
}
_scheduledAt = ULLONG_MAX;
_disappearingMessagesFinder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:storageManager];
return self;
}
- (void)run
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
__block uint expirationCount = 0;
[self.disappearingMessagesFinder enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
// sanity check
if (message.expiresAt > now) {
DDLogError(@"%@ Refusing to remove message which doesn't expire until: %lld", self.tag, message.expiresAt);
return;
}
DDLogDebug(@"%@ removing message which expired at: %lld", self.tag, message.expiresAt);
[message remove];
expirationCount++;
}];
DDLogDebug(@"%@ removed %u expired messages", self.tag, expirationCount);
}
- (void)runLoop
{
// allow next runAt to schedule.
self.scheduledAt = ULLONG_MAX;
[self run];
uint64_t now = [NSDate ows_millisecondTimeStamp];
NSNumber *nextExpirationTimestampNumber = [self.disappearingMessagesFinder nextExpirationTimestamp];
if (!nextExpirationTimestampNumber) {
// In theory we could kill the loop here. It should resume when the next expiring message is saved,
// But this is a safeguard for any race conditions that exist while running the job as a new message is saved.
unsigned int delaySeconds = (10 * 60); // 10 minutes.
DDLogDebug(
@"%@ No more expiring messages. Setting next check %u seconds into the future", self.tag, delaySeconds);
[self runBy:now + delaySeconds * 1000];
return;
}
uint64_t nextExpirationAt = [nextExpirationTimestampNumber unsignedLongLongValue];
uint64_t runByMilliseconds;
if (nextExpirationAt < now + 1000) {
DDLogWarn(@"%@ Next run requested at %llu, which is too soon. Delaying by 1 sec to prevent churn",
self.tag,
nextExpirationAt);
runByMilliseconds = now + 1000;
} else {
runByMilliseconds = nextExpirationAt;
}
DDLogVerbose(@"%@ Requesting next expiration to run by: %llu", self.tag, nextExpirationAt);
[self runBy:runByMilliseconds];
}
- (void)setExpirationForMessage:(TSMessage *)message
{
if (!message.isExpiringMessage) {
return;
}
OWSDisappearingMessagesConfiguration *disappearingConfig =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
if (!disappearingConfig.isEnabled) {
return;
}
[self setExpirationForMessage:message expirationStartedAt:[NSDate ows_millisecondTimeStamp]];
}
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{
if (!message.isExpiringMessage) {
return;
}
int startedSecondsAgo = [NSDate new].timeIntervalSince1970 - expirationStartedAt / 1000.0;
DDLogDebug(@"%@ Starting expiration for message read %d seconds ago", self.tag, startedSecondsAgo);
// Don't clobber if multiple actions simultaneously triggered expiration.
if (message.expireStartedAt == 0 || message.expireStartedAt > expirationStartedAt) {
message.expireStartedAt = expirationStartedAt;
[message save];
}
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration.
[self runBy:message.expiresAt];
}
- (void)setExpirationsForThread:(TSThread *)thread
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(@"%@ Starting expiring message which should have already "
@"been started.",
self.tag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message expirationStartedAt:now];
}];
}
- (void)runBy:(uint64_t)timestamp
{
// Prevent amplification.
if (timestamp >= self.scheduledAt) {
DDLogVerbose(@"%@ expiration already scheduled before %llu", self.tag, timestamp);
return;
}
// Update Schedule
DDLogVerbose(@"%@ Scheduled expiration run at %llu", self.tag, timestamp);
self.scheduledAt = timestamp;
uint64_t millisecondsDelay = timestamp - [NSDate ows_millisecondTimeStamp];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * millisecondsDelay),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[self runLoop];
});
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,19 @@
// Created by Michael Kirk on 9/24/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class TSMessagesManager;
@interface OWSIncomingMessageReadObserver : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
messagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
- (void)startObserving;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,82 @@
// Created by Michael Kirk on 9/24/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSIncomingMessageReadObserver.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSSendReadReceiptsJob.h"
#import "TSIncomingMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingMessageReadObserver ()
@property BOOL isObserving;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) OWSSendReadReceiptsJob *sendReadReceiptsJob;
@end
@implementation OWSIncomingMessageReadObserver
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
messagesManager:(TSMessagesManager *)messagesManager;
{
self = [super init];
if (!self) {
return self;
}
_isObserving = NO;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
_sendReadReceiptsJob = [[OWSSendReadReceiptsJob alloc] initWithMessagesManager:messagesManager];
return self;
}
- (void)startObserving
{
if (self.isObserving) {
return;
}
self.isObserving = true;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleLocalReadNotification:)
name:TSIncomingMessageWasReadOnThisDeviceNotification
object:nil];
}
- (void)handleLocalReadNotification:(NSNotification *)notification
{
if (![notification.object isKindOfClass:[TSIncomingMessage class]]) {
DDLogError(@"%@ Read receipt notifier got unexpected object: %@", self.tag, notification.object);
return;
}
TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
[self.disappearingMessagesJob setExpirationForMessage:message];
[self.sendReadReceiptsJob runWith:message];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,25 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class OWSDisappearingMessagesConfiguration;
@class TSMessagesManager;
@class TSThread;
@interface OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
thread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
+ (void)runWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
thread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager;
- (void)run;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,76 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h"
#import "OWSDisappearingMessagesConfigurationMessage.h"
#import "TSMessagesManager+sendMessages.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob ()
@property (nonatomic, readonly) OWSDisappearingMessagesConfiguration *configuration;
@property (nonatomic, readonly) TSMessagesManager *messageManager;
@property (nonatomic, readonly) TSThread *thread;
@end
@implementation OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
thread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager
{
self = [super init];
if (!self) {
return self;
}
_thread = thread;
_configuration = configuration;
_messageManager = messagesManager;
return self;
}
+ (void)runWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
thread:(TSThread *)thread
messagesManager:(TSMessagesManager *)messagesManager
{
OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob *job =
[[self alloc] initWithConfiguration:configuration thread:thread messagesManager:messagesManager];
[job run];
}
- (void)run
{
OWSDisappearingMessagesConfigurationMessage *message =
[[OWSDisappearingMessagesConfigurationMessage alloc] initWithConfiguration:self.configuration
thread:self.thread];
[self.messageManager sendMessage:message
inThread:self.thread
success:^{
DDLogDebug(
@"%@ Successfully notified %@ of new disappearing messages configuration", self.tag, self.thread);
}
failure:^{
DDLogError(@"%@ Failed to notify %@ of new disappearing messages configuration", self.tag, self.thread);
}];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -23,6 +23,8 @@
@class OWSSignalServiceProtosGroupDetailsAvatarBuilder;
@class OWSSignalServiceProtosGroupDetailsBuilder;
@class OWSSignalServiceProtosSyncMessage;
@class OWSSignalServiceProtosSyncMessageBlocked;
@class OWSSignalServiceProtosSyncMessageBlockedBuilder;
@class OWSSignalServiceProtosSyncMessageBuilder;
@class OWSSignalServiceProtosSyncMessageContacts;
@class OWSSignalServiceProtosSyncMessageContactsBuilder;
@ -93,6 +95,7 @@ NSString *NSStringFromOWSSignalServiceProtosEnvelopeType(OWSSignalServiceProtosE
typedef NS_ENUM(SInt32, OWSSignalServiceProtosDataMessageFlags) {
OWSSignalServiceProtosDataMessageFlagsEndSession = 1,
OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate = 2,
};
BOOL OWSSignalServiceProtosDataMessageFlagsIsValidValue(OWSSignalServiceProtosDataMessageFlags value);
@ -102,6 +105,7 @@ typedef NS_ENUM(SInt32, OWSSignalServiceProtosSyncMessageRequestType) {
OWSSignalServiceProtosSyncMessageRequestTypeUnknown = 0,
OWSSignalServiceProtosSyncMessageRequestTypeContacts = 1,
OWSSignalServiceProtosSyncMessageRequestTypeGroups = 2,
OWSSignalServiceProtosSyncMessageRequestTypeBlocked = 3,
};
BOOL OWSSignalServiceProtosSyncMessageRequestTypeIsValidValue(OWSSignalServiceProtosSyncMessageRequestType value);
@ -302,23 +306,28 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define DataMessage_attachments @"attachments"
#define DataMessage_group @"group"
#define DataMessage_flags @"flags"
#define DataMessage_expireTimer @"expireTimer"
@interface OWSSignalServiceProtosDataMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasBody_:1;
BOOL hasGroup_:1;
BOOL hasFlags_:1;
BOOL hasExpireTimer_:1;
NSString* body;
OWSSignalServiceProtosGroupContext* group;
UInt32 flags;
UInt32 expireTimer;
NSMutableArray * attachmentsArray;
}
- (BOOL) hasBody;
- (BOOL) hasGroup;
- (BOOL) hasFlags;
- (BOOL) hasExpireTimer;
@property (readonly, strong) NSString* body;
@property (readonly, strong) NSArray<OWSSignalServiceProtosAttachmentPointer*> * attachments;
@property (readonly, strong) OWSSignalServiceProtosGroupContext* group;
@property (readonly) UInt32 flags;
@property (readonly) UInt32 expireTimer;
- (OWSSignalServiceProtosAttachmentPointer*)attachmentsAtIndex:(NSUInteger)index;
+ (instancetype) defaultInstance;
@ -378,6 +387,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (UInt32) flags;
- (OWSSignalServiceProtosDataMessageBuilder*) setFlags:(UInt32) value;
- (OWSSignalServiceProtosDataMessageBuilder*) clearFlags;
- (BOOL) hasExpireTimer;
- (UInt32) expireTimer;
- (OWSSignalServiceProtosDataMessageBuilder*) setExpireTimer:(UInt32) value;
- (OWSSignalServiceProtosDataMessageBuilder*) clearExpireTimer;
@end
#define SyncMessage_sent @"sent"
@ -385,27 +399,32 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define SyncMessage_groups @"groups"
#define SyncMessage_request @"request"
#define SyncMessage_read @"read"
#define SyncMessage_blocked @"blocked"
@interface OWSSignalServiceProtosSyncMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasSent_:1;
BOOL hasContacts_:1;
BOOL hasGroups_:1;
BOOL hasRequest_:1;
BOOL hasBlocked_:1;
OWSSignalServiceProtosSyncMessageSent* sent;
OWSSignalServiceProtosSyncMessageContacts* contacts;
OWSSignalServiceProtosSyncMessageGroups* groups;
OWSSignalServiceProtosSyncMessageRequest* request;
OWSSignalServiceProtosSyncMessageBlocked* blocked;
NSMutableArray * readArray;
}
- (BOOL) hasSent;
- (BOOL) hasContacts;
- (BOOL) hasGroups;
- (BOOL) hasRequest;
- (BOOL) hasBlocked;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageSent* sent;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageContacts* contacts;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageGroups* groups;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageRequest* request;
@property (readonly, strong) NSArray<OWSSignalServiceProtosSyncMessageRead*> * read;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageBlocked* blocked;
- (OWSSignalServiceProtosSyncMessageRead*)readAtIndex:(NSUInteger)index;
+ (instancetype) defaultInstance;
@ -429,21 +448,26 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define Sent_destination @"destination"
#define Sent_timestamp @"timestamp"
#define Sent_message @"message"
#define Sent_expirationStartTimestamp @"expirationStartTimestamp"
@interface OWSSignalServiceProtosSyncMessageSent : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasTimestamp_:1;
BOOL hasExpirationStartTimestamp_:1;
BOOL hasDestination_:1;
BOOL hasMessage_:1;
UInt64 timestamp;
UInt64 expirationStartTimestamp;
NSString* destination;
OWSSignalServiceProtosDataMessage* message;
}
- (BOOL) hasDestination;
- (BOOL) hasTimestamp;
- (BOOL) hasMessage;
- (BOOL) hasExpirationStartTimestamp;
@property (readonly, strong) NSString* destination;
@property (readonly) UInt64 timestamp;
@property (readonly, strong) OWSSignalServiceProtosDataMessage* message;
@property (readonly) UInt64 expirationStartTimestamp;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
@ -496,6 +520,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setMessageBuilder:(OWSSignalServiceProtosDataMessageBuilder*) builderForValue;
- (OWSSignalServiceProtosSyncMessageSentBuilder*) mergeMessage:(OWSSignalServiceProtosDataMessage*) value;
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearMessage;
- (BOOL) hasExpirationStartTimestamp;
- (UInt64) expirationStartTimestamp;
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setExpirationStartTimestamp:(UInt64) value;
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearExpirationStartTimestamp;
@end
#define Contacts_blob @"blob"
@ -602,6 +631,56 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosSyncMessageGroupsBuilder*) clearBlob;
@end
#define Blocked_numbers @"numbers"
@interface OWSSignalServiceProtosSyncMessageBlocked : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
NSMutableArray * numbersArray;
}
@property (readonly, strong) NSArray * numbers;
- (NSString*)numbersAtIndex:(NSUInteger)index;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
- (BOOL) isInitialized;
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder;
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder;
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderWithPrototype:(OWSSignalServiceProtosSyncMessageBlocked*) prototype;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) toBuilder;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input;
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
@end
@interface OWSSignalServiceProtosSyncMessageBlockedBuilder : PBGeneratedMessageBuilder {
@private
OWSSignalServiceProtosSyncMessageBlocked* resultBlocked;
}
- (OWSSignalServiceProtosSyncMessageBlocked*) defaultInstance;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clear;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clone;
- (OWSSignalServiceProtosSyncMessageBlocked*) build;
- (OWSSignalServiceProtosSyncMessageBlocked*) buildPartial;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFrom:(OWSSignalServiceProtosSyncMessageBlocked*) other;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
- (NSMutableArray *)numbers;
- (NSString*)numbersAtIndex:(NSUInteger)index;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)addNumbers:(NSString*)value;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)setNumbersArray:(NSArray *)array;
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)clearNumbers;
@end
#define Request_type @"type"
@interface OWSSignalServiceProtosSyncMessageRequest : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
@ -762,6 +841,13 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosSyncMessageBuilder *)addRead:(OWSSignalServiceProtosSyncMessageRead*)value;
- (OWSSignalServiceProtosSyncMessageBuilder *)setReadArray:(NSArray<OWSSignalServiceProtosSyncMessageRead*> *)array;
- (OWSSignalServiceProtosSyncMessageBuilder *)clearRead;
- (BOOL) hasBlocked;
- (OWSSignalServiceProtosSyncMessageBlocked*) blocked;
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlockedBuilder:(OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderForValue;
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) clearBlocked;
@end
#define AttachmentPointer_id @"id"
@ -949,21 +1035,26 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define ContactDetails_number @"number"
#define ContactDetails_name @"name"
#define ContactDetails_avatar @"avatar"
#define ContactDetails_color @"color"
@interface OWSSignalServiceProtosContactDetails : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasNumber_:1;
BOOL hasName_:1;
BOOL hasColor_:1;
BOOL hasAvatar_:1;
NSString* number;
NSString* name;
NSString* color;
OWSSignalServiceProtosContactDetailsAvatar* avatar;
}
- (BOOL) hasNumber;
- (BOOL) hasName;
- (BOOL) hasAvatar;
- (BOOL) hasColor;
@property (readonly, strong) NSString* number;
@property (readonly, strong) NSString* name;
@property (readonly, strong) OWSSignalServiceProtosContactDetailsAvatar* avatar;
@property (readonly, strong) NSString* color;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
@ -1076,6 +1167,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosContactDetailsBuilder*) setAvatarBuilder:(OWSSignalServiceProtosContactDetailsAvatarBuilder*) builderForValue;
- (OWSSignalServiceProtosContactDetailsBuilder*) mergeAvatar:(OWSSignalServiceProtosContactDetailsAvatar*) value;
- (OWSSignalServiceProtosContactDetailsBuilder*) clearAvatar;
- (BOOL) hasColor;
- (NSString*) color;
- (OWSSignalServiceProtosContactDetailsBuilder*) setColor:(NSString*) value;
- (OWSSignalServiceProtosContactDetailsBuilder*) clearColor;
@end
#define GroupDetails_id @"id"

View File

@ -863,6 +863,7 @@ static OWSSignalServiceProtosContent* defaultOWSSignalServiceProtosContentInstan
@property (strong) NSMutableArray<OWSSignalServiceProtosAttachmentPointer*> * attachmentsArray;
@property (strong) OWSSignalServiceProtosGroupContext* group;
@property UInt32 flags;
@property UInt32 expireTimer;
@end
@implementation OWSSignalServiceProtosDataMessage
@ -890,11 +891,19 @@ static OWSSignalServiceProtosContent* defaultOWSSignalServiceProtosContentInstan
hasFlags_ = !!_value_;
}
@synthesize flags;
- (BOOL) hasExpireTimer {
return !!hasExpireTimer_;
}
- (void) setHasExpireTimer:(BOOL) _value_ {
hasExpireTimer_ = !!_value_;
}
@synthesize expireTimer;
- (instancetype) init {
if ((self = [super init])) {
self.body = @"";
self.group = [OWSSignalServiceProtosGroupContext defaultInstance];
self.flags = 0;
self.expireTimer = 0;
}
return self;
}
@ -932,6 +941,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
if (self.hasFlags) {
[output writeUInt32:4 value:self.flags];
}
if (self.hasExpireTimer) {
[output writeUInt32:5 value:self.expireTimer];
}
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
@ -953,6 +965,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
if (self.hasFlags) {
size_ += computeUInt32Size(4, self.flags);
}
if (self.hasExpireTimer) {
size_ += computeUInt32Size(5, self.expireTimer);
}
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
@ -1006,6 +1021,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
if (self.hasFlags) {
[output appendFormat:@"%@%@: %@\n", indent, @"flags", [NSNumber numberWithInteger:self.flags]];
}
if (self.hasExpireTimer) {
[output appendFormat:@"%@%@: %@\n", indent, @"expireTimer", [NSNumber numberWithInteger:self.expireTimer]];
}
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
@ -1025,6 +1043,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
if (self.hasFlags) {
[dictionary setObject: [NSNumber numberWithInteger:self.flags] forKey: @"flags"];
}
if (self.hasExpireTimer) {
[dictionary setObject: [NSNumber numberWithInteger:self.expireTimer] forKey: @"expireTimer"];
}
[self.unknownFields storeInDictionary:dictionary];
}
- (BOOL) isEqual:(id)other {
@ -1043,6 +1064,8 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
(!self.hasGroup || [self.group isEqual:otherMessage.group]) &&
self.hasFlags == otherMessage.hasFlags &&
(!self.hasFlags || self.flags == otherMessage.flags) &&
self.hasExpireTimer == otherMessage.hasExpireTimer &&
(!self.hasExpireTimer || self.expireTimer == otherMessage.expireTimer) &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
@ -1059,6 +1082,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
if (self.hasFlags) {
hashCode = hashCode * 31 + [[NSNumber numberWithInteger:self.flags] hash];
}
if (self.hasExpireTimer) {
hashCode = hashCode * 31 + [[NSNumber numberWithInteger:self.expireTimer] hash];
}
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@ -1067,6 +1093,7 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
BOOL OWSSignalServiceProtosDataMessageFlagsIsValidValue(OWSSignalServiceProtosDataMessageFlags value) {
switch (value) {
case OWSSignalServiceProtosDataMessageFlagsEndSession:
case OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate:
return YES;
default:
return NO;
@ -1076,6 +1103,8 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
switch (value) {
case OWSSignalServiceProtosDataMessageFlagsEndSession:
return @"OWSSignalServiceProtosDataMessageFlagsEndSession";
case OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate:
return @"OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate";
default:
return nil;
}
@ -1135,6 +1164,9 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
if (other.hasFlags) {
[self setFlags:other.flags];
}
if (other.hasExpireTimer) {
[self setExpireTimer:other.expireTimer];
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
@ -1179,6 +1211,10 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
[self setFlags:[input readUInt32]];
break;
}
case 40: {
[self setExpireTimer:[input readUInt32]];
break;
}
}
}
}
@ -1265,6 +1301,22 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
resultDataMessage.flags = 0;
return self;
}
- (BOOL) hasExpireTimer {
return resultDataMessage.hasExpireTimer;
}
- (UInt32) expireTimer {
return resultDataMessage.expireTimer;
}
- (OWSSignalServiceProtosDataMessageBuilder*) setExpireTimer:(UInt32) value {
resultDataMessage.hasExpireTimer = YES;
resultDataMessage.expireTimer = value;
return self;
}
- (OWSSignalServiceProtosDataMessageBuilder*) clearExpireTimer {
resultDataMessage.hasExpireTimer = NO;
resultDataMessage.expireTimer = 0;
return self;
}
@end
@interface OWSSignalServiceProtosSyncMessage ()
@ -1273,6 +1325,7 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
@property (strong) OWSSignalServiceProtosSyncMessageGroups* groups;
@property (strong) OWSSignalServiceProtosSyncMessageRequest* request;
@property (strong) NSMutableArray<OWSSignalServiceProtosSyncMessageRead*> * readArray;
@property (strong) OWSSignalServiceProtosSyncMessageBlocked* blocked;
@end
@implementation OWSSignalServiceProtosSyncMessage
@ -1307,12 +1360,20 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
@synthesize request;
@synthesize readArray;
@dynamic read;
- (BOOL) hasBlocked {
return !!hasBlocked_;
}
- (void) setHasBlocked:(BOOL) _value_ {
hasBlocked_ = !!_value_;
}
@synthesize blocked;
- (instancetype) init {
if ((self = [super init])) {
self.sent = [OWSSignalServiceProtosSyncMessageSent defaultInstance];
self.contacts = [OWSSignalServiceProtosSyncMessageContacts defaultInstance];
self.groups = [OWSSignalServiceProtosSyncMessageGroups defaultInstance];
self.request = [OWSSignalServiceProtosSyncMessageRequest defaultInstance];
self.blocked = [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
}
return self;
}
@ -1353,6 +1414,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
[output writeMessage:5 value:element];
}];
if (self.hasBlocked) {
[output writeMessage:6 value:self.blocked];
}
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
@ -1377,6 +1441,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
size_ += computeMessageSize(5, element);
}];
if (self.hasBlocked) {
size_ += computeMessageSize(6, self.blocked);
}
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
@ -1442,6 +1509,12 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
[output appendFormat:@"%@}\n", indent];
}];
if (self.hasBlocked) {
[output appendFormat:@"%@%@ {\n", indent, @"blocked"];
[self.blocked writeDescriptionTo:output
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
[output appendFormat:@"%@}\n", indent];
}
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
@ -1470,6 +1543,11 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
[element storeInDictionary:elementDictionary];
[dictionary setObject:[NSDictionary dictionaryWithDictionary:elementDictionary] forKey:@"read"];
}
if (self.hasBlocked) {
NSMutableDictionary *messageDictionary = [NSMutableDictionary dictionary];
[self.blocked storeInDictionary:messageDictionary];
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"blocked"];
}
[self.unknownFields storeInDictionary:dictionary];
}
- (BOOL) isEqual:(id)other {
@ -1490,6 +1568,8 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
self.hasRequest == otherMessage.hasRequest &&
(!self.hasRequest || [self.request isEqual:otherMessage.request]) &&
[self.readArray isEqualToArray:otherMessage.readArray] &&
self.hasBlocked == otherMessage.hasBlocked &&
(!self.hasBlocked || [self.blocked isEqual:otherMessage.blocked]) &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
@ -1509,6 +1589,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
hashCode = hashCode * 31 + [element hash];
}];
if (self.hasBlocked) {
hashCode = hashCode * 31 + [self.blocked hash];
}
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@ -1518,6 +1601,7 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
@property (strong) NSString* destination;
@property UInt64 timestamp;
@property (strong) OWSSignalServiceProtosDataMessage* message;
@property UInt64 expirationStartTimestamp;
@end
@implementation OWSSignalServiceProtosSyncMessageSent
@ -1543,11 +1627,19 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
hasMessage_ = !!_value_;
}
@synthesize message;
- (BOOL) hasExpirationStartTimestamp {
return !!hasExpirationStartTimestamp_;
}
- (void) setHasExpirationStartTimestamp:(BOOL) _value_ {
hasExpirationStartTimestamp_ = !!_value_;
}
@synthesize expirationStartTimestamp;
- (instancetype) init {
if ((self = [super init])) {
self.destination = @"";
self.timestamp = 0L;
self.message = [OWSSignalServiceProtosDataMessage defaultInstance];
self.expirationStartTimestamp = 0L;
}
return self;
}
@ -1576,6 +1668,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
if (self.hasMessage) {
[output writeMessage:3 value:self.message];
}
if (self.hasExpirationStartTimestamp) {
[output writeUInt64:4 value:self.expirationStartTimestamp];
}
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
@ -1594,6 +1689,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
if (self.hasMessage) {
size_ += computeMessageSize(3, self.message);
}
if (self.hasExpirationStartTimestamp) {
size_ += computeUInt64Size(4, self.expirationStartTimestamp);
}
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
@ -1641,6 +1739,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
[output appendFormat:@"%@}\n", indent];
}
if (self.hasExpirationStartTimestamp) {
[output appendFormat:@"%@%@: %@\n", indent, @"expirationStartTimestamp", [NSNumber numberWithLongLong:self.expirationStartTimestamp]];
}
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
@ -1655,6 +1756,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
[self.message storeInDictionary:messageDictionary];
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"message"];
}
if (self.hasExpirationStartTimestamp) {
[dictionary setObject: [NSNumber numberWithLongLong:self.expirationStartTimestamp] forKey: @"expirationStartTimestamp"];
}
[self.unknownFields storeInDictionary:dictionary];
}
- (BOOL) isEqual:(id)other {
@ -1672,6 +1776,8 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
(!self.hasTimestamp || self.timestamp == otherMessage.timestamp) &&
self.hasMessage == otherMessage.hasMessage &&
(!self.hasMessage || [self.message isEqual:otherMessage.message]) &&
self.hasExpirationStartTimestamp == otherMessage.hasExpirationStartTimestamp &&
(!self.hasExpirationStartTimestamp || self.expirationStartTimestamp == otherMessage.expirationStartTimestamp) &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
@ -1685,6 +1791,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
if (self.hasMessage) {
hashCode = hashCode * 31 + [self.message hash];
}
if (self.hasExpirationStartTimestamp) {
hashCode = hashCode * 31 + [[NSNumber numberWithLongLong:self.expirationStartTimestamp] hash];
}
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@ -1737,6 +1846,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
if (other.hasMessage) {
[self mergeMessage:other.message];
}
if (other.hasExpirationStartTimestamp) {
[self setExpirationStartTimestamp:other.expirationStartTimestamp];
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
@ -1775,6 +1887,10 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
[self setMessage:[subBuilder buildPartial]];
break;
}
case 32: {
[self setExpirationStartTimestamp:[input readUInt64]];
break;
}
}
}
}
@ -1840,6 +1956,22 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
resultSent.message = [OWSSignalServiceProtosDataMessage defaultInstance];
return self;
}
- (BOOL) hasExpirationStartTimestamp {
return resultSent.hasExpirationStartTimestamp;
}
- (UInt64) expirationStartTimestamp {
return resultSent.expirationStartTimestamp;
}
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setExpirationStartTimestamp:(UInt64) value {
resultSent.hasExpirationStartTimestamp = YES;
resultSent.expirationStartTimestamp = value;
return self;
}
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearExpirationStartTimestamp {
resultSent.hasExpirationStartTimestamp = NO;
resultSent.expirationStartTimestamp = 0L;
return self;
}
@end
@interface OWSSignalServiceProtosSyncMessageContacts ()
@ -2302,6 +2434,224 @@ static OWSSignalServiceProtosSyncMessageGroups* defaultOWSSignalServiceProtosSyn
}
@end
@interface OWSSignalServiceProtosSyncMessageBlocked ()
@property (strong) NSMutableArray * numbersArray;
@end
@implementation OWSSignalServiceProtosSyncMessageBlocked
@synthesize numbersArray;
@dynamic numbers;
- (instancetype) init {
if ((self = [super init])) {
}
return self;
}
static OWSSignalServiceProtosSyncMessageBlocked* defaultOWSSignalServiceProtosSyncMessageBlockedInstance = nil;
+ (void) initialize {
if (self == [OWSSignalServiceProtosSyncMessageBlocked class]) {
defaultOWSSignalServiceProtosSyncMessageBlockedInstance = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
}
}
+ (instancetype) defaultInstance {
return defaultOWSSignalServiceProtosSyncMessageBlockedInstance;
}
- (instancetype) defaultInstance {
return defaultOWSSignalServiceProtosSyncMessageBlockedInstance;
}
- (NSArray *)numbers {
return numbersArray;
}
- (NSString*)numbersAtIndex:(NSUInteger)index {
return [numbersArray objectAtIndex:index];
}
- (BOOL) isInitialized {
return YES;
}
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output {
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
[output writeString:1 value:element];
}];
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
__block SInt32 size_ = memoizedSerializedSize;
if (size_ != -1) {
return size_;
}
size_ = 0;
{
__block SInt32 dataSize = 0;
const NSUInteger count = self.numbersArray.count;
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
dataSize += computeStringSizeNoTag(element);
}];
size_ += dataSize;
size_ += (SInt32)(1 * count);
}
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromData:data] build];
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromData:data extensionRegistry:extensionRegistry] build];
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromInputStream:input] build];
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromCodedInputStream:input] build];
}
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromCodedInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder {
return [[OWSSignalServiceProtosSyncMessageBlockedBuilder alloc] init];
}
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderWithPrototype:(OWSSignalServiceProtosSyncMessageBlocked*) prototype {
return [[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFrom:prototype];
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder {
return [OWSSignalServiceProtosSyncMessageBlocked builder];
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) toBuilder {
return [OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:self];
}
- (void) writeDescriptionTo:(NSMutableString*) output withIndent:(NSString*) indent {
[self.numbersArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[output appendFormat:@"%@%@: %@\n", indent, @"numbers", obj];
}];
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
[dictionary setObject:self.numbers forKey: @"numbers"];
[self.unknownFields storeInDictionary:dictionary];
}
- (BOOL) isEqual:(id)other {
if (other == self) {
return YES;
}
if (![other isKindOfClass:[OWSSignalServiceProtosSyncMessageBlocked class]]) {
return NO;
}
OWSSignalServiceProtosSyncMessageBlocked *otherMessage = other;
return
[self.numbersArray isEqualToArray:otherMessage.numbersArray] &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
__block NSUInteger hashCode = 7;
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
hashCode = hashCode * 31 + [element hash];
}];
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@end
@interface OWSSignalServiceProtosSyncMessageBlockedBuilder()
@property (strong) OWSSignalServiceProtosSyncMessageBlocked* resultBlocked;
@end
@implementation OWSSignalServiceProtosSyncMessageBlockedBuilder
@synthesize resultBlocked;
- (instancetype) init {
if ((self = [super init])) {
self.resultBlocked = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
}
return self;
}
- (PBGeneratedMessage*) internalGetResult {
return resultBlocked;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clear {
self.resultBlocked = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
return self;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clone {
return [OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:resultBlocked];
}
- (OWSSignalServiceProtosSyncMessageBlocked*) defaultInstance {
return [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
}
- (OWSSignalServiceProtosSyncMessageBlocked*) build {
[self checkInitialized];
return [self buildPartial];
}
- (OWSSignalServiceProtosSyncMessageBlocked*) buildPartial {
OWSSignalServiceProtosSyncMessageBlocked* returnMe = resultBlocked;
self.resultBlocked = nil;
return returnMe;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFrom:(OWSSignalServiceProtosSyncMessageBlocked*) other {
if (other == [OWSSignalServiceProtosSyncMessageBlocked defaultInstance]) {
return self;
}
if (other.numbersArray.count > 0) {
if (resultBlocked.numbersArray == nil) {
resultBlocked.numbersArray = [[NSMutableArray alloc] initWithArray:other.numbersArray];
} else {
[resultBlocked.numbersArray addObjectsFromArray:other.numbersArray];
}
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input {
return [self mergeFromCodedInputStream:input extensionRegistry:[PBExtensionRegistry emptyRegistry]];
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
PBUnknownFieldSetBuilder* unknownFields = [PBUnknownFieldSet builderWithUnknownFields:self.unknownFields];
while (YES) {
SInt32 tag = [input readTag];
switch (tag) {
case 0:
[self setUnknownFields:[unknownFields build]];
return self;
default: {
if (![self parseUnknownField:input unknownFields:unknownFields extensionRegistry:extensionRegistry tag:tag]) {
[self setUnknownFields:[unknownFields build]];
return self;
}
break;
}
case 10: {
[self addNumbers:[input readString]];
break;
}
}
}
}
- (NSMutableArray *)numbers {
return resultBlocked.numbersArray;
}
- (NSString*)numbersAtIndex:(NSUInteger)index {
return [resultBlocked numbersAtIndex:index];
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)addNumbers:(NSString*)value {
if (resultBlocked.numbersArray == nil) {
resultBlocked.numbersArray = [[NSMutableArray alloc]init];
}
[resultBlocked.numbersArray addObject:value];
return self;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)setNumbersArray:(NSArray *)array {
resultBlocked.numbersArray = [[NSMutableArray alloc] initWithArray:array];
return self;
}
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)clearNumbers {
resultBlocked.numbersArray = nil;
return self;
}
@end
@interface OWSSignalServiceProtosSyncMessageRequest ()
@property OWSSignalServiceProtosSyncMessageRequestType type;
@end
@ -2426,6 +2776,7 @@ BOOL OWSSignalServiceProtosSyncMessageRequestTypeIsValidValue(OWSSignalServicePr
case OWSSignalServiceProtosSyncMessageRequestTypeUnknown:
case OWSSignalServiceProtosSyncMessageRequestTypeContacts:
case OWSSignalServiceProtosSyncMessageRequestTypeGroups:
case OWSSignalServiceProtosSyncMessageRequestTypeBlocked:
return YES;
default:
return NO;
@ -2439,6 +2790,8 @@ NSString *NSStringFromOWSSignalServiceProtosSyncMessageRequestType(OWSSignalServ
return @"OWSSignalServiceProtosSyncMessageRequestTypeContacts";
case OWSSignalServiceProtosSyncMessageRequestTypeGroups:
return @"OWSSignalServiceProtosSyncMessageRequestTypeGroups";
case OWSSignalServiceProtosSyncMessageRequestTypeBlocked:
return @"OWSSignalServiceProtosSyncMessageRequestTypeBlocked";
default:
return nil;
}
@ -2848,6 +3201,9 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
[resultSyncMessage.readArray addObjectsFromArray:other.readArray];
}
}
if (other.hasBlocked) {
[self mergeBlocked:other.blocked];
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
@ -2911,6 +3267,15 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
[self addRead:[subBuilder buildPartial]];
break;
}
case 50: {
OWSSignalServiceProtosSyncMessageBlockedBuilder* subBuilder = [OWSSignalServiceProtosSyncMessageBlocked builder];
if (self.hasBlocked) {
[subBuilder mergeFrom:self.blocked];
}
[input readMessage:subBuilder extensionRegistry:extensionRegistry];
[self setBlocked:[subBuilder buildPartial]];
break;
}
}
}
}
@ -3055,6 +3420,36 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
resultSyncMessage.readArray = nil;
return self;
}
- (BOOL) hasBlocked {
return resultSyncMessage.hasBlocked;
}
- (OWSSignalServiceProtosSyncMessageBlocked*) blocked {
return resultSyncMessage.blocked;
}
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value {
resultSyncMessage.hasBlocked = YES;
resultSyncMessage.blocked = value;
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlockedBuilder:(OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderForValue {
return [self setBlocked:[builderForValue build]];
}
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value {
if (resultSyncMessage.hasBlocked &&
resultSyncMessage.blocked != [OWSSignalServiceProtosSyncMessageBlocked defaultInstance]) {
resultSyncMessage.blocked =
[[[OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:resultSyncMessage.blocked] mergeFrom:value] buildPartial];
} else {
resultSyncMessage.blocked = value;
}
resultSyncMessage.hasBlocked = YES;
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder*) clearBlocked {
resultSyncMessage.hasBlocked = NO;
resultSyncMessage.blocked = [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
return self;
}
@end
@interface OWSSignalServiceProtosAttachmentPointer ()
@ -3932,6 +4327,7 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
@property (strong) NSString* number;
@property (strong) NSString* name;
@property (strong) OWSSignalServiceProtosContactDetailsAvatar* avatar;
@property (strong) NSString* color;
@end
@implementation OWSSignalServiceProtosContactDetails
@ -3957,11 +4353,19 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
hasAvatar_ = !!_value_;
}
@synthesize avatar;
- (BOOL) hasColor {
return !!hasColor_;
}
- (void) setHasColor:(BOOL) _value_ {
hasColor_ = !!_value_;
}
@synthesize color;
- (instancetype) init {
if ((self = [super init])) {
self.number = @"";
self.name = @"";
self.avatar = [OWSSignalServiceProtosContactDetailsAvatar defaultInstance];
self.color = @"";
}
return self;
}
@ -3990,6 +4394,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
if (self.hasAvatar) {
[output writeMessage:3 value:self.avatar];
}
if (self.hasColor) {
[output writeString:4 value:self.color];
}
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
@ -4008,6 +4415,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
if (self.hasAvatar) {
size_ += computeMessageSize(3, self.avatar);
}
if (self.hasColor) {
size_ += computeStringSize(4, self.color);
}
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
@ -4055,6 +4465,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
[output appendFormat:@"%@}\n", indent];
}
if (self.hasColor) {
[output appendFormat:@"%@%@: %@\n", indent, @"color", self.color];
}
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
@ -4069,6 +4482,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
[self.avatar storeInDictionary:messageDictionary];
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"avatar"];
}
if (self.hasColor) {
[dictionary setObject: self.color forKey: @"color"];
}
[self.unknownFields storeInDictionary:dictionary];
}
- (BOOL) isEqual:(id)other {
@ -4086,6 +4502,8 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
(!self.hasName || [self.name isEqual:otherMessage.name]) &&
self.hasAvatar == otherMessage.hasAvatar &&
(!self.hasAvatar || [self.avatar isEqual:otherMessage.avatar]) &&
self.hasColor == otherMessage.hasColor &&
(!self.hasColor || [self.color isEqual:otherMessage.color]) &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
@ -4099,6 +4517,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
if (self.hasAvatar) {
hashCode = hashCode * 31 + [self.avatar hash];
}
if (self.hasColor) {
hashCode = hashCode * 31 + [self.color hash];
}
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@ -4406,6 +4827,9 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
if (other.hasAvatar) {
[self mergeAvatar:other.avatar];
}
if (other.hasColor) {
[self setColor:other.color];
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
@ -4444,6 +4868,10 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
[self setAvatar:[subBuilder buildPartial]];
break;
}
case 34: {
[self setColor:[input readString]];
break;
}
}
}
}
@ -4509,6 +4937,22 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
resultContactDetails.avatar = [OWSSignalServiceProtosContactDetailsAvatar defaultInstance];
return self;
}
- (BOOL) hasColor {
return resultContactDetails.hasColor;
}
- (NSString*) color {
return resultContactDetails.color;
}
- (OWSSignalServiceProtosContactDetailsBuilder*) setColor:(NSString*) value {
resultContactDetails.hasColor = YES;
resultContactDetails.color = value;
return self;
}
- (OWSSignalServiceProtosContactDetailsBuilder*) clearColor {
resultContactDetails.hasColor = NO;
resultContactDetails.color = @"";
return self;
}
@end
@interface OWSSignalServiceProtosGroupDetails ()

View File

@ -44,12 +44,7 @@ dispatch_queue_t attachmentsQueue() {
attachmentPointerProtos = dataMessage.attachments;
}
TSThread *thread;
if (dataMessage.hasGroup) {
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
} else {
thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
}
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
OWSAttachmentsProcessor *attachmentsProcessor = [[OWSAttachmentsProcessor alloc]
initWithAttachmentPointersProtos:attachmentPointerProtos

View File

@ -26,4 +26,6 @@ typedef void (^failedSendingCompletionBlock)();
success:(successSendingCompletionBlock)successCompletionBlock
failure:(failedSendingCompletionBlock)failedCompletionBlock;
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt;
@end

View File

@ -3,6 +3,9 @@
#import "ContactsUpdater.h"
#import "NSData+messagePadding.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSLegacyMessageServiceParams.h"
#import "OWSMessageServiceParams.h"
#import "OWSOutgoingSentMessageTranscript.h"
@ -25,7 +28,7 @@
#define InvalidDeviceException @"InvalidDeviceException"
@interface TSMessagesManager ()
@interface TSMessagesManager (sendMessagesPrivate)
dispatch_queue_t sendingQueue(void);
@property TSNetworkManager *networkManager;
@ -170,8 +173,7 @@ dispatch_queue_t sendingQueue() {
} else {
// Special situation: if we are sending to ourselves in a single thread, we treat this as an incoming
// message
[self handleMessageSent:message];
[[TSMessagesManager sharedManager] handleSendToMyself:message]; // TODO self?
[self handleSendToMyself:message];
}
}
});
@ -253,7 +255,7 @@ dispatch_queue_t sendingQueue() {
deviceMessages = @[];
if (remainingAttempts == 0) {
DDLogWarn(@"%@ Terminal failure to build any device messages. Giving up with exception:%@",
self.tag,
self.logTag,
exception);
[self processException:exception outgoingMessage:message inThread:thread];
return;
@ -269,7 +271,7 @@ dispatch_queue_t sendingQueue() {
success:^(NSURLSessionDataTask *task, id responseObject) {
dispatch_async(sendingQueue(), ^{
[recipient save];
[self handleMessageSent:message];
[self handleMessageSentLocally:message];
BLOCK_SAFE_RUN(successBlock);
});
}
@ -286,14 +288,14 @@ dispatch_queue_t sendingQueue() {
}
case 409: {
// Mismatched devices
DDLogWarn(@"%@ Mismatch Devices.", self.tag);
DDLogWarn(@"%@ Mismatch Devices.", self.logTag);
NSError *e;
NSDictionary *serializedResponse =
[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&e];
if (e) {
DDLogError(@"%@ Failed to serialize response of mismatched devices: %@", self.tag, e);
DDLogError(@"%@ Failed to serialize response of mismatched devices: %@", self.logTag, e);
} else {
[self handleMismatchedDevices:serializedResponse recipient:recipient];
}
@ -369,13 +371,40 @@ dispatch_queue_t sendingQueue() {
}];
}
- (void)handleMessageSent:(TSOutgoingMessage *)message
- (void)handleMessageSentLocally:(TSOutgoingMessage *)message
{
[self saveMessage:message withState:TSOutgoingMessageStateSent];
if (message.shouldSyncTranscript) {
message.hasSyncedTranscript = YES;
[self sendSyncTranscriptForMessage:message];
}
[self.disappearingMessagesJob setExpirationForMessage:message];
}
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt
{
[self saveMessage:message withState:TSOutgoingMessageStateDelivered];
[self becomeConsistentWithDisappearingConfigurationForMessage:message];
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:sentAt];
}
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSContactThread *cThread =
[TSContactThread getOrCreateThreadWithContactId:[TSAccountManager localNumber] transaction:transaction];
[cThread saveWithTransaction:transaction];
TSIncomingMessage *incomingMessage =
[[TSIncomingMessage alloc] initWithTimestamp:(outgoingMessage.timestamp + 1)
inThread:cThread
messageBody:outgoingMessage.body
attachmentIds:outgoingMessage.attachmentIds
expiresInSeconds:outgoingMessage.expiresInSeconds];
[incomingMessage saveWithTransaction:transaction];
}];
[self handleMessageSentLocally:outgoingMessage];
}
- (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message
@ -577,14 +606,14 @@ dispatch_queue_t sendingQueue() {
});
}
+ (NSString *)tag
+ (NSString *)logTag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
- (NSString *)logTag
{
return self.class.tag;
return self.class.logTag;
}
@end

View File

@ -5,26 +5,29 @@
#import "TSInvalidIdentityKeySendingErrorMessage.h"
#import "TSOutgoingMessage.h"
@class TSCall;
@class YapDatabaseConnection;
NS_ASSUME_NONNULL_BEGIN
@class TSNetworkManager;
@class TSStorageManager;
@class OWSSignalServiceProtosEnvelope;
@class OWSSignalServiceProtosDataMessage;
@class ContactsUpdater;
@class OWSDisappearingMessagesJob;
@protocol ContactsManagerProtocol;
@interface TSMessagesManager : NSObject
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
dbConnection:(YapDatabaseConnection *)dbConnection
storageManager:(TSStorageManager *)storageManager
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater NS_DESIGNATED_INITIALIZER;
+ (instancetype)sharedManager;
@property (readonly) YapDatabaseConnection *dbConnection;
@property (readonly) TSNetworkManager *networkManager;
@property (readonly) ContactsUpdater *contactsUpdater;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) TSNetworkManager *networkManager;
@property (nonatomic, readonly) ContactsUpdater *contactsUpdater;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope;
@ -43,10 +46,27 @@
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds;
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage;
/**
* @returns
* Group or Contact thread for message, creating a new one if necessary.
*/
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage;
/**
* Synchronize our disappearing messages settings with that of the given message. Useful so we can
* become eventually consistent with remote senders.
*
* @param message
* Can be an expiring or non expiring message. We match the expiration timer of the message, including disabling
* expiring messages if the message is not an expiring message.
*/
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSMessage *)message;
- (NSUInteger)unreadMessagesCount;
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread;
@end
NS_ASSUME_NONNULL_END

View File

@ -6,8 +6,13 @@
#import "ContactsUpdater.h"
#import "MimeTypeUtil.h"
#import "NSData+messagePadding.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSIncomingSentMessageTranscript.h"
#import "OWSReadReceiptsProcessor.h"
#import "OWSRecordTranscriptJob.h"
#import "OWSSyncContactsMessage.h"
#import "OWSSyncGroupsMessage.h"
#import "TSAccountManager.h"
@ -26,9 +31,12 @@
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/SessionCipher.h>
NS_ASSUME_NONNULL_BEGIN
@interface TSMessagesManager ()
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@end
@ -46,14 +54,13 @@
- (instancetype)init
{
return [self initWithNetworkManager:[TSNetworkManager sharedManager]
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
storageManager:[TSStorageManager sharedManager]
contactsManager:[TextSecureKitEnv sharedEnv].contactsManager
contactsUpdater:[ContactsUpdater sharedUpdater]];
}
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
dbConnection:(YapDatabaseConnection *)dbConnection
storageManager:(TSStorageManager *)storageManager
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater
{
@ -63,11 +70,14 @@
return self;
}
_storageManager = storageManager;
_networkManager = networkManager;
_dbConnection = dbConnection;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
_dbConnection = storageManager.newDatabaseConnection;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
return self;
}
@ -234,22 +244,25 @@
}
}];
if (ignoreMessage) {
DDLogDebug(@"Received message from group that I left or don't know "
@"about, ignoring");
// FIXME: https://github.com/WhisperSystems/Signal-iOS/issues/1340
DDLogDebug(@"%@ Received message from group that I left or don't know about, ignoring", self.tag);
return;
}
}
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
DDLogVerbose(@"Received end session message...");
DDLogVerbose(@"%@ Received end session message", self.tag);
[self handleEndSessionMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
DDLogVerbose(@"%@ Received expiration timer update message", self.tag);
[self handleExpirationTimerUpdateMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
} else if (dataMessage.attachments.count > 0
|| (dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
&& dataMessage.group.hasAvatar)) {
DDLogVerbose(@"Received push media message (attachment) or group with an avatar...");
DDLogVerbose(@"%@ Received push media message (attachment) or group with an avatar", self.tag);
[self handleReceivedMediaWithEnvelope:incomingEnvelope dataMessage:dataMessage];
} else {
DDLogVerbose(@"Received individual push text message...");
DDLogVerbose(@"%@ Received data message.", self.tag);
[self handleReceivedTextMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
}
}
@ -258,13 +271,13 @@
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
{
if (syncMessage.hasSent) {
DDLogInfo(@"Received `sent` syncMessage, recording message transcript.");
DDLogInfo(@"%@ Received `sent` syncMessage, recording message transcript.", self.tag);
OWSIncomingSentMessageTranscript *transcript =
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:messageEnvelope.relay];
[transcript record];
[[[OWSRecordTranscriptJob alloc] initWithMessagesManager:self incomingSentMessageTranscript:transcript] run];
} else if (syncMessage.hasRequest) {
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
DDLogInfo(@"Received request `Contacts` syncMessage.");
DDLogInfo(@"%@ Received request `Contacts` syncMessage.", self.tag);
OWSSyncContactsMessage *syncContactsMessage =
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager];
@ -274,14 +287,14 @@
inMessage:syncContactsMessage
thread:nil
success:^{
DDLogInfo(@"Successfully sent Contacts response syncMessage.");
DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.tag);
}
failure:^{
DDLogError(@"Failed to send Contacts response syncMessage.");
DDLogError(@"%@ Failed to send Contacts response syncMessage.", self.tag);
}];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
DDLogInfo(@"Received request `groups` syncMessage.");
DDLogInfo(@"%@ Received request `groups` syncMessage.", self.tag);
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
@ -290,20 +303,21 @@
inMessage:syncGroupsMessage
thread:nil
success:^{
DDLogInfo(@"Successfully sent Groups response syncMessage.");
DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.tag);
}
failure:^{
DDLogError(@"Failed to send Groups response syncMessage.");
DDLogError(@"%@ Failed to send Groups response syncMessage.", self.tag);
}];
}
} else if (syncMessage.read.count > 0) {
DDLogInfo(@"Received %ld read receipt(s)", (u_long)syncMessage.read.count);
DDLogInfo(@"%@ Received %ld read receipt(s)", self.tag, (u_long)syncMessage.read.count);
OWSReadReceiptsProcessor *readReceiptsProcessor =
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read];
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read
storageManager:self.storageManager];
[readReceiptsProcessor process];
} else {
DDLogWarn(@"Ignoring unsupported sync message.");
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
}
}
@ -315,7 +329,7 @@
[TSContactThread getOrCreateThreadWithContactId:endSessionEnvelope.source transaction:transaction];
uint64_t timeStamp = endSessionEnvelope.timestamp;
if (thread) {
if (thread) { // TODO thread should always be nonnull.
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
inThread:thread
messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
@ -325,35 +339,53 @@
[[TSStorageManager sharedManager] deleteAllSessionsForContact:endSessionEnvelope.source];
}
- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) {
DDLogInfo(@"%@ Expiring messages duration turned to %u for thread %@",
self.tag,
(unsigned int)dataMessage.expireTimer,
thread);
disappearingMessagesConfiguration =
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
enabled:YES
durationSeconds:dataMessage.expireTimer];
} else {
DDLogInfo(@"%@ Expiring messages have been turned off for thread %@", self.tag, thread);
disappearingMessagesConfiguration = [[OWSDisappearingMessagesConfiguration alloc]
initWithThreadId:thread.uniqueId
enabled:NO
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
}
[disappearingMessagesConfiguration save];
NSString *name = [self.contactsManager nameStringForPhoneIdentifier:envelope.source];
OWSDisappearingConfigurationUpdateInfoMessage *message =
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:envelope.timestamp
thread:thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:name];
[message save];
}
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)textMessageEnvelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[]];
}
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSContactThread *cThread =
[TSContactThread getOrCreateThreadWithContactId:[TSAccountManager localNumber] transaction:transaction];
[cThread saveWithTransaction:transaction];
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:(outgoingMessage.timestamp + 1)
inThread:cThread
messageBody:outgoingMessage.body
attachmentIds:outgoingMessage.attachmentIds];
[incomingMessage saveWithTransaction:transaction];
}];
}
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
attachmentIds:(NSArray<NSString *> *)attachmentIds
{
uint64_t timeStamp = envelope.timestamp;
uint64_t timestamp = envelope.timestamp;
NSString *body = dataMessage.body;
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
__block TSIncomingMessage *incomingMessage;
__block TSIncomingMessage *_Nullable incomingMessage;
__block TSThread *thread;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@ -383,7 +415,7 @@
NSString *updateGroupInfo = [gThread.groupModel getInfoStringAboutUpdateTo:model];
gThread.groupModel = model;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
inThread:gThread
messageType:TSInfoMessageTypeGroupUpdate
customMessage:updateGroupInfo] saveWithTransaction:transaction];
@ -397,16 +429,18 @@
gThread.groupModel.groupMemberIds = newGroupMembers;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
inThread:gThread
messageType:TSInfoMessageTypeGroupUpdate
customMessage:updateGroupInfo] saveWithTransaction:transaction];
} else {
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
inThread:gThread
authorId:envelope.source
messageBody:body
attachmentIds:attachmentIds];
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
[incomingMessage saveWithTransaction:transaction];
}
@ -416,44 +450,52 @@
transaction:transaction
relay:envelope.relay];
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
inThread:cThread
messageBody:body
attachmentIds:attachmentIds];
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
thread = cThread;
}
if (thread && incomingMessage) {
// Android allows attachments to be sent with body.
// We want the text to be displayed under the attachment
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
uint64_t textMessageTimestamp = timeStamp + 1000;
[incomingMessage saveWithTransaction:transaction];
// Android allows attachments to be sent with body.
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;
TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:gThread
authorId:envelope.source
messageBody:body];
[textMessage saveWithTransaction:transaction];
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:gThread
authorId:envelope.source
messageBody:body];
} else {
TSContactThread *cThread = (TSContactThread *)thread;
TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:cThread
messageBody:body];
[textMessage saveWithTransaction:transaction];
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
inThread:cThread
messageBody:body];
}
textMessage.expiresInSeconds = dataMessage.expireTimer;
[textMessage saveWithTransaction:transaction];
}
[incomingMessage saveWithTransaction:transaction];
}
}];
if (incomingMessage && thread) {
// In case we already have a read receipt for this new message (happens sometimes).
OWSReadReceiptsProcessor *readReceiptsProcessor =
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage];
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage
storageManager:self.storageManager];
[readReceiptsProcessor process];
[self becomeConsistentWithDisappearingConfigurationForMessage:incomingMessage];
// Update thread preview in inbox
[thread touch];
// TODO Delay notification by 100ms?
// It's pretty annoying when you're phone keeps buzzing while you're having a conversation on Desktop.
NSString *name = [thread name];
@ -465,9 +507,56 @@
return incomingMessage;
}
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSMessage *)message
{
// Become eventually consistent in the case that the remote changed their settings at the same time.
// Also in case remote doesn't support expiring messages
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
BOOL changed = NO;
if (message.expiresInSeconds == 0) {
if (disappearingMessagesConfiguration.isEnabled) {
changed = YES;
DDLogWarn(@"%@ Received remote message which had no expiration set, disabling our expiration to become "
@"consistent.",
self.tag);
disappearingMessagesConfiguration.enabled = NO;
[disappearingMessagesConfiguration save];
}
} else if (message.expiresInSeconds != disappearingMessagesConfiguration.durationSeconds) {
changed = YES;
DDLogInfo(
@"%@ Received remote message with different expiration set, updating our expiration to become consistent.",
self.tag);
disappearingMessagesConfiguration.enabled = YES;
disappearingMessagesConfiguration.durationSeconds = message.expiresInSeconds;
[disappearingMessagesConfiguration save];
}
if (!changed) {
return;
}
if ([message isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:incomingMessage.authorId];
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
thread:message.thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:contactName] save];
} else {
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
thread:message.thread
configuration:disappearingMessagesConfiguration]
save];
}
}
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
{
DDLogError(@"Got exception: %@ of type: %@", exception.description, exception.name);
DDLogError(@"%@ Got exception: %@ of type: %@", self.tag, exception.description, exception.name);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage;
@ -495,8 +584,9 @@
- (void)processException:(NSException *)exception
outgoingMessage:(TSOutgoingMessage *)message
inThread:(TSThread *)thread {
DDLogWarn(@"Got exception: %@", exception.description);
inThread:(TSThread *)thread
{
DDLogWarn(@"%@ Got exception: %@", self.tag, exception);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage;
@ -522,6 +612,16 @@
}];
}
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
{
if (dataMessage.hasGroup) {
return [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
} else {
return [TSContactThread getOrCreateThreadWithContactId:envelope.source];
}
}
- (NSUInteger)unreadMessagesCount {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
@ -550,4 +650,18 @@
return numberOfItems;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,27 @@
// Created by Michael Kirk on 9/22/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class OWSFingerprint;
@interface OWSFingerprintBuilder : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
/**
* Builds a fingerprint combining your current credentials with their most recently accepted credentials.
*/
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId;
/**
* Builds a fingerprint combining your current credentials with the specified identity key.
* You can use this to present a new identity key for verification.
*/
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,51 @@
// Created by Michael Kirk on 9/22/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSFingerprintBuilder.h"
#import "OWSFingerprint.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+keyingMaterial.h"
#import <25519/Curve25519.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSFingerprintBuilder ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@end
@implementation OWSFingerprintBuilder
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super init];
if (!self) {
return self;
}
_storageManager = storageManager;
return self;
}
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId
{
NSData *theirIdentityKey = [self.storageManager identityKeyForRecipientId:theirSignalId];
return [self fingerprintWithTheirSignalId:theirSignalId theirIdentityKey:theirIdentityKey];
}
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey
{
NSString *mySignalId = [self.storageManager localNumber];
NSData *myIdentityKey = [self.storageManager identityKeyPair].publicKey;
return [OWSFingerprint fingerprintWithMyStableId:mySignalId
myIdentityKey:myIdentityKey
theirStableId:theirSignalId
theirIdentityKey:theirIdentityKey];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -26,7 +26,7 @@
if ([transaction objectForKey:TSStorageRegisteredNumberKey inCollection:TSStorageUserAccountCollection]) {
block();
} else {
DDLogDebug(@"%@ Skipping block since no local number is registered", self.tag);
DDLogDebug(@"%@ Skipping block since no local number is registered", self.logTag);
}
}];
}
@ -63,14 +63,14 @@
#pragma mark - Logging
+ (NSString *)tag
+ (NSString *)logTag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
- (NSString *)logTag
{
return self.class.tag;
return self.class.logTag;
}
@end

View File

@ -3,6 +3,7 @@
#import "TSStorageManager.h"
#import "NSData+Base64.h"
#import "OWSDisappearingMessagesFinder.h"
#import "OWSReadReceipt.h"
#import "SignalRecipient.h"
#import "TSAttachmentStream.h"
@ -142,6 +143,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
// Register extensions which aren't essential for rendering threads async
[TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView];
[OWSReadReceipt asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:self.database];
OWSDisappearingMessagesFinder *finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self];
[finder asyncRegisterDatabaseExtensions];
}
- (void)protectSignalFiles {

View File

@ -84,6 +84,8 @@
*/
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)touch;
/**
* The unique identifier of the stored object
*/

View File

@ -36,6 +36,13 @@
}];
}
- (void)touch
{
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction touchObjectForKey:self.uniqueId inCollection:[self.class collection]];
}];
}
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[transaction removeObjectForKey:self.uniqueId inCollection:[[self class] collection]];

View File

@ -0,0 +1,62 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesConfiguration.h"
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesConfigurationTest : XCTestCase
@end
@implementation OWSDisappearingMessagesConfigurationTest
- (void)testDictionaryValueDidChange
{
OWSDisappearingMessagesConfiguration *configuration =
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:@"fake-thread-id"
enabled:YES
durationSeconds:10];
XCTAssertTrue(configuration.dictionaryValueDidChange);
[configuration save];
XCTAssertFalse(configuration.dictionaryValueDidChange);
configuration.enabled = NO;
XCTAssertTrue(configuration.dictionaryValueDidChange);
configuration.enabled = YES;
XCTAssertFalse(configuration.dictionaryValueDidChange);
OWSDisappearingMessagesConfiguration *reloadedConfiguration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:@"fake-thread-id"];
XCTAssertNotNil(reloadedConfiguration); // Sanity Check.
XCTAssertFalse(reloadedConfiguration.dictionaryValueDidChange);
reloadedConfiguration.durationSeconds = 30;
XCTAssertTrue(reloadedConfiguration.dictionaryValueDidChange);
reloadedConfiguration.durationSeconds = 10;
XCTAssertFalse(reloadedConfiguration.dictionaryValueDidChange);
}
- (void)testDontStoreEphemeralProperties
{
OWSDisappearingMessagesConfiguration *configuration =
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:@"fake-thread-id"
enabled:YES
durationSeconds:10];
// Unfortunately this test will break every time you add, remove, or rename a property, but on the
// plus side it has a chance of catching when you indadvertently remove our ephemeral properties
// from our Mantle storage blacklist.
NSArray<NSString *> *expected = @[ @"enabled", @"durationSeconds", @"uniqueId" ];
XCTAssertEqualObjects(expected, [configuration.dictionaryValue allKeys]);
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,29 @@
// Created by Michael Kirk on 9/25/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSReadReceipt.h"
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSReadReceiptTest : XCTestCase
@end
@implementation OWSReadReceiptTest
- (void)testDontStoreEphemeralProperties
{
OWSReadReceipt *readReceipt = [[OWSReadReceipt alloc] initWithSenderId:@"fake-sender-id" timestamp:1];
// Unfortunately this test will break every time you add, remove, or rename a property, but on the
// plus side it has a chance of catching when you indadvertently remove our ephemeral properties
// from our Mantle storage blacklist.
NSArray<NSString *> *expected = @[ @"senderId", @"uniqueId", @"timestamp" ];
XCTAssertEqualObjects(expected, [readReceipt.dictionaryValue allKeys]);
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,20 +1,25 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "NSDate+millisecondTimeStamp.h"
#import "TSAttachment.h"
#import "TSMessage.h"
#import "TSThread.h"
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface TSMessageTest : XCTestCase
@property TSThread *thread;
@end
@implementation TSMessageTest
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
self.thread = [[TSThread alloc] init];
}
- (void)tearDown {
@ -22,40 +27,61 @@
[super tearDown];
}
- (void)testDescription {
TSThread *thread = [[TSThread alloc] init];
TSMessage *message =
[[TSMessage alloc] initWithTimestamp:1 inThread:thread messageBody:@"My message body"];
- (void)testExpiresAtWithoutStartedTimer
{
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"foo"
attachmentIds:@[]
expiresInSeconds:100];
XCTAssertEqual(0, message.expiresAt);
}
- (void)testExpiresAtWithStartedTimer
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"foo"
attachmentIds:@[]
expiresInSeconds:10
expireStartedAt:now];
XCTAssertEqual(now + 10000, message.expiresAt);
}
- (void)testDescription
{
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"My message body"];
XCTAssertEqualObjects(@"My message body", [message description]);
}
- (void)testDescriptionWithBogusAttachmentId {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithBogusAttachmentId
{
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
inThread:self.thread
messageBody:@"My message body"
attachmentIds:@[ @"fake-attachment-id" ]];
NSString *actualDescription = [message description];
XCTAssertEqualObjects(@"UNKNOWN_ATTACHMENT_LABEL", actualDescription);
}
- (void)testDescriptionWithEmptyAttachments {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithEmptyAttachments
{
TSMessage *message =
[[TSMessage alloc] initWithTimestamp:1 inThread:thread messageBody:@"My message body" attachmentIds:@[]];
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"My message body" attachmentIds:@[]];
NSString *actualDescription = [message description];
XCTAssertEqualObjects(@"My message body", actualDescription);
}
- (void)testDescriptionWithPhotoAttachmentId {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithPhotoAttachmentId
{
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-photo-attachment-id"
encryptionKey:[[NSData alloc] init]
contentType:@"image/jpeg"];
[attachment save];
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
inThread:self.thread
messageBody:@"My message body"
attachmentIds:@[ @"fake-photo-attachment-id" ]];
NSString *actualDescription = [message description];
@ -63,15 +89,15 @@
}
- (void)testDescriptionWithVideoAttachmentId {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithVideoAttachmentId
{
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-video-attachment-id"
encryptionKey:[[NSData alloc] init]
contentType:@"video/mp4"];
[attachment save];
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
inThread:self.thread
messageBody:@"My message body"
attachmentIds:@[ @"fake-video-attachment-id" ]];
NSString *actualDescription = [message description];
@ -79,30 +105,30 @@
}
- (void)testDescriptionWithAudioAttachmentId {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithAudioAttachmentId
{
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-audio-attachment-id"
encryptionKey:[[NSData alloc] init]
contentType:@"audio/mp3"];
[attachment save];
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
inThread:self.thread
messageBody:@"My message body"
attachmentIds:@[ @"fake-audio-attachment-id" ]];
NSString *actualDescription = [message description];
XCTAssertEqualObjects(@"📻 ATTACHMENT", actualDescription);
}
- (void)testDescriptionWithUnkownAudioContentType {
TSThread *thread = [[TSThread alloc] init];
- (void)testDescriptionWithUnkownAudioContentType
{
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-nonsense-attachment-id"
encryptionKey:[[NSData alloc] init]
contentType:@"non/sense"];
[attachment save];
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
inThread:self.thread
messageBody:@"My message body"
attachmentIds:@[ @"fake-nonsense-attachment-id" ]];
NSString *actualDescription = [message description];
@ -110,3 +136,5 @@
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,184 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesFinder.h"
#import "TSMessage.h"
#import "TSStorageManager.h"
#import "TSThread.h"
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesFinder (Testing)
- (NSArray<TSMessage *> *)fetchExpiredMessages;
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread;
@end
@interface OWSDisappearingMessageFinderTest : XCTestCase
@property TSStorageManager *storageManager;
@property OWSDisappearingMessagesFinder *finder;
@property TSThread *thread;
@property uint64_t now;
@end
@implementation OWSDisappearingMessageFinderTest
- (void)setUp
{
[super setUp];
[TSMessage removeAllObjectsInCollection];
self.storageManager = [TSStorageManager sharedManager];
self.thread = [TSThread new];
[self.thread save];
self.now = [NSDate ows_millisecondTimeStamp];
// Test subject
self.finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self.storageManager];
[self.finder blockingRegisterDatabaseExtensions];
}
- (void)testExpiredMessages
{
TSMessage *expiredMessage1 = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"expiredMessage1"
attachmentIds:@[]
expiresInSeconds:1
expireStartedAt:self.now - 20000];
[expiredMessage1 save];
TSMessage *expiredMessage2 = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"expiredMessage2"
attachmentIds:@[]
expiresInSeconds:2
expireStartedAt:self.now - 2001];
[expiredMessage2 save];
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"notYetExpiredMessage"
attachmentIds:@[]
expiresInSeconds:20
expireStartedAt:self.now - 10000];
[notYetExpiredMessage save];
TSMessage *unreadExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"unereadExpiringMessage"
attachmentIds:@[]
expiresInSeconds:10
expireStartedAt:0];
[unreadExpiringMessage save];
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"unexpiringMessage"
attachmentIds:@[]
expiresInSeconds:0
expireStartedAt:0];
[unExpiringMessage save];
TSMessage *unExpiringMessage2 =
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"unexpiringMessage2"];
[unExpiringMessage2 save];
NSArray<TSMessage *> *actualMessages = [self.finder fetchExpiredMessages];
NSArray<TSMessage *> *expectedMessages = @[ expiredMessage1, expiredMessage2 ];
XCTAssertEqualObjects(expectedMessages, actualMessages);
}
- (void)testUnstartedExpiredMessagesForThread
{
TSMessage *expiredMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"expiredMessage2"
attachmentIds:@[]
expiresInSeconds:2
expireStartedAt:self.now - 2001];
[expiredMessage save];
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"notYetExpiredMessage"
attachmentIds:@[]
expiresInSeconds:20
expireStartedAt:self.now - 10000];
[notYetExpiredMessage save];
TSMessage *unreadExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"unereadExpiringMessage"
attachmentIds:@[]
expiresInSeconds:10
expireStartedAt:0];
[unreadExpiringMessage save];
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"unexpiringMessage"
attachmentIds:@[]
expiresInSeconds:0
expireStartedAt:0];
[unExpiringMessage save];
TSMessage *unExpiringMessage2 =
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"unexpiringMessage2"];
[unExpiringMessage2 save];
NSArray<TSMessage *> *actualMessages = [self.finder fetchUnstartedExpiringMessagesInThread:self.thread];
NSArray<TSMessage *> *expectedMessages = @[ unreadExpiringMessage ];
XCTAssertEqualObjects(expectedMessages, actualMessages);
}
- (void)testNextExpirationTimestampNilWhenNoExpiringMessages
{
// Sanity check.
XCTAssertNil(self.finder.nextExpirationTimestamp);
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"unexpiringMessage"
attachmentIds:@[]
expiresInSeconds:0
expireStartedAt:0];
[unExpiringMessage save];
XCTAssertNil(self.finder.nextExpirationTimestamp);
}
- (void)testNextExpirationTimestampNilWithUpcomingExpiringMessages
{
TSMessage *soonToExpireMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"soonToExpireMessage"
attachmentIds:@[]
expiresInSeconds:10
expireStartedAt:self.now - 9000];
[soonToExpireMessage save];
XCTAssertNotNil(self.finder.nextExpirationTimestamp);
XCTAssertEqual(self.now + 1000, [self.finder.nextExpirationTimestamp unsignedLongLongValue]);
// expired message should take precedence
TSMessage *expiredMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:self.thread
messageBody:@"expiredMessage"
attachmentIds:@[]
expiresInSeconds:10
expireStartedAt:self.now - 11000];
[expiredMessage save];
XCTAssertNotNil(self.finder.nextExpirationTimestamp);
XCTAssertEqual(self.now - 1000, [self.finder.nextExpirationTimestamp unsignedLongLongValue]);
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,73 @@
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesFinder.h"
#import "OWSDisappearingMessagesJob.h"
#import "TSMessage.h"
#import "TSStorageManager.h"
#import "TSThread.h"
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesJobTest : XCTestCase
@end
@implementation OWSDisappearingMessagesJobTest
- (void)setUp
{
[super setUp];
[TSMessage removeAllObjectsInCollection];
}
- (void)testRemoveAnyExpiredMessage
{
TSThread *thread = [TSThread new];
uint64_t now = [NSDate ows_millisecondTimeStamp];
TSMessage *expiredMessage1 = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
messageBody:@"expiredMessage1"
attachmentIds:@[]
expiresInSeconds:1
expireStartedAt:now - 20000];
[expiredMessage1 save];
TSMessage *expiredMessage2 = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
messageBody:@"expiredMessage2"
attachmentIds:@[]
expiresInSeconds:2
expireStartedAt:now - 2001];
[expiredMessage2 save];
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
messageBody:@"notYetExpiredMessage"
attachmentIds:@[]
expiresInSeconds:20
expireStartedAt:now - 10000];
[notYetExpiredMessage save];
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
inThread:thread
messageBody:@"unexpiringMessage"
attachmentIds:@[]
expiresInSeconds:0
expireStartedAt:0];
[unExpiringMessage save];
OWSDisappearingMessagesJob *job =
[[OWSDisappearingMessagesJob alloc] initWithStorageManager:[TSStorageManager sharedManager]];
// Sanity Check.
XCTAssertEqual(4, [TSMessage numberOfKeysInCollection]);
[job run];
XCTAssertEqual(2, [TSMessage numberOfKeysInCollection]);
}
@end
NS_ASSUME_NONNULL_END

View File

@ -183,7 +183,7 @@
OWSFakeContactsManager *fakeContactsManager = [OWSFakeContactsManager new];
TSMessagesManager *messagesManager =
[[TSMessagesManager alloc] initWithNetworkManager:fakeNetworkManager
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
storageManager:[TSStorageManager sharedManager]
contactsManager:fakeContactsManager
contactsUpdater:fakeContactsUpdater];