Merge branch 'charlesmchen/streamlineSignalRecipient'

pull/1/head
Matthew Chen 7 years ago
commit 3b2ae010d7

@ -127,10 +127,6 @@ public class MessageFetcherJob: NSObject {
}
builder.setType(type)
if let relay = messageDict["relay"] as? String {
builder.setRelay(relay)
}
guard let timestamp = messageDict["timestamp"] as? UInt64 else {
Logger.error("\(self.logTag) message body didn't have timestamp")
return nil

@ -74,7 +74,6 @@
#import <SignalServiceKit/OWSPrimaryStorage.h>
#import <SignalServiceKit/OWSReadReceiptManager.h>
#import <SignalServiceKit/OWSVerificationStateChangeMessage.h>
#import <SignalServiceKit/SignalRecipient.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSGroupModel.h>
#import <SignalServiceKit/TSInvalidIdentityKeyReceivingErrorMessage.h>

@ -5,6 +5,7 @@
#import "DebugUIContacts.h"
#import "OWSTableViewController.h"
#import "Signal-Swift.h"
#import "SignalApp.h"
#import <Contacts/Contacts.h>
NS_ASSUME_NONNULL_BEGIN
@ -50,6 +51,18 @@ NS_ASSUME_NONNULL_BEGIN
actionBlock:^{
[DebugUIContacts clearSignalAccountCache];
}],
[OWSTableItem itemWithTitle:@"Clear SignalRecipient Cache"
actionBlock:^{
[DebugUIContacts clearSignalRecipientCache];
}],
[OWSTableItem itemWithTitle:@"New Unregistered Contact Thread"
actionBlock:^{
[DebugUIContacts createUnregisteredContactThread];
}],
[OWSTableItem itemWithTitle:@"New Unregistered Group Thread"
actionBlock:^{
[DebugUIContacts createUnregisteredGroupThread];
}],
]];
}
@ -1290,6 +1303,12 @@ NS_ASSUME_NONNULL_BEGIN
[SignalAccount removeAllObjectsInCollection];
}
+ (void)clearSignalRecipientCache
{
DDLogWarn(@"%@ Deleting all signal recipients.", self.logTag);
[SignalRecipient removeAllObjectsInCollection];
}
+ (void)deleteAllContacts
{
[self deleteContactsWithFilter:^(CNContact *contact) {
@ -1304,6 +1323,41 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
+ (NSString *)unregisteredRecipientId
{
// We ensure that the phone number is invalid by appending too many digits.
NSMutableString *recipientId = [@"+1" mutableCopy];
for (int i = 0; i < 11; i++) {
[recipientId appendFormat:@"%d", (int)(arc4random() % 10)];
}
return [recipientId copy];
}
+ (void)createUnregisteredContactThread
{
NSString *recipientId = [self unregisteredRecipientId];
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
[SignalApp.sharedApp presentConversationForThread:thread];
}
+ (void)createUnregisteredGroupThread
{
NSString *unregisteredRecipientId = [self unregisteredRecipientId];
NSString *validRecipientId = @"+19174054216";
NSString *groupName = @"Partially invalid group";
NSMutableArray<NSString *> *recipientIds = [@[
unregisteredRecipientId,
validRecipientId,
[TSAccountManager localNumber],
] mutableCopy];
NSData *groupId = [SecurityUtils generateRandomBytes:16];
TSGroupModel *model =
[[TSGroupModel alloc] initWithTitle:groupName memberIds:recipientIds image:nil groupId:groupId];
TSGroupThread *thread = [TSGroupThread getOrCreateThreadWithGroupModel:model];
[SignalApp.sharedApp presentConversationForThread:thread];
}
@end
NS_ASSUME_NONNULL_END

@ -3729,7 +3729,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
digest:nil
byteCount:filesize
contentType:@"audio/mp3"
relay:@""
sourceFilename:@"test.mp3"
attachmentType:TSAttachmentTypeDefault];
pointer.state = TSAttachmentPointerStateFailed;
@ -4596,7 +4595,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
digest:nil
byteCount:filesize
contentType:fakeAssetLoader.mimeType
relay:@""
sourceFilename:fakeAssetLoader.filename
attachmentType:TSAttachmentTypeDefault];
attachmentPointer.state = TSAttachmentPointerStateFailed;

@ -24,8 +24,7 @@
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager
primaryStorage:primaryStorage
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
contactsManager:contactsManager];
instance = [[Environment alloc] initWithContactsManager:contactsManager
contactsUpdater:contactsUpdater

@ -208,8 +208,8 @@ NS_ASSUME_NONNULL_BEGIN
__block NSMutableArray *result = [NSMutableArray array];
for (PhoneNumber *number in [self.parsedPhoneNumbers sortedArrayUsingSelector:@selector(compare:)]) {
SignalRecipient *signalRecipient =
[SignalRecipient recipientWithTextSecureIdentifier:number.toE164 withTransaction:transaction];
SignalRecipient *_Nullable signalRecipient =
[SignalRecipient registeredRecipientForRecipientId:number.toE164 transaction:transaction];
if (signalRecipient) {
[result addObject:signalRecipient];
}
@ -223,7 +223,7 @@ NS_ASSUME_NONNULL_BEGIN
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (PhoneNumber *number in self.parsedPhoneNumbers) {
if ([SignalRecipient recipientWithTextSecureIdentifier:number.toE164 withTransaction:transaction]) {
if ([SignalRecipient isRegisteredRecipient:number.toE164 transaction:transaction]) {
[identifiers addObject:number.toE164];
}
}

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "SignalRecipient.h"
@ -12,8 +12,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)sharedUpdater;
- (nullable SignalRecipient *)synchronousLookup:(NSString *)identifier error:(NSError **)error;
// This asynchronously tries to verify whether or not a contact id
// corresponds to a service account.
//

@ -38,53 +38,27 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (nullable SignalRecipient *)synchronousLookup:(NSString *)identifier error:(NSError **)error
{
OWSAssert(error);
DDLogInfo(@"%@ %s %@", self.logTag, __PRETTY_FUNCTION__, identifier);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block SignalRecipient *recipient;
// Assigning to a pointer parameter within the block is not preventing the referenced error from being dealloc
// Instead, we avoid ambiguity in ownership by assigning to a local __block variable ensuring the error will be
// retained until our error parameter can take ownership.
__block NSError *retainedError;
[self lookupIdentifier:identifier
success:^(SignalRecipient *fetchedRecipient) {
recipient = fetchedRecipient;
dispatch_semaphore_signal(sema);
}
failure:^(NSError *lookupError) {
DDLogError(
@"%@ Could not find recipient for recipientId: %@, error: %@.", self.logTag, identifier, lookupError);
retainedError = lookupError;
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
*error = retainedError;
return recipient;
}
- (void)lookupIdentifier:(NSString *)identifier
- (void)lookupIdentifier:(NSString *)recipientId
success:(void (^)(SignalRecipient *recipient))success
failure:(void (^)(NSError *error))failure
{
OWSAssert(recipientId.length > 0);
// This should never happen according to nullability annotations... but IIRC it does. =/
if (!identifier) {
if (!recipientId) {
OWSFail(@"%@ Cannot lookup nil identifier", self.logTag);
failure(OWSErrorWithCodeDescription(OWSErrorCodeInvalidMethodParameters, @"Cannot lookup nil identifier"));
return;
}
[self contactIntersectionWithSet:[NSSet setWithObject:identifier]
success:^(NSSet<NSString *> *_Nonnull matchedIds) {
if (matchedIds.count == 1) {
success([SignalRecipient recipientWithTextSecureIdentifier:identifier]);
NSSet *recipiendIds = [NSSet setWithObject:recipientId];
[self contactIntersectionWithSet:recipiendIds
success:^(NSSet<SignalRecipient *> *recipients) {
if (recipients.count > 0) {
OWSAssert(recipients.count == 1);
SignalRecipient *recipient = recipients.allObjects.firstObject;
success(recipient);
} else {
failure(OWSErrorMakeNoSuchSignalRecipientError());
}
@ -103,12 +77,8 @@ NS_ASSUME_NONNULL_BEGIN
}
[self contactIntersectionWithSet:[NSSet setWithArray:identifiers]
success:^(NSSet<NSString *> *_Nonnull matchedIds) {
if (matchedIds.count > 0) {
NSMutableArray<SignalRecipient *> *recipients = [NSMutableArray new];
for (NSString *identifier in matchedIds) {
[recipients addObject:[SignalRecipient recipientWithTextSecureIdentifier:identifier]];
}
success:^(NSSet<SignalRecipient *> *recipients) {
if (recipients.count > 0) {
success([recipients copy]);
} else {
failure(OWSErrorMakeNoSuchSignalRecipientError());
@ -117,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
failure:failure];
}
// TODO: Modify this to support delta lookups.
- (void)updateSignalContactIntersectionWithABContacts:(NSArray<Contact *> *)abContacts
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure
@ -130,7 +101,8 @@ NS_ASSUME_NONNULL_BEGIN
}
NSMutableSet *recipientIds = [NSMutableSet set];
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * transaction) {
// TODO: Don't do this.
NSArray *allRecipientKeys = [transaction allKeysInCollection:[SignalRecipient collection]];
[recipientIds addObjectsFromArray:allRecipientKeys];
}];
@ -138,78 +110,70 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableSet<NSString *> *allContacts = [[abPhoneNumbers setByAddingObjectsFromSet:recipientIds] mutableCopy];
[self contactIntersectionWithSet:allContacts
success:^(NSSet<NSString *> *matchedIds) {
[recipientIds minusSet:matchedIds];
// Cleaning up unregistered identifiers
[OWSPrimaryStorage.dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in recipientIds) {
SignalRecipient *recipient =
[SignalRecipient fetchObjectWithUniqueID:identifier
transaction:transaction];
[recipient removeWithTransaction:transaction];
}
}];
success:^(NSSet<SignalRecipient *> *recipients) {
DDLogInfo(@"%@ successfully intersected contacts.", self.logTag);
success();
}
failure:failure];
}
- (void)contactIntersectionWithSet:(NSSet<NSString *> *)idSet
success:(void (^)(NSSet<NSString *> *matchedIds))success
- (void)contactIntersectionWithSet:(NSSet<NSString *> *)recipientIdsToLookup
success:(void (^)(NSSet<SignalRecipient *> *recipients))success
failure:(void (^)(NSError *error))failure {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *phoneNumbersByHashes = [NSMutableDictionary dictionary];
for (NSString *identifier in idSet) {
[phoneNumbersByHashes setObject:identifier
forKey:[Cryptography truncatedSHA1Base64EncodedWithoutPadding:identifier]];
NSMutableDictionary<NSString *, NSString *> *phoneNumbersByHashes = [NSMutableDictionary new];
for (NSString *recipientId in recipientIdsToLookup) {
NSString *hash = [Cryptography truncatedSHA1Base64EncodedWithoutPadding:recipientId];
phoneNumbersByHashes[hash] = recipientId;
}
NSArray *hashes = [phoneNumbersByHashes allKeys];
NSArray<NSString *> *hashes = [phoneNumbersByHashes allKeys];
TSRequest *request = [OWSRequestFactory contactsIntersectionRequestWithHashesArray:hashes];
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *tsTask, id responseDict) {
NSMutableDictionary *attributesForIdentifier = [NSMutableDictionary dictionary];
NSArray *contactsArray = [(NSDictionary *)responseDict objectForKey:@"contacts"];
// Map attributes to phone numbers
if (contactsArray) {
for (NSDictionary *dict in contactsArray) {
NSString *hash = [dict objectForKey:@"token"];
NSString *identifier = [phoneNumbersByHashes objectForKey:hash];
if (!identifier) {
DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.logTag);
break;
success:^(NSURLSessionDataTask *task, id responseDict) {
NSMutableSet<NSString *> *registeredRecipientIds = [NSMutableSet new];
if ([responseDict isKindOfClass:[NSDictionary class]]) {
NSArray<NSDictionary *> *_Nullable contactsArray = responseDict[@"contacts"];
if ([contactsArray isKindOfClass:[NSArray class]]) {
for (NSDictionary *contactDict in contactsArray) {
if (![contactDict isKindOfClass:[NSDictionary class]]) {
OWSProdLogAndFail(@"%@ invalid contact dictionary.", self.logTag);
continue;
}
NSString *_Nullable hash = contactDict[@"token"];
if (hash.length < 1) {
OWSProdLogAndFail(@"%@ contact missing hash.", self.logTag);
continue;
}
NSString *_Nullable recipientId = phoneNumbersByHashes[hash];
if (recipientId.length < 1) {
OWSProdLogAndFail(@"%@ An intersecting hash wasn't found in the mapping.", self.logTag);
continue;
}
if (![recipientIdsToLookup containsObject:recipientId]) {
OWSProdLogAndFail(@"%@ Intersection response included unexpected recipient.", self.logTag);
continue;
}
[registeredRecipientIds addObject:recipientId];
}
[attributesForIdentifier setObject:dict forKey:identifier];
}
}
// Insert or update contact attributes
[OWSPrimaryStorage.dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in attributesForIdentifier) {
SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:identifier
withTransaction:transaction];
if (!recipient) {
recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil];
}
NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
recipient.relay = attributes[@"relay"];
[recipient saveWithTransaction:transaction];
NSMutableSet<SignalRecipient *> *recipients = [NSMutableSet new];
[OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *recipientId in recipientIdsToLookup) {
if ([registeredRecipientIds containsObject:recipientId]) {
SignalRecipient *recipient =
[SignalRecipient markRecipientAsRegisteredAndGet:recipientId transaction:transaction];
[recipients addObject:recipient];
} else {
[SignalRecipient removeUnregisteredRecipient:recipientId transaction:transaction];
}
}];
}
}];
success([NSSet setWithArray:attributesForIdentifier.allKeys]);
success([recipients copy]);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) {

@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
// * Contacts with multiple signal accounts will correspond to
// multiple instances of SignalAccount.
// * For non-contacts, the contact property will be nil.
//
@interface SignalAccount : TSYapDatabaseObject
// An E164 value identifying the signal account.
@ -42,11 +41,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithRecipientId:(NSString *)recipientId;
// In most cases this should be non-null. This should only
// be null in the case where the SignalRecipient was
// deleted before this property was accessed.
- (nullable SignalRecipient *)signalRecipientWithTransaction:(YapDatabaseReadTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

@ -36,15 +36,6 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (nullable SignalRecipient *)signalRecipientWithTransaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssertIsOnMainThread();
OWSAssert(transaction);
OWSAssert(self.recipientId.length > 0);
return [SignalRecipient recipientWithTextSecureIdentifier:self.recipientId withTransaction:transaction];
}
- (nullable NSString *)uniqueId
{
return _recipientId;

@ -6,36 +6,45 @@
NS_ASSUME_NONNULL_BEGIN
// SignalRecipient serves two purposes:
//
// a) It serves as a cache of "known" Signal accounts. When the service indicates
// that an account exists, we make sure that an instance of SignalRecipient exists
// for that recipient id (using mark as registered).
// When the service indicates that an account does not exist, we remove any
// SignalRecipient.
// b) We hang the "known device list" for known signal accounts on this entity.
@interface SignalRecipient : TSYapDatabaseObject
- (instancetype)initWithTextSecureIdentifier:(NSString *)textSecureIdentifier
relay:(nullable NSString *)relay;
+ (instancetype)selfRecipient;
+ (void)ensureRecipientExistsWithRecipientId:(NSString *)recipientId
deviceId:(UInt32)deviceId
relay:(NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@property (nonatomic, readonly) NSOrderedSet *devices;
+ (nullable instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier;
+ (nullable instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
withTransaction:(YapDatabaseReadTransaction *)transaction;
- (instancetype)init NS_UNAVAILABLE;
@property (readonly) NSOrderedSet *devices;
- (void)addDevices:(NSSet *)set;
- (void)removeDevices:(NSSet *)set;
+ (instancetype)selfRecipient;
@property (nonatomic, nullable) NSString *relay;
+ (nullable instancetype)registeredRecipientForRecipientId:(NSString *)recipientId
transaction:(YapDatabaseReadTransaction *)transaction;
+ (instancetype)getOrBuildUnsavedRecipientForRecipientId:(NSString *)recipientId
transaction:(YapDatabaseReadTransaction *)transaction;
- (BOOL)supportsVoice;
// This property indicates support for both WebRTC audio and video calls.
- (BOOL)supportsWebRTC;
- (void)addDevicesToRegisteredRecipient:(NSSet *)devices
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)removeDevicesFromRegisteredRecipient:(NSSet *)devices
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (NSString *)recipientId;
- (NSComparisonResult)compare:(SignalRecipient *)other;
+ (BOOL)isRegisteredRecipient:(NSString *)recipientId transaction:(YapDatabaseReadTransaction *)transaction;
+ (SignalRecipient *)markRecipientAsRegisteredAndGet:(NSString *)recipientId
transaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (void)markRecipientAsRegistered:(NSString *)recipientId
deviceId:(UInt32)deviceId
transaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (void)removeUnregisteredRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

@ -3,7 +3,6 @@
//
#import "SignalRecipient.h"
#import "OWSIdentityManager.h"
#import "TSAccountManager.h"
#import <YapDatabase/YapDatabaseConnection.h>
@ -11,47 +10,28 @@ NS_ASSUME_NONNULL_BEGIN
@interface SignalRecipient ()
@property NSOrderedSet *devices;
@property (nonatomic) NSOrderedSet *devices;
@end
@implementation SignalRecipient
#pragma mark -
+ (NSString *)collection {
return @"SignalRecipient";
}
@implementation SignalRecipient
+ (void)ensureRecipientExistsWithRecipientId:(NSString *)recipientId
deviceId:(UInt32)deviceId
relay:(NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction
+ (instancetype)getOrBuildUnsavedRecipientForRecipientId:(NSString *)recipientId
transaction:(YapDatabaseReadTransaction *)transaction
{
SignalRecipient *_Nullable existingRecipient =
[self recipientWithTextSecureIdentifier:recipientId withTransaction:transaction];
if (!existingRecipient) {
DDLogDebug(
@"%@ in %s creating recipient with deviceId: %u", self.logTag, __PRETTY_FUNCTION__, (unsigned int)deviceId);
SignalRecipient *newRecipient = [[self alloc] initWithTextSecureIdentifier:recipientId relay:relay];
[newRecipient addDevices:[NSSet setWithObject:@(deviceId)]];
[newRecipient saveWithTransaction:transaction];
return;
}
OWSAssert(transaction);
OWSAssert(recipientId.length > 0);
if (![existingRecipient.devices containsObject:@(deviceId)]) {
DDLogDebug(@"%@ in %s adding device %u to existing recipient.",
self.logTag,
__PRETTY_FUNCTION__,
(unsigned int)deviceId);
[existingRecipient addDevices:[NSSet setWithObject:@(deviceId)]];
[existingRecipient saveWithTransaction:transaction];
SignalRecipient *_Nullable recipient = [self registeredRecipientForRecipientId:recipientId transaction:transaction];
if (!recipient) {
recipient = [[self alloc] initWithTextSecureIdentifier:recipientId];
}
return recipient;
}
- (instancetype)initWithTextSecureIdentifier:(NSString *)textSecureIdentifier
relay:(nullable NSString *)relay
{
self = [super initWithUniqueId:textSecureIdentifier];
if (!self) {
@ -75,8 +55,6 @@ NS_ASSUME_NONNULL_BEGIN
_devices = [NSOrderedSet orderedSetWithObject:@(1)];
}
_relay = [relay isEqualToString:@""] ? nil : relay;
return self;
}
@ -98,17 +76,23 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+ (nullable instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
withTransaction:(YapDatabaseReadTransaction *)transaction
+ (nullable instancetype)registeredRecipientForRecipientId:(NSString *)recipientId
transaction:(YapDatabaseReadTransaction *)transaction
{
return [self fetchObjectWithUniqueID:textSecureIdentifier transaction:transaction];
OWSAssert(transaction);
OWSAssert(recipientId.length > 0);
return [self fetchObjectWithUniqueID:recipientId transaction:transaction];
}
+ (nullable instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
+ (nullable instancetype)recipientForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
__block SignalRecipient *recipient;
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
recipient = [self recipientWithTextSecureIdentifier:textSecureIdentifier withTransaction:transaction];
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
recipient = [self registeredRecipientForRecipientId:recipientId transaction:transaction];
}];
return recipient;
}
@ -116,42 +100,72 @@ NS_ASSUME_NONNULL_BEGIN
// TODO This method should probably live on the TSAccountManager rather than grabbing a global singleton.
+ (instancetype)selfRecipient
{
SignalRecipient *myself = [self recipientWithTextSecureIdentifier:[TSAccountManager localNumber]];
SignalRecipient *myself = [self recipientForRecipientId:[TSAccountManager localNumber]];
if (!myself) {
myself = [[self alloc] initWithTextSecureIdentifier:[TSAccountManager localNumber] relay:nil];
myself = [[self alloc] initWithTextSecureIdentifier:[TSAccountManager localNumber]];
}
return myself;
}
- (void)addDevices:(NSSet *)set
- (void)addDevices:(NSSet *)devices
{
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] && [set containsObject:@(1)]) {
OWSAssert(devices.count > 0);
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] && [devices containsObject:@(1)]) {
OWSFail(@"%@ in %s adding self as recipient device", self.logTag, __PRETTY_FUNCTION__);
return;
}
NSMutableOrderedSet *updatedDevices = [self.devices mutableCopy];
[updatedDevices unionSet:set];
[updatedDevices unionSet:devices];
self.devices = [updatedDevices copy];
}
- (void)removeDevices:(NSSet *)set
- (void)removeDevices:(NSSet *)devices
{
NSMutableOrderedSet *updatedDevices = [self.devices mutableCopy];
[updatedDevices minusSet:set];
OWSAssert(devices.count > 0);
NSMutableOrderedSet *updatedDevices = [self.devices mutableCopy];
[updatedDevices minusSet:devices];
self.devices = [updatedDevices copy];
}
- (BOOL)supportsVoice
- (void)addDevicesToRegisteredRecipient:(NSSet *)devices transaction:(YapDatabaseReadWriteTransaction *)transaction
{
return YES;
OWSAssert(transaction);
OWSAssert(devices.count > 0);
[self addDevices:devices];
SignalRecipient *latest =
[SignalRecipient markRecipientAsRegisteredAndGet:self.recipientId transaction:transaction];
if ([devices isSubsetOfSet:latest.devices.set]) {
return;
}
DDLogDebug(@"%@ adding devices: %@, to recipient: %@", self.logTag, devices, latest.recipientId);
[latest addDevices:devices];
[latest saveWithTransaction_internal:transaction];
}
- (BOOL)supportsWebRTC
- (void)removeDevicesFromRegisteredRecipient:(NSSet *)devices transaction:(YapDatabaseReadWriteTransaction *)transaction
{
return YES;
OWSAssert(transaction);
OWSAssert(devices.count > 0);
[self removeDevices:devices];
SignalRecipient *latest =
[SignalRecipient markRecipientAsRegisteredAndGet:self.recipientId transaction:transaction];
if (![devices isSubsetOfSet:latest.devices.set]) {
return;
}
DDLogDebug(@"%@ removing devices: %@, from recipient: %@", self.logTag, devices, latest.recipientId);
[latest removeDevices:devices];
[latest saveWithTransaction_internal:transaction];
}
- (NSString *)recipientId
@ -165,12 +179,80 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
// We only want to mutate the persisted SignalRecipients in the database
// using other methods of this class, e.g. markRecipientAsRegistered...
// to create, addDevices and removeDevices to mutate. We're trying to
// be strict about using persisted SignalRecipients as a cache to
// reflect "last known registration status". Forcing our codebase to
// use those methods helps ensure that we update the cache deliberately.
OWSProdLogAndFail(@"%@ Don't call saveWithTransaction from outside this class.", self.logTag);
[self saveWithTransaction_internal:transaction];
}
- (void)saveWithTransaction_internal:(YapDatabaseReadWriteTransaction *)transaction
{
[super saveWithTransaction:transaction];
DDLogVerbose(@"%@ saved signal recipient: %@", self.logTag, self.recipientId);
}
+ (BOOL)isRegisteredRecipient:(NSString *)recipientId transaction:(YapDatabaseReadTransaction *)transaction
{
SignalRecipient *_Nullable instance = [self registeredRecipientForRecipientId:recipientId transaction:transaction];
return instance != nil;
}
+ (SignalRecipient *)markRecipientAsRegisteredAndGet:(NSString *)recipientId
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
OWSAssert(recipientId.length > 0);
SignalRecipient *_Nullable instance = [self registeredRecipientForRecipientId:recipientId transaction:transaction];
if (!instance) {
DDLogDebug(@"%@ creating recipient: %@", self.logTag, recipientId);
instance = [[self alloc] initWithTextSecureIdentifier:recipientId];
[instance saveWithTransaction_internal:transaction];
}
return instance;
}
+ (void)markRecipientAsRegistered:(NSString *)recipientId
deviceId:(UInt32)deviceId
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
OWSAssert(recipientId.length > 0);
SignalRecipient *recipient = [self markRecipientAsRegisteredAndGet:recipientId transaction:transaction];
if (![recipient.devices containsObject:@(deviceId)]) {
DDLogDebug(@"%@ in %s adding device %u to existing recipient.",
self.logTag,
__PRETTY_FUNCTION__,
(unsigned int)deviceId);
[recipient addDevices:[NSSet setWithObject:@(deviceId)]];
[recipient saveWithTransaction_internal:transaction];
}
}
+ (void)removeUnregisteredRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
OWSAssert(recipientId.length > 0);
SignalRecipient *_Nullable instance = [self registeredRecipientForRecipientId:recipientId transaction:transaction];
if (!instance) {
return;
}
DDLogDebug(@"%@ removing recipient: %@", self.logTag, recipientId);
[instance removeWithTransaction:transaction];
}
@end
NS_ASSUME_NONNULL_END

@ -15,10 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction
relay:(nullable NSString *)relay;
// Unlike getOrCreateThreadWithContactId, this will _NOT_ create a thread if one does not already exist.
+ (nullable instancetype)getThreadWithContactId:(NSString *)contactId transaction:(YapDatabaseReadTransaction *)transaction;

@ -27,35 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction
relay:(nullable NSString *)relay
{
OWSAssert(contactId.length > 0);
SignalRecipient *recipient =
[SignalRecipient recipientWithTextSecureIdentifier:contactId withTransaction:transaction];
if (!recipient) {
// If no recipient record exists for that contactId, create an empty record
// for immediate use, then ask ContactsUpdater to try to update it async.
recipient =
[[SignalRecipient alloc] initWithTextSecureIdentifier:contactId
relay:relay];
[recipient saveWithTransaction:transaction];
// Update recipient with Server record async.
[[ContactsUpdater sharedUpdater] lookupIdentifier:contactId
success:^(SignalRecipient *recipient) {
}
failure:^(NSError *error) {
DDLogWarn(@"Failed to lookup contact with error:%@", error);
}];
}
return [self getOrCreateThreadWithContactId:contactId transaction:transaction];
}
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction {
OWSAssert(contactId.length > 0);

@ -4,7 +4,6 @@
#import "TSGroupThread.h"
#import "NSData+Base64.h"
#import "SignalRecipient.h"
#import "TSAttachmentStream.h"
#import <SignalServiceKit/TSAccountManager.h>
#import <YapDatabase/YapDatabaseConnection.h>

@ -80,7 +80,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:transcript.attachmentPointerProtos
relay:transcript.relay
networkManager:self.networkManager
transaction:transaction];

@ -30,7 +30,6 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
relay:(nullable NSString *)relay
networkManager:(TSNetworkManager *)networkManager
transaction:(YapDatabaseReadWriteTransaction *)transaction NS_DESIGNATED_INITIALIZER;

@ -57,7 +57,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
}
- (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
relay:(nullable NSString *)relay
networkManager:(TSNetworkManager *)networkManager
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
@ -72,7 +71,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
NSMutableArray<TSAttachmentPointer *> *attachmentPointers = [NSMutableArray new];
for (OWSSignalServiceProtosAttachmentPointer *attachmentProto in attachmentProtos) {
TSAttachmentPointer *pointer = [TSAttachmentPointer attachmentPointerFromProto:attachmentProto relay:relay];
TSAttachmentPointer *pointer = [TSAttachmentPointer attachmentPointerFromProto:attachmentProto];
[attachmentIds addObject:pointer.uniqueId];
[pointer saveWithTransaction:transaction];
@ -152,8 +151,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
if (attachment.serverId < 100) {
DDLogError(@"%@ Suspicious attachment id: %llu", self.logTag, (unsigned long long)attachment.serverId);
}
TSRequest *request =
[OWSRequestFactory attachmentRequestWithAttachmentId:attachment.serverId relay:attachment.relay];
TSRequest *request = [OWSRequestFactory attachmentRequestWithAttachmentId:attachment.serverId];
[self.networkManager makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {

@ -25,14 +25,11 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
digest:(nullable NSData *)digest
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType
relay:(NSString *)relay
sourceFilename:(nullable NSString *)sourceFilename
attachmentType:(TSAttachmentType)attachmentType NS_DESIGNATED_INITIALIZER;
+ (TSAttachmentPointer *)attachmentPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto
relay:(NSString *_Nullable)relay;
+ (TSAttachmentPointer *)attachmentPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto;
@property (nonatomic, readonly) NSString *relay;
@property (atomic) TSAttachmentPointerState state;
@property (nullable, atomic) NSString *mostRecentFailureLocalizedText;

@ -30,7 +30,6 @@ NS_ASSUME_NONNULL_BEGIN
digest:(nullable NSData *)digest
byteCount:(UInt32)byteCount
contentType:(NSString *)contentType
relay:(NSString *)relay
sourceFilename:(nullable NSString *)sourceFilename
attachmentType:(TSAttachmentType)attachmentType
{
@ -45,7 +44,6 @@ NS_ASSUME_NONNULL_BEGIN
_digest = digest;
_state = TSAttachmentPointerStateEnqueued;
_relay = relay;
self.attachmentType = attachmentType;
return self;
@ -53,7 +51,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSAttachmentPointer *)attachmentPointerFromProto:(OWSSignalServiceProtosAttachmentPointer *)attachmentProto
relay:(NSString *_Nullable)relay
{
OWSAssert(attachmentProto.id != 0);
OWSAssert(attachmentProto.key != nil);
@ -75,7 +72,6 @@ NS_ASSUME_NONNULL_BEGIN
digest:digest
byteCount:attachmentProto.size
contentType:attachmentProto.contentType
relay:relay
sourceFilename:attachmentProto.fileName
attachmentType:attachmentType];
return pointer;

@ -19,10 +19,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingSentMessageTranscript : NSObject
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@property (nonatomic, readonly) NSString *relay;
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) uint64_t timestamp;

@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSIncomingSentMessageTranscript
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
self = [super init];
@ -27,7 +26,6 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
_relay = relay;
_dataMessage = sentProto.message;
_recipientId = sentProto.destination;
_timestamp = sentProto.timestamp;
@ -45,9 +43,8 @@ NS_ASSUME_NONNULL_BEGIN
_thread = [TSContactThread getOrCreateThreadWithContactId:_recipientId transaction:transaction];
}
_quotedMessage =
[TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread relay:relay transaction:transaction];
_contact = [OWSContacts contactForDataMessage:_dataMessage relay:relay transaction:transaction];
_quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread transaction:transaction];
_contact = [OWSContacts contactForDataMessage:_dataMessage transaction:transaction];
return self;
}

@ -10,7 +10,6 @@ NS_ASSUME_NONNULL_BEGIN
@class CNContact;
@class OWSAttachmentInfo;
@class OWSSignalServiceProtosDataMessage;
@class OWSSignalServiceProtosDataMessage;
@class OWSSignalServiceProtosDataMessageContact;
@class TSAttachment;
@class TSAttachmentStream;
@ -177,7 +176,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
+ (nullable OWSSignalServiceProtosDataMessageContact *)protoForContact:(OWSContact *)contact;
+ (nullable OWSContact *)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end

@ -893,7 +893,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
}
+ (nullable OWSContact *)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(dataMessage);
@ -968,8 +967,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
if (avatarInfo.hasAvatar) {
OWSSignalServiceProtosAttachmentPointer *avatarAttachment = avatarInfo.avatar;
TSAttachmentPointer *attachmentPointer =
[TSAttachmentPointer attachmentPointerFromProto:avatarAttachment relay:relay];
TSAttachmentPointer *attachmentPointer = [TSAttachmentPointer attachmentPointerFromProto:avatarAttachment];
[attachmentPointer saveWithTransaction:transaction];
contact.avatarAttachmentId = attachmentPointer.uniqueId;

@ -92,7 +92,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (nullable instancetype)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
thread:(TSThread *)thread
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end

@ -105,7 +105,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
thread:(TSThread *)thread
relay:(nullable NSString *)relay
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(dataMessage);
@ -169,7 +168,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSignalServiceProtosAttachmentPointer *thumbnailAttachmentProto = quotedAttachment.thumbnail;
TSAttachmentPointer *thumbnailPointer =
[TSAttachmentPointer attachmentPointerFromProto:thumbnailAttachmentProto relay:relay];
[TSAttachmentPointer attachmentPointerFromProto:thumbnailAttachmentProto];
[thumbnailPointer saveWithTransaction:transaction];
attachmentInfo.thumbnailAttachmentPointerId = thumbnailPointer.uniqueId;

@ -8,7 +8,6 @@
#import "OWSPrimaryStorage+SessionStore.h"
#import "OWSPrimaryStorage.h"
#import "PreKeyBundle+jsonDict.h"
#import "SignalRecipient.h"
#import "TSContactThread.h"
#import "TSErrorMessage_privateConstructor.h"
#import "TSOutgoingMessage.h"

@ -110,10 +110,9 @@ NS_ASSUME_NONNULL_BEGIN
DecryptSuccessBlock successBlock
= ^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) {
[SignalRecipient ensureRecipientExistsWithRecipientId:envelope.source
deviceId:envelope.sourceDevice
relay:envelope.relay
transaction:transaction];
[SignalRecipient markRecipientAsRegistered:envelope.source
deviceId:envelope.sourceDevice
transaction:transaction];
successBlockParameter(plaintextData, transaction);
};

@ -516,7 +516,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(groupThread);
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ]
relay:envelope.relay
networkManager:self.networkManager
transaction:transaction];
@ -553,7 +552,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments
relay:envelope.relay
networkManager:self.networkManager
transaction:transaction];
if (!attachmentsProcessor.hasSupportedAttachments) {
@ -605,7 +603,6 @@ NS_ASSUME_NONNULL_BEGIN
if (syncMessage.hasSent) {
OWSIncomingSentMessageTranscript *transcript =
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent
relay:envelope.relay
transaction:transaction];
OWSRecordTranscriptJob *recordJob =
@ -916,8 +913,7 @@ NS_ASSUME_NONNULL_BEGIN
uint64_t timestamp = envelope.timestamp;
NSString *body = dataMessage.body;
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
OWSContact *_Nullable contact =
[OWSContacts contactForDataMessage:dataMessage relay:envelope.relay transaction:transaction];
OWSContact *_Nullable contact = [OWSContacts contactForDataMessage:dataMessage transaction:transaction];
if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo) {
[self handleGroupInfoRequest:envelope dataMessage:dataMessage transaction:transaction];
@ -1016,7 +1012,6 @@ NS_ASSUME_NONNULL_BEGIN
TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage
thread:oldGroupThread
relay:envelope.relay
transaction:transaction];
DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu",
@ -1060,13 +1055,11 @@ NS_ASSUME_NONNULL_BEGIN
self.logTag,
envelopeAddress(envelope),
(unsigned long)timestamp);
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source
transaction:transaction
relay:envelope.relay];
TSContactThread *thread =
[TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage
thread:thread
relay:envelope.relay
transaction:transaction];
TSIncomingMessage *incomingMessage =

@ -8,11 +8,9 @@ NS_ASSUME_NONNULL_BEGIN
extern const NSUInteger kOversizeTextMessageSizeThreshold;
@class ContactsUpdater;
@class OWSBlockingManager;
@class OWSPrimaryStorage;
@class OWSUploadingService;
@class SignalRecipient;
@class TSInvalidIdentityKeySendingErrorMessage;
@class TSNetworkManager;
@class TSOutgoingMessage;
@ -42,15 +40,13 @@ NS_SWIFT_NAME(MessageSender)
// For subclassing in tests
OWSUploadingService *_uploadingService;
ContactsUpdater *_contactsUpdater;
}
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater;
contactsManager:(id<ContactsManagerProtocol>)contactsManager;
- (void)setBlockingManager:(OWSBlockingManager *)blockingManager;

@ -4,7 +4,6 @@
#import "OWSMessageSender.h"
#import "AppContext.h"
#import "ContactsUpdater.h"
#import "NSData+keyVersionByte.h"
#import "NSData+messagePadding.h"
#import "NSDate+OWS.h"
@ -78,7 +77,7 @@ void AssertIsOnSendingQueue()
messageSender:(OWSMessageSender *)messageSender
dbConnection:(YapDatabaseConnection *)dbConnection
success:(void (^)(void))aSuccessHandler
failure:(void (^)(NSError *_Nonnull error))aFailureHandler NS_DESIGNATED_INITIALIZER;
failure:(void (^)(NSError * error))aFailureHandler NS_DESIGNATED_INITIALIZER;
@end
@ -100,7 +99,7 @@ void AssertIsOnSendingQueue()
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) void (^successHandler)(void);
@property (nonatomic, readonly) void (^failureHandler)(NSError *_Nonnull error);
@property (nonatomic, readonly) void (^failureHandler)(NSError * error);
@end
@ -112,7 +111,7 @@ void AssertIsOnSendingQueue()
messageSender:(OWSMessageSender *)messageSender
dbConnection:(YapDatabaseConnection *)dbConnection
success:(void (^)(void))successHandler
failure:(void (^)(NSError *_Nonnull error))failureHandler
failure:(void (^)(NSError * error))failureHandler
{
self = [super init];
if (!self) {
@ -152,7 +151,7 @@ void AssertIsOnSendingQueue()
// Sanity check preconditions
if (self.message.hasAttachments) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction * transaction) {
TSAttachmentStream *attachmentStream
= (TSAttachmentStream *)[self.message attachmentWithTransaction:transaction];
OWSAssert(attachmentStream);
@ -216,7 +215,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) ContactsUpdater *contactsUpdater;
@property (atomic, readonly) NSMutableDictionary<NSString *, NSOperationQueue *> *sendingQueueMap;
@end
@ -226,7 +224,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater
{
self = [super init];
if (!self) {
@ -236,7 +233,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
_networkManager = networkManager;
_primaryStorage = primaryStorage;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
_sendingQueueMap = [NSMutableDictionary new];
_dbConnection = primaryStorage.newDatabaseConnection;
@ -451,42 +447,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (NSArray<SignalRecipient *> *)signalRecipientsForRecipientIds:(NSArray<NSString *> *)recipientIds
message:(TSOutgoingMessage *)message
error:(NSError **)error
{
OWSAssert(recipientIds);
OWSAssert(message);
OWSAssert(error);
*error = nil;
NSMutableArray<SignalRecipient *> *recipients = [NSMutableArray new];
for (NSString *recipientId in recipientIds) {
SignalRecipient *existingRecipient = [SignalRecipient recipientWithTextSecureIdentifier:recipientId];
if (existingRecipient) {
[recipients addObject:existingRecipient];
} else {
SignalRecipient *newRecipient = [self.contactsUpdater synchronousLookup:recipientId error:error];
if (newRecipient) {
[recipients addObject:newRecipient];
} else {
DDLogWarn(@"%@ No SignalRecipient for recipientId: %@", self.logTag, recipientId);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// Mark this recipient as "skipped".
[message updateWithSkippedRecipient:recipientId transaction:transaction];
}];
}
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (NSString *recipientId in recipientIds) {
SignalRecipient *recipient =
[SignalRecipient getOrBuildUnsavedRecipientForRecipientId:recipientId transaction:transaction];
[recipients addObject:recipient];
}
}
if (recipients.count == 0 && !*error) {
// error should be set in contactsUpater, but just in case.
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotFindContacts1]);
*error = OWSErrorMakeFailedToSendOutgoingMessageError();
}
return [recipients copy];
}];
return recipients;
}
- (void)sendMessageToService:(TSOutgoingMessage *)message
@ -553,20 +526,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return;
}
NSError *error;
NSArray<SignalRecipient *> *recipients =
[self signalRecipientsForRecipientIds:sendingRecipientIds.allObjects message:message error:&error];
if (recipients.count == 0) {
if (!error) {
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotFindContacts2]);
error = OWSErrorMakeFailedToSendOutgoingMessageError();
}
// If no recipients were found, there's no reason to retry. It will just fail again.
[error setIsRetryable:NO];
failureHandler(error);
return;
}
[self signalRecipientsForRecipientIds:sendingRecipientIds.allObjects message:message];
OWSAssert(recipients.count == sendingRecipientIds.count);
[self groupSend:recipients message:message thread:gThread success:successHandler failure:failureHandler];
@ -593,26 +555,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return;
}
SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:recipientContactId];
if (!recipient) {
NSError *error;
// possibly returns nil.
recipient = [self.contactsUpdater synchronousLookup:recipientContactId error:&error];
if (error) {
if (error.code == OWSErrorCodeNoSuchSignalRecipient) {
DDLogWarn(@"%@ recipient contact not found", self.logTag);
[self unregisteredRecipient:recipient message:message thread:thread];
}
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotFindContacts3]);
// No need to repeat trying to find a failure. Apart from repeatedly failing, it would also cause us
// to print redundant error messages.
[error setIsRetryable:NO];
failureHandler(error);
return;
}
}
NSArray<SignalRecipient *> *recipients =
[self signalRecipientsForRecipientIds:@[recipientContactId] message:message];
OWSAssert(recipients.count == 1);
SignalRecipient *recipient = recipients.firstObject;
if (!recipient) {
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
@ -759,15 +705,24 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
message:(TSOutgoingMessage *)message
thread:(TSThread *)thread
{
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if (thread.isGroupThread) {
// Mark as "skipped" group members who no longer have signal accounts.
[message updateWithSkippedRecipient:recipient.recipientId transaction:transaction];
}
[recipient removeWithTransaction:transaction];
if (![SignalRecipient isRegisteredRecipient:recipient.recipientId transaction:transaction]) {
return;
}
[SignalRecipient removeUnregisteredRecipient:recipient.recipientId transaction:transaction];
[[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:recipient.recipientId]
saveWithTransaction:transaction];
// TODO: Should we deleteAllSessionsForContact here?
// If so, we'll need to avoid doing a prekey fetch every
// time we try to send a message to an unregistered user.
}];
}
@ -779,6 +734,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
OWSAssert(message);
OWSAssert(recipient);
OWSAssert(thread || [message isKindOfClass:[OWSOutgoingSyncMessage class]]);
DDLogInfo(@"%@ attempting to send message: %@, timestamp: %llu, recipient: %@",
@ -821,7 +778,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSArray<NSDictionary *> *deviceMessages;
@try {
deviceMessages = [self deviceMessages:message forRecipient:recipient];
deviceMessages = [self deviceMessages:message recipient:recipient];
} @catch (NSException *exception) {
deviceMessages = @[];
if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
@ -943,7 +900,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// This emulates the completion logic of an actual successful save (see below).
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithSkippedRecipient:localNumber transaction:transaction];
[recipient saveWithTransaction:transaction];
}];
successHandler();
});
@ -967,6 +923,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// * The first (after upgrading?) time we send a sync message to our linked devices.
// * After unlinking all linked devices.
// * After trying and failing to link a device.
// * The first time we send a message to a user, if they don't have their
// default device. For example, if they have unregistered
// their primary but still have a linked device. Or later, when they re-register.
//
// When we're not sure if we have linked devices, we need to try
// to send self-sync messages even if they have no device messages
@ -977,7 +936,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
TSRequest *request = [OWSRequestFactory submitMessageRequestWithRecipient:recipient.uniqueId
messages:deviceMessages
relay:recipient.relay
timeStamp:message.timestamp];
if (useWebsocketIfAvailable && TSSocketManager.canMakeRequests) {
[TSSocketManager.sharedManager makeRequest:request
@ -1061,8 +1019,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sendingQueue], ^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[recipient saveWithTransaction:transaction];
[message updateWithSentRecipient:recipient.uniqueId transaction:transaction];
// If we've just delivered a message to a user, we know they
// have a valid Signal account.
[SignalRecipient markRecipientAsRegisteredAndGet:recipient.recipientId transaction:transaction];
}];
[self handleMessageSentLocally:message];
@ -1112,6 +1073,25 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
});
};
void (^handle404)(void) = ^{
DDLogWarn(@"%@ Unregistered recipient: %@", self.logTag, recipient.uniqueId);
OWSAssert(thread);
dispatch_async([OWSDispatch sendingQueue], ^{
[self unregisteredRecipient:recipient message:message thread:thread];
NSError *error = OWSErrorMakeNoSuchSignalRecipientError();
// No need to retry if the recipient is not registered.
[error setIsRetryable:NO];
// If one member of a group deletes their account,
// the group should ignore errors when trying to send
// messages to this ex-member.
[error setShouldBeIgnoredForGroups:YES];
failureHandler(error);
});
};
switch (statusCode) {
case 401: {
DDLogWarn(@"%@ Unable to send due to invalid credentials. Did the user's client get de-authed by "
@ -1125,22 +1105,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return failureHandler(error);
}
case 404: {
DDLogWarn(@"%@ Unregistered recipient: %@", self.logTag, recipient.uniqueId);
OWSAssert(thread);
[self unregisteredRecipient:recipient message:message thread:thread];
NSError *error = OWSErrorMakeNoSuchSignalRecipientError();
// No need to retry if the recipient is not registered.
[error setIsRetryable:NO];
// If one member of a group deletes their account,
// the group should ignore errors when trying to send
// messages to this ex-member.
[error setShouldBeIgnoredForGroups:YES];
return failureHandler(error);
handle404();
return;
}
case 409: {
// Mismatched devices
DDLogWarn(@"%@ Mismatched devices for recipient: %@", self.logTag, recipient.uniqueId);
DDLogWarn(@"%@ Mismatched devices for recipient: %@ (%zd)",
self.logTag,
recipient.uniqueId,
deviceMessages.count);
NSError *_Nullable error = nil;
NSDictionary *_Nullable responseJson = nil;
@ -1153,6 +1126,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return failureHandler(error);
}
NSNumber *_Nullable errorCode = responseJson[@"code"];
if ([@(404) isEqual:errorCode]) {
// Some 404s are returned as 409.
handle404();
return;
}
[self handleMismatchedDevicesWithResponseJson:responseJson recipient:recipient completion:retrySend];
break;
}
@ -1200,7 +1180,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
[self.dbConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * transaction) {
if (extraDevices.count < 1 && missingDevices.count < 1) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]);
}
@ -1213,16 +1193,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
protocolContext:transaction];
}
[recipient removeDevices:[NSSet setWithArray:extraDevices]];
[recipient removeDevicesFromRegisteredRecipient:[NSSet setWithArray:extraDevices]
transaction:transaction];
}
if (missingDevices && missingDevices.count > 0) {
DDLogInfo(@"%@ Adding missing devices: %@", self.logTag, missingDevices);
[recipient addDevices:[NSSet setWithArray:missingDevices]];
[recipient addDevicesToRegisteredRecipient:[NSSet setWithArray:missingDevices]
transaction:transaction];
}
[recipient saveWithTransaction:transaction];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
completionHandler();
});
@ -1270,8 +1250,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}];
}
- (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message
forRecipient:(SignalRecipient *)recipient
- (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message recipient:(SignalRecipient *)recipient
{
OWSAssert(message);
OWSAssert(recipient);
@ -1292,7 +1271,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
messageDict = [self encryptedMessageWithPlaintext:plainText
toRecipient:recipient.uniqueId
recipient:recipient
deviceId:deviceNumber
keyingStorage:self.primaryStorage
isSilent:message.isSilent
@ -1314,7 +1293,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
} @catch (NSException *exception) {
if ([exception.name isEqualToString:OWSMessageSenderInvalidDeviceException]) {
[recipient removeDevices:[NSSet setWithObject:deviceNumber]];
[self.dbConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * transaction) {
[recipient removeDevicesFromRegisteredRecipient:[NSSet setWithObject:deviceNumber]
transaction:transaction];
}];
} else {
@throw exception;
}
@ -1325,18 +1308,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
- (NSDictionary *)encryptedMessageWithPlaintext:(NSData *)plainText
toRecipient:(NSString *)identifier
recipient:(SignalRecipient *)recipient
deviceId:(NSNumber *)deviceNumber
keyingStorage:(OWSPrimaryStorage *)storage
isSilent:(BOOL)isSilent
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(plainText);
OWSAssert(identifier.length > 0);
OWSAssert(recipient);
OWSAssert(deviceNumber);
OWSAssert(storage);
OWSAssert(transaction);
NSString *identifier = recipient.recipientId;
OWSAssert(identifier.length > 0);
if (![storage containsSession:identifier deviceId:[deviceNumber intValue] protocolContext:transaction]) {
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block PreKeyBundle *_Nullable bundle;
@ -1360,6 +1346,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
DDLogError(@"Server replied to PreKeyBundle request with error: %@", error);
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
if (response.statusCode == 404) {
[recipient removeDevicesFromRegisteredRecipient:[NSSet setWithObject:deviceNumber]
transaction:transaction];
// Can't throw exception from within callback as it's probabably a different thread.
exception = [NSException exceptionWithName:OWSMessageSenderInvalidDeviceException
reason:@"Device not registered"

@ -37,7 +37,7 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)allocAttachmentRequest;
+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId relay:(nullable NSString *)relay;
+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId;
+ (TSRequest *)availablePreKeysCountRequest;
@ -60,7 +60,6 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
relay:(nullable NSString *)relay
timeStamp:(uint64_t)timeStamp;
+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey;

@ -101,17 +101,12 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId relay:(nullable NSString *)relay
+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId
{
OWSAssert(attachmentId > 0);
NSString *path = [NSString stringWithFormat:@"%@/%llu", textSecureAttachmentsAPI, attachmentId];
// TODO: Should this be in the parameters?
if (relay.length > 0) {
path = [path stringByAppendingFormat:@"?relay=%@", relay];
}
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
@ -211,7 +206,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
relay:(nullable NSString *)relay
timeStamp:(uint64_t)timeStamp
{
// NOTE: messages may be empty; See comments in OWSDeviceManager.
@ -219,14 +213,11 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(timeStamp > 0);
NSString *path = [textSecureMessagesAPI stringByAppendingString:recipientId];
NSMutableDictionary *parameters = [@{
NSDictionary *parameters = @{
@"messages" : messages,
@"timestamp" : @(timeStamp),
} mutableCopy];
};
if (relay) {
parameters[@"relay"] = relay;
}
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
}

@ -222,6 +222,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
break;
}
case 417: {
// TODO: Is this response code obsolete?
DDLogWarn(@"The number is already registered on a relay. Please unregister there first: %@", request);
failureBlock(task,
[self errorWithHTTPCode:statusCode

@ -1,13 +1,12 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "Cryptography.h"
#import "ProfileManagerProtocol.h"
#import "ProtoBuf+OWS.h"
#import "SignalRecipient.h"
#import "TSThread.h"
#import "TextSecureKitEnv.h"
#import "Cryptography.h"
NS_ASSUME_NONNULL_BEGIN

Loading…
Cancel
Save