diff --git a/SignalServiceKit/src/Contacts/ContactsUpdater.m b/SignalServiceKit/src/Contacts/ContactsUpdater.m index 60b6325d6..7adb95f20 100644 --- a/SignalServiceKit/src/Contacts/ContactsUpdater.m +++ b/SignalServiceKit/src/Contacts/ContactsUpdater.m @@ -38,31 +38,27 @@ NS_ASSUME_NONNULL_BEGIN return self; } -- (SignalRecipient *)signalRecipientForRegisteredRecipientId:(NSString *)recipientId -{ - __block SignalRecipient *recipient; - [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - recipient = - [SignalRecipient ensureRecipientExistsWithRegisteredRecipientId:recipientId transaction:transaction]; - }]; - 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 *_Nonnull matchedIds) { - if (matchedIds.count == 1) { - success([self signalRecipientForRegisteredRecipientId:identifier]); + NSSet *recipiendIds = [NSSet setWithObject:recipientId]; + [self contactIntersectionWithSet:recipiendIds + success:^(NSSet *recipients) { + if (recipients.count > 0) { + OWSAssert(recipients.count == 1); + + SignalRecipient *recipient = recipients.allObjects.firstObject; + success(recipient); } else { failure(OWSErrorMakeNoSuchSignalRecipientError()); } @@ -81,13 +77,8 @@ NS_ASSUME_NONNULL_BEGIN } [self contactIntersectionWithSet:[NSSet setWithArray:identifiers] - success:^(NSSet *_Nonnull matchedIds) { - if (matchedIds.count > 0) { - NSMutableArray *recipients = [NSMutableArray new]; - for (NSString *identifier in matchedIds) { - [recipients - addObject:[self signalRecipientForRegisteredRecipientId:identifier]]; - } + success:^(NSSet *recipients) { + if (recipients.count > 0) { success([recipients copy]); } else { failure(OWSErrorMakeNoSuchSignalRecipientError()); @@ -96,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN failure:failure]; } +// TODO: Modify this to support delta lookups. - (void)updateSignalContactIntersectionWithABContacts:(NSArray *)abContacts success:(void (^)(void))success failure:(void (^)(NSError *error))failure @@ -109,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]; }]; @@ -117,62 +110,70 @@ NS_ASSUME_NONNULL_BEGIN NSMutableSet *allContacts = [[abPhoneNumbers setByAddingObjectsFromSet:recipientIds] mutableCopy]; [self contactIntersectionWithSet:allContacts - success:^(NSSet *matchedIds) { - [recipientIds minusSet:matchedIds]; - - // TODO: - // - // Update cache of registered identifiers. + success:^(NSSet *recipients) { DDLogInfo(@"%@ successfully intersected contacts.", self.logTag); success(); } failure:failure]; } -- (void)contactIntersectionWithSet:(NSSet *)idSet - success:(void (^)(NSSet *matchedIds))success +- (void)contactIntersectionWithSet:(NSSet *)recipientIdsToLookup + success:(void (^)(NSSet *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 *phoneNumbersByHashes = [NSMutableDictionary new]; + for (NSString *recipientId in recipientIdsToLookup) { + NSString *hash = [Cryptography truncatedSHA1Base64EncodedWithoutPadding:recipientId]; + phoneNumbersByHashes[hash] = recipientId; } - NSArray *hashes = [phoneNumbersByHashes allKeys]; - + NSArray *hashes = [phoneNumbersByHashes allKeys]; + TSRequest *request = [OWSRequestFactory contactsIntersectionRequestWithHashesArray:hashes]; [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *tsTask, id responseDict) { - NSMutableSet *identifiers = [NSMutableSet new]; - 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.length < 1) { - DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.logTag); - continue; + success:^(NSURLSessionDataTask *task, id responseDict) { + NSMutableSet *registeredRecipientIds = [NSMutableSet new]; + + if ([responseDict isKindOfClass:[NSDictionary class]]) { + NSArray *_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]; } - - [identifiers addObject:identifier]; } } - - // Insert or update contact attributes - // - // TODO: Do we need to _eagerly_ ensure a SignalRecipient instance exists? - [OWSPrimaryStorage.dbReadWriteConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (NSString *identifier in identifiers) { - [SignalRecipient ensureRecipientExistsWithRegisteredRecipientId:identifier - transaction:transaction]; + + NSMutableSet *recipients = [NSMutableSet new]; + [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + for (NSString *recipientId in recipientIdsToLookup) { + if ([registeredRecipientIds containsObject:recipientId]) { + SignalRecipient *recipient = + [SignalRecipient markAccountAsRegistered:recipientId transaction:transaction]; + [recipients addObject:recipient]; + } else { + [SignalRecipient markAccountAsNotRegistered:recipientId transaction:transaction]; } - }]; + } + }]; - success([identifiers copy]); + success([recipients copy]); } failure:^(NSURLSessionDataTask *task, NSError *error) { if (!IsNSErrorNetworkFailure(error)) { diff --git a/SignalServiceKit/src/Contacts/SignalRecipient.h b/SignalServiceKit/src/Contacts/SignalRecipient.h index 14611f818..a68b70394 100644 --- a/SignalServiceKit/src/Contacts/SignalRecipient.h +++ b/SignalServiceKit/src/Contacts/SignalRecipient.h @@ -21,12 +21,6 @@ NS_ASSUME_NONNULL_BEGIN + (SignalRecipient *)ensureRecipientExistsWithRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; -+ (void)ensureRecipientExistsWithRecipientId:(NSString *)recipientId - deviceId:(UInt32)deviceId - transaction:(YapDatabaseReadWriteTransaction *)transaction; - -// TODO: Replace with cache of known signal account ids. -// TODO: Remove? + (nullable instancetype)registeredRecipientForRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadTransaction *)transaction; @@ -40,11 +34,12 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Replace with cache of known signal account ids. + (BOOL)isRegisteredSignalAccount:(NSString *)recipientId transaction:(YapDatabaseReadTransaction *)transaction; -+ (void)markAccountAsRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; ++ (SignalRecipient *)markAccountAsRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; ++ (void)markAccountAsRegistered:(NSString *)recipientId + deviceId:(UInt32)deviceId + transaction:(YapDatabaseReadWriteTransaction *)transaction; + (void)markAccountAsNotRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; -- (void)markAccountAsNotRegisteredWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Contacts/SignalRecipient.m b/SignalServiceKit/src/Contacts/SignalRecipient.m index cb614f4e8..c6e596102 100644 --- a/SignalServiceKit/src/Contacts/SignalRecipient.m +++ b/SignalServiceKit/src/Contacts/SignalRecipient.m @@ -57,36 +57,6 @@ NS_ASSUME_NONNULL_BEGIN return recipient; } -+ (void)ensureRecipientExistsWithRecipientId:(NSString *)recipientId - deviceId:(UInt32)deviceId - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - SignalRecipient *_Nullable existingRecipient = [self recipientForRecipientId:recipientId transaction:transaction]; - if (!existingRecipient) { - DDLogDebug(@"%@ in %s creating recipient: %@, with deviceId: %u", - self.logTag, - __PRETTY_FUNCTION__, - recipientId, - (unsigned int)deviceId); - - SignalRecipient *newRecipient = [[self alloc] initWithTextSecureIdentifier:recipientId]; - [newRecipient addDevices:[NSSet setWithObject:@(deviceId)]]; - [newRecipient saveWithTransaction:transaction]; - - return; - } - - 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]; - } -} - - (instancetype)initWithTextSecureIdentifier:(NSString *)textSecureIdentifier { self = [super initWithUniqueId:textSecureIdentifier]; @@ -184,17 +154,19 @@ NS_ASSUME_NONNULL_BEGIN return; } - NSMutableOrderedSet *updatedDevices = [self.devices mutableCopy]; + NSMutableOrderedSet *updatedDevices = (self.devices + ? [self.devices mutableCopy] + : [NSMutableOrderedSet new]); [updatedDevices unionSet:set]; - self.devices = [updatedDevices copy]; } - (void)removeDevices:(NSSet *)set { - NSMutableOrderedSet *updatedDevices = [self.devices mutableCopy]; + NSMutableOrderedSet *updatedDevices = (self.devices + ? [self.devices mutableCopy] + : [NSMutableOrderedSet new]); [updatedDevices minusSet:set]; - self.devices = [updatedDevices copy]; } @@ -230,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN return (instance && !instance.mayBeUnregistered); } -+ (void)markAccountAsRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction ++ (SignalRecipient *)markAccountAsRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(transaction); OWSAssert(recipientId.length > 0); @@ -242,13 +214,30 @@ NS_ASSUME_NONNULL_BEGIN instance = [[self alloc] initWithTextSecureIdentifier:recipientId]; [instance saveWithTransaction:transaction]; - return; + } else if (instance.mayBeUnregistered) { + instance.mayBeUnregistered = NO; + [instance saveWithTransaction:transaction]; } - if (!instance.mayBeUnregistered) { - return; + return instance; +} + ++ (void)markAccountAsRegistered:(NSString *)recipientId + deviceId:(UInt32)deviceId + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + OWSAssert(transaction); + OWSAssert(recipientId.length > 0); + + SignalRecipient *recipient = [self markAccountAsRegistered: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:transaction]; } - instance.mayBeUnregistered = NO; - [instance saveWithTransaction:transaction]; } + (void)markAccountAsNotRegistered:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction @@ -267,15 +256,6 @@ NS_ASSUME_NONNULL_BEGIN [instance saveWithTransaction:transaction]; } -- (void)markAccountAsNotRegisteredWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(transaction); - - self.mayBeUnregistered = YES; - - [SignalRecipient markAccountAsNotRegistered:self.recipientId transaction:transaction]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index fca8303ea..ab4dafab6 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -110,7 +110,7 @@ NS_ASSUME_NONNULL_BEGIN DecryptSuccessBlock successBlock = ^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) { - [SignalRecipient ensureRecipientExistsWithRecipientId:envelope.source + [SignalRecipient markAccountAsRegistered:envelope.source deviceId:envelope.sourceDevice transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index a06b73cd2..147e35bd5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -716,7 +716,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; return; } - [recipient markAccountAsNotRegisteredWithTransaction:transaction]; + [SignalRecipient markAccountAsNotRegistered:recipient.recipientId + transaction:transaction]; [[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:recipient.recipientId] saveWithTransaction:transaction];