|
|
|
@ -5,15 +5,18 @@
|
|
|
|
|
#import "OWSProfileManager.h"
|
|
|
|
|
#import "Environment.h"
|
|
|
|
|
#import "OWSUserProfile.h"
|
|
|
|
|
#import <PromiseKit/AnyPromise.h>
|
|
|
|
|
#import <SignalCoreKit/Cryptography.h>
|
|
|
|
|
#import <SignalCoreKit/NSData+OWS.h>
|
|
|
|
|
#import <SignalCoreKit/NSDate+OWS.h>
|
|
|
|
|
#import <SignalCoreKit/NSString+SSK.h>
|
|
|
|
|
#import <SignalMessaging/SignalMessaging-Swift.h>
|
|
|
|
|
#import <SignalServiceKit/AppContext.h>
|
|
|
|
|
#import <SignalServiceKit/AppReadiness.h>
|
|
|
|
|
#import <SignalServiceKit/MIMETypeUtil.h>
|
|
|
|
|
#import <SignalServiceKit/NSData+Image.h>
|
|
|
|
|
#import <SignalServiceKit/NSNotificationCenter+OWS.h>
|
|
|
|
|
#import <SignalServiceKit/OWSBlockingManager.h>
|
|
|
|
|
#import <SignalServiceKit/OWSFileSystem.h>
|
|
|
|
|
#import <SignalServiceKit/OWSMessageSender.h>
|
|
|
|
|
#import <SignalServiceKit/OWSPrimaryStorage.h>
|
|
|
|
@ -27,6 +30,7 @@
|
|
|
|
|
#import <SignalServiceKit/TSThread.h>
|
|
|
|
|
#import <SignalServiceKit/TSYapDatabaseObject.h>
|
|
|
|
|
#import <SignalServiceKit/UIImage+OWS.h>
|
|
|
|
|
#import <SignalServiceKit/YapDatabaseConnection+OWS.h>
|
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
|
@ -40,12 +44,11 @@ NSString *const kOWSProfileManager_GroupWhitelistCollection = @"kOWSProfileManag
|
|
|
|
|
const NSUInteger kOWSProfileManager_NameDataLength = 26;
|
|
|
|
|
const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
|
|
|
|
|
typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|
|
|
|
|
|
|
|
|
@interface OWSProfileManager ()
|
|
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) OWSMessageSender *messageSender;
|
|
|
|
|
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
|
|
|
|
@property (nonatomic, readonly) TSNetworkManager *networkManager;
|
|
|
|
|
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
|
|
|
|
|
|
|
|
|
|
// This property can be accessed on any thread, while synchronized on self.
|
|
|
|
|
@property (atomic, readonly) OWSUserProfile *localUserProfile;
|
|
|
|
@ -72,8 +75,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
|
|
|
|
messageSender:(OWSMessageSender *)messageSender
|
|
|
|
|
networkManager:(TSNetworkManager *)networkManager
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
|
@ -83,18 +84,18 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
|
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
|
OWSAssertDebug(primaryStorage);
|
|
|
|
|
OWSAssertDebug(messageSender);
|
|
|
|
|
OWSAssertDebug(messageSender);
|
|
|
|
|
|
|
|
|
|
_messageSender = messageSender;
|
|
|
|
|
_dbConnection = primaryStorage.newDatabaseConnection;
|
|
|
|
|
_networkManager = networkManager;
|
|
|
|
|
|
|
|
|
|
_profileAvatarImageCache = [NSCache new];
|
|
|
|
|
_currentAvatarDownloads = [NSMutableSet new];
|
|
|
|
|
|
|
|
|
|
OWSSingletonAssert();
|
|
|
|
|
|
|
|
|
|
[AppReadiness runNowOrWhenAppIsReady:^{
|
|
|
|
|
[self rotateLocalProfileKeyIfNecessary];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -109,6 +110,10 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
selector:@selector(applicationDidBecomeActive:)
|
|
|
|
|
name:OWSApplicationDidBecomeActiveNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
|
selector:@selector(blockListDidChange:)
|
|
|
|
|
name:kNSNotificationName_BlockListDidChange
|
|
|
|
|
object:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Dependencies
|
|
|
|
@ -124,7 +129,21 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
|
|
|
|
|
- (OWSIdentityManager *)identityManager
|
|
|
|
|
{
|
|
|
|
|
return [OWSIdentityManager sharedManager];
|
|
|
|
|
return SSKEnvironment.shared.identityManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (OWSMessageSender *)messageSender {
|
|
|
|
|
OWSAssertDebug(SSKEnvironment.shared.messageSender);
|
|
|
|
|
|
|
|
|
|
return SSKEnvironment.shared.messageSender;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (TSNetworkManager *)networkManager {
|
|
|
|
|
return SSKEnvironment.shared.networkManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (OWSBlockingManager *)blockingManager {
|
|
|
|
|
return SSKEnvironment.shared.blockingManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - User Profile Accessor
|
|
|
|
@ -227,7 +246,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
successBlock();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
failure:^{
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
};
|
|
|
|
@ -258,11 +277,11 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
success:^(NSString *_Nullable avatarUrlPath) {
|
|
|
|
|
tryToUpdateService(avatarUrlPath, fileName);
|
|
|
|
|
}
|
|
|
|
|
failure:^{
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
failure:^{
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
@ -272,7 +291,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
success:^(NSString *_Nullable avatarUrlPath) {
|
|
|
|
|
tryToUpdateService(nil, nil);
|
|
|
|
|
}
|
|
|
|
|
failure:^{
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
} else {
|
|
|
|
@ -283,8 +302,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
|
|
|
|
|
- (void)writeAvatarToDisk:(UIImage *)avatar
|
|
|
|
|
success:(void (^)(NSData *data, NSString *fileName))successBlock
|
|
|
|
|
failure:(void (^)(void))failureBlock
|
|
|
|
|
{
|
|
|
|
|
failure:(ProfileManagerFailureBlock)failureBlock {
|
|
|
|
|
OWSAssertDebug(avatar);
|
|
|
|
|
OWSAssertDebug(successBlock);
|
|
|
|
|
OWSAssertDebug(failureBlock);
|
|
|
|
@ -299,12 +317,11 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
BOOL success = [data writeToFile:filePath atomically:YES];
|
|
|
|
|
OWSAssertDebug(success);
|
|
|
|
|
if (success) {
|
|
|
|
|
successBlock(data, fileName);
|
|
|
|
|
return;
|
|
|
|
|
return successBlock(data, fileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
failureBlock();
|
|
|
|
|
failureBlock(OWSErrorWithCodeDescription(OWSErrorCodeAvatarWriteFailed, @"Avatar write failed."));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -334,8 +351,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
// If avatarData is nil, we are clearing the avatar.
|
|
|
|
|
- (void)uploadAvatarToService:(NSData *_Nullable)avatarData
|
|
|
|
|
success:(void (^)(NSString *_Nullable avatarUrlPath))successBlock
|
|
|
|
|
failure:(void (^)(void))failureBlock
|
|
|
|
|
{
|
|
|
|
|
failure:(ProfileManagerFailureBlock)failureBlock {
|
|
|
|
|
OWSAssertDebug(successBlock);
|
|
|
|
|
OWSAssertDebug(failureBlock);
|
|
|
|
|
OWSAssertDebug(avatarData == nil || avatarData.length > 0);
|
|
|
|
@ -371,8 +387,8 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
|
|
|
|
|
if (![formResponseObject isKindOfClass:[NSDictionary class]]) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidResponse]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSDictionary *responseMap = formResponseObject;
|
|
|
|
|
OWSLogError(@"responseObject: %@", formResponseObject);
|
|
|
|
@ -380,44 +396,44 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
NSString *formAcl = responseMap[@"acl"];
|
|
|
|
|
if (![formAcl isKindOfClass:[NSString class]] || formAcl.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidAcl]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formKey = responseMap[@"key"];
|
|
|
|
|
if (![formKey isKindOfClass:[NSString class]] || formKey.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidKey]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formPolicy = responseMap[@"policy"];
|
|
|
|
|
if (![formPolicy isKindOfClass:[NSString class]] || formPolicy.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidPolicy]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formAlgorithm = responseMap[@"algorithm"];
|
|
|
|
|
if (![formAlgorithm isKindOfClass:[NSString class]] || formAlgorithm.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidAlgorithm]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formCredential = responseMap[@"credential"];
|
|
|
|
|
if (![formCredential isKindOfClass:[NSString class]] || formCredential.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidCredential]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formDate = responseMap[@"date"];
|
|
|
|
|
if (![formDate isKindOfClass:[NSString class]] || formDate.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidDate]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
NSString *formSignature = responseMap[@"signature"];
|
|
|
|
|
if (![formSignature isKindOfClass:[NSString class]] || formSignature.length < 1) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents profileManagerErrorAvatarUploadFormInvalidSignature]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
return failureBlock(
|
|
|
|
|
OWSErrorWithCodeDescription(OWSErrorCodeAvatarUploadFailed, @"Avatar upload failed."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self.avatarHTTPManager POST:@""
|
|
|
|
@ -453,9 +469,9 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
OWSLogInfo(@"successfully uploaded avatar with key: %@", formKey);
|
|
|
|
|
successBlock(formKey);
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSURLSessionDataTask *_Nullable uploadTask, NSError *_Nonnull error) {
|
|
|
|
|
failure:^(NSURLSessionDataTask *_Nullable uploadTask, NSError *error) {
|
|
|
|
|
OWSLogError(@"uploading avatar failed with error: %@", error);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return failureBlock(error);
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSURLSessionDataTask *task, NSError *error) {
|
|
|
|
@ -466,15 +482,14 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OWSLogError(@"Failed to get profile avatar upload form: %@", error);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return failureBlock(error);
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)updateServiceWithProfileName:(nullable NSString *)localProfileName
|
|
|
|
|
success:(void (^)(void))successBlock
|
|
|
|
|
failure:(void (^)(void))failureBlock
|
|
|
|
|
{
|
|
|
|
|
failure:(ProfileManagerFailureBlock)failureBlock {
|
|
|
|
|
OWSAssertDebug(successBlock);
|
|
|
|
|
OWSAssertDebug(failureBlock);
|
|
|
|
|
|
|
|
|
@ -488,7 +503,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSURLSessionDataTask *task, NSError *error) {
|
|
|
|
|
OWSLogError(@"Failed to update profile with error: %@", error);
|
|
|
|
|
failureBlock();
|
|
|
|
|
failureBlock(error);
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -504,6 +519,210 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
[ProfileFetcherJob runWithRecipientId:localNumber ignoreThrottling:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Profile Key Rotation
|
|
|
|
|
|
|
|
|
|
- (nullable NSString *)groupKeyForGroupId:(NSData *)groupId {
|
|
|
|
|
NSString *groupIdKey = [groupId hexadecimalString];
|
|
|
|
|
return groupIdKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (nullable NSData *)groupIdForGroupKey:(NSString *)groupKey {
|
|
|
|
|
NSMutableData *groupId = [NSMutableData new];
|
|
|
|
|
|
|
|
|
|
if (groupKey.length % 2 != 0) {
|
|
|
|
|
OWSFailDebug(@"Group key has unexpected length: %@ (%lu)", groupKey, (unsigned long)groupKey.length);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
for (NSUInteger i = 0; i + 2 <= groupKey.length; i += 2) {
|
|
|
|
|
NSString *_Nullable byteString = [groupKey substringWithRange:NSMakeRange(i, 2)];
|
|
|
|
|
if (!byteString) {
|
|
|
|
|
OWSFailDebug(@"Couldn't slice group key.");
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
unsigned byteValue;
|
|
|
|
|
if (![[NSScanner scannerWithString:byteString] scanHexInt:&byteValue]) {
|
|
|
|
|
OWSFailDebug(@"Couldn't parse hex byte: %@.", byteString);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
if (byteValue > 0xff) {
|
|
|
|
|
OWSFailDebug(@"Invalid hex byte: %@ (%d).", byteString, byteValue);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
uint8_t byte = (uint8_t)(0xff & byteValue);
|
|
|
|
|
[groupId appendBytes:&byte length:1];
|
|
|
|
|
}
|
|
|
|
|
if (groupId.length != kGroupIdLength) {
|
|
|
|
|
OWSFailDebug(@"Parsed group id has unexpected length: %@ (%lu)",
|
|
|
|
|
groupId.hexadecimalString,
|
|
|
|
|
(unsigned long)groupId.length);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
return [groupId copy];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)rotateLocalProfileKeyIfNecessary {
|
|
|
|
|
[self
|
|
|
|
|
rotateLocalProfileKeyIfNecessaryWithSuccess:^{
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)rotateLocalProfileKeyIfNecessaryWithSuccess:(dispatch_block_t)success
|
|
|
|
|
failure:(ProfileManagerFailureBlock)failure {
|
|
|
|
|
OWSAssertDebug(AppReadiness.isAppReady);
|
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
NSMutableSet<NSString *> *whitelistedRecipientIds = [NSMutableSet new];
|
|
|
|
|
NSMutableSet<NSData *> *whitelistedGroupIds = [NSMutableSet new];
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
[whitelistedRecipientIds
|
|
|
|
|
addObjectsFromArray:[transaction allKeysInCollection:kOWSProfileManager_UserWhitelistCollection]];
|
|
|
|
|
|
|
|
|
|
NSArray<NSString *> *whitelistedGroupKeys =
|
|
|
|
|
[transaction allKeysInCollection:kOWSProfileManager_GroupWhitelistCollection];
|
|
|
|
|
for (NSString *groupKey in whitelistedGroupKeys) {
|
|
|
|
|
NSData *_Nullable groupId = [self groupIdForGroupKey:groupKey];
|
|
|
|
|
if (!groupId) {
|
|
|
|
|
OWSFailDebug(@"Couldn't parse group key: %@.", groupKey);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[whitelistedGroupIds addObject:groupId];
|
|
|
|
|
|
|
|
|
|
TSGroupThread *_Nullable thread = [TSGroupThread threadWithGroupId:groupId transaction:transaction];
|
|
|
|
|
if (!thread) {
|
|
|
|
|
OWSLogInfo(@"Could not find whitelisted thread: %@", groupId.hexadecimalString);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[whitelistedRecipientIds addObjectsFromArray:thread.recipientIdentifiers];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
NSString *_Nullable localNumber = [TSAccountManager localNumber];
|
|
|
|
|
if (localNumber) {
|
|
|
|
|
[whitelistedRecipientIds removeObject:localNumber];
|
|
|
|
|
} else {
|
|
|
|
|
OWSFailDebug(@"Missing localNumber");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSSet<NSString *> *blockedRecipientIds = [NSSet setWithArray:self.blockingManager.blockedPhoneNumbers];
|
|
|
|
|
NSSet<NSData *> *blockedGroupIds = [NSSet setWithArray:self.blockingManager.blockedGroupIds];
|
|
|
|
|
|
|
|
|
|
// Find the users and groups which are both a) blocked b) may have our current profile key.
|
|
|
|
|
NSMutableSet<NSString *> *intersectingRecipientIds = [blockedRecipientIds mutableCopy];
|
|
|
|
|
[intersectingRecipientIds intersectSet:whitelistedRecipientIds];
|
|
|
|
|
NSMutableSet<NSData *> *intersectingGroupIds = [blockedGroupIds mutableCopy];
|
|
|
|
|
[intersectingGroupIds intersectSet:whitelistedGroupIds];
|
|
|
|
|
|
|
|
|
|
BOOL isProfileKeySharedWithBlocked = (intersectingRecipientIds.count > 0 || intersectingGroupIds.count > 0);
|
|
|
|
|
if (!isProfileKeySharedWithBlocked) {
|
|
|
|
|
// No need to rotate the profile key.
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rotate the profile key
|
|
|
|
|
|
|
|
|
|
// Make copies of the current local profile state.
|
|
|
|
|
OWSUserProfile *localUserProfile = self.localUserProfile;
|
|
|
|
|
NSString *_Nullable oldProfileName = localUserProfile.profileName;
|
|
|
|
|
NSString *_Nullable oldAvatarFileName = localUserProfile.avatarFileName;
|
|
|
|
|
NSData *_Nullable oldAvatarData = [self profileAvatarDataForRecipientId:self.tsAccountManager.localNumber];
|
|
|
|
|
|
|
|
|
|
// Rotate the stored profile key.
|
|
|
|
|
AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
|
[self.localUserProfile updateWithProfileKey:[OWSAES256Key generateRandomKey]
|
|
|
|
|
dbConnection:self.dbConnection
|
|
|
|
|
completion:^{
|
|
|
|
|
// The value doesn't matter, we just need any non-NSError value.
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
}];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
// Try to re-upload our profile name, if any.
|
|
|
|
|
//
|
|
|
|
|
// This may fail.
|
|
|
|
|
promise = promise.then(^(id value) {
|
|
|
|
|
if (oldProfileName.length < 1) {
|
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
}
|
|
|
|
|
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
|
[self updateServiceWithProfileName:oldProfileName
|
|
|
|
|
success:^{
|
|
|
|
|
OWSLogInfo(@"Update to profile name succeeded.");
|
|
|
|
|
|
|
|
|
|
// The value doesn't matter, we just need any non-NSError value.
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
resolve(error);
|
|
|
|
|
}];
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Try to re-upload our profile avatar, if any.
|
|
|
|
|
//
|
|
|
|
|
// This may fail.
|
|
|
|
|
promise = promise.then(^(id value) {
|
|
|
|
|
if (oldAvatarData.length < 1 || oldAvatarFileName.length < 1) {
|
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
}
|
|
|
|
|
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
|
[self uploadAvatarToService:oldAvatarData
|
|
|
|
|
success:^(NSString *_Nullable avatarUrlPath) {
|
|
|
|
|
OWSLogInfo(@"Update to profile avatar after profile key rotation succeeded.");
|
|
|
|
|
// We need to update the local profile with the avatar state since
|
|
|
|
|
// it is cleared during the "avatar update" process.
|
|
|
|
|
[self.localUserProfile updateWithAvatarUrlPath:avatarUrlPath
|
|
|
|
|
avatarFileName:oldAvatarFileName
|
|
|
|
|
dbConnection:self.dbConnection
|
|
|
|
|
completion:^{
|
|
|
|
|
// The value doesn't matter, we just need any
|
|
|
|
|
// non-NSError value.
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
failure:^(NSError *error) {
|
|
|
|
|
OWSLogInfo(@"Update to profile avatar after profile key rotation failed.");
|
|
|
|
|
resolve(error);
|
|
|
|
|
}];
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Try to re-upload our profile avatar, if any.
|
|
|
|
|
//
|
|
|
|
|
// This may fail.
|
|
|
|
|
promise = promise.then(^(id value) {
|
|
|
|
|
// Remove blocked users and groups from profile whitelist.
|
|
|
|
|
//
|
|
|
|
|
// This will always succeed.
|
|
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
|
[transaction removeObjectsForKeys:intersectingRecipientIds.allObjects
|
|
|
|
|
inCollection:kOWSProfileManager_UserWhitelistCollection];
|
|
|
|
|
for (NSData *groupId in intersectingGroupIds) {
|
|
|
|
|
NSString *groupIdKey = [self groupKeyForGroupId:groupId];
|
|
|
|
|
[transaction removeObjectForKey:groupIdKey
|
|
|
|
|
inCollection:kOWSProfileManager_GroupWhitelistCollection];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
return @(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
promise = promise.then(^(id value) {
|
|
|
|
|
success();
|
|
|
|
|
});
|
|
|
|
|
promise = promise.catch(^(NSError *error) {
|
|
|
|
|
if ([error isKindOfClass:[NSError class]]) {
|
|
|
|
|
failure(error);
|
|
|
|
|
} else {
|
|
|
|
|
failure(OWSErrorMakeAssertionError(@"Profile key rotation failure missing error."));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
[promise retainUntilComplete];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Profile Whitelist
|
|
|
|
|
|
|
|
|
|
- (void)clearProfileWhitelist
|
|
|
|
@ -581,6 +800,10 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
{
|
|
|
|
|
OWSAssertDebug(recipientId.length > 0);
|
|
|
|
|
|
|
|
|
|
if ([self.blockingManager isRecipientIdBlocked:recipientId]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__block BOOL result = NO;
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
NSNumber *_Nullable oldValue =
|
|
|
|
@ -594,7 +817,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
{
|
|
|
|
|
OWSAssertDebug(groupId.length > 0);
|
|
|
|
|
|
|
|
|
|
NSString *groupIdKey = [groupId hexadecimalString];
|
|
|
|
|
NSString *groupIdKey = [self groupKeyForGroupId:groupId];
|
|
|
|
|
|
|
|
|
|
__block BOOL didChange = NO;
|
|
|
|
|
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
@ -645,7 +868,11 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
{
|
|
|
|
|
OWSAssertDebug(groupId.length > 0);
|
|
|
|
|
|
|
|
|
|
NSString *groupIdKey = [groupId hexadecimalString];
|
|
|
|
|
if ([self.blockingManager isGroupIdBlocked:groupId]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSString *groupIdKey = [self groupKeyForGroupId:groupId];
|
|
|
|
|
|
|
|
|
|
__block BOOL result = NO;
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
@ -1173,6 +1400,14 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
|
|
|
|
// TODO: Sync if necessary.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)blockListDidChange:(NSNotification *)notification {
|
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
|
|
|
|
|
|
[AppReadiness runNowOrWhenAppIsReady:^{
|
|
|
|
|
[self rotateLocalProfileKeyIfNecessary];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|
|
|
|
|